Sorry in advance for the long post, I'm not sure how to reduce it though.
I have started using the blotter / quanstrat package from this tutorial of Guy Yollin. If I use Mr. Yollin code as it is, no worries I'm getting similar results.
library(blotter)
currency("USD")
initDate <- "2000-01-01"
startDate <- "2000-01-02"
endDate <- "2016-07-01"
initEq <- 1e6
The only change I am making is to use data locally stored on a .csv using a slightly modified function given by the Systematic Investor on his Github.
Here is the function.
getSymbols.sit <- function(
Symbols,
env = .GlobalEnv,
auto.assign = TRUE,
stock.folder = 'Google Drive/Software/TechnicalAnalysis/StockData',
stock.date.format = '%Y-%m-%d',
...)
{
require(quantmod)
for(i in 1:length(Symbols)) {
s = Symbols[i]
temp = list()
temp[[ s ]] = list(src='csv', format=stock.date.format, dir=stock.folder)
setSymbolLookup(temp)
temp = quantmod::getSymbols(s, env = env, auto.assign = auto.assign)
if (!auto.assign) {
cat(s, format(range(index(temp)), '%d-%b-%Y'), '\n', sep='\t')
return(temp)
}
if(!is.null(env[[ s ]]))
cat(i, 'out of', length(Symbols), 'Reading', s, format(range(index(env[[ s ]])), '%d-%b-%Y'), '\n', sep='\t')
else
cat(i, 'out of', length(Symbols), 'Missing', s, '\n', sep='\t')
}
}
Then calling it.
getSymbols.sit("SPY", source = "yahoo", from=startDate, to=endDate, adjust=T)
Everything seems to work so far. and now continuing building the back test based on the 10 months SMA from Faber.
stock("SPY", currency = "USD", multiplier = 1)
SPY=to.monthly(SPY, indexAt = 'endof', drop.time = FALSE)
SPY$SMA10m <- SMA(Cl(SPY), n=10)
portfolio.st <- "portf.faber"
account.st <- "acct.faber"
initPortf(portfolio.st, "SPY", initDate = initDate)
initAcct(account.st, portfolios = portfolio.st, initDate = initDate, initEq = initEq)
Now creatingn the strategy and running it.
for(i in 1:nrow(SPY))
{
#set up all the values for the specific date and update them in the loop
actualDate <- time(SPY)[i]
equity = getEndEq(Account = account.st, Date = actualDate)
closePrice <- as.numeric(Cl(SPY[i]))
posn <- getPosQty(Portfolio = portfolio.st, Symbol = "SPY", Date = actualDate)
unitSize = as.numeric(trunc(equity/closePrice))
ma <- as.numeric(SPY$SMA10m[i])
#Take market decision
if( !is.na(ma) ) { #we have to wait to have our first 10sma
if( posn == 0 ) { #if no position then we go long
if( closePrice > ma ) {
addTxn(Portfolio = portfolio.st, Symbol = "SPY", TxnDate = actualDate,
TxnQty = unitSize, TxnPrice = closePrice, TxnFees = 0) }
} else {
if( closePrice < ma ) { #sell share and go cash if closing price < 10sma
addTxn(Portfolio = portfolio.st, Symbol = "SPY", TxnDate = actualDate,
TxnQty = -posn, TxnPrice = closePrice, TxnFees = 0)
} else {
if( i == nrow(SPY) ) #last recorded price, we close the system
addTxn(Portfolio = portfolio.st, Symbol = "SPY", TxnDate = actualDate,
TxnQty = -posn, TxnPrice = closePrice, TxnFees = 0)
}
}
}
updatePortf(portfolio.st, Dates = actualDate)
updateAcct(name = account.st, Dates = actualDate)
updateEndEq(Account = account.st, actualDate)
}
Although the script run through without error, there are 3 things that I am getting concern about.
a warning when using the getSymbols.sit(). and I am not sure what to do about it. Here is the warning.
1 out of 1 Reading SPY 03-Jan-2000 19-Jul-2016
Warning message:
In if (as.character(sc[[1]]) != calling.fun) return() :
the condition has length > 1 and only the first element will be used
a warning when running the last 3 update functions.
There were 50 or more warnings (use warnings() to see the first 50)
1: In updatePortf(portfolio.st, Dates = actualDate) :
Incompatible methods ("Ops.POSIXt", "Ops.Date") for ">="
If I use the checkBlotterUpdate (as given in the tutorial) function I get an error.
checkBlotterUpdate(portfolio.st, account.st)
[1] "portfolio P&L doesn't match sum of symbols P&L" [1] FALSE
So it seems I have to be worry about these warnings but I'm not sure how to fix the problem.
If run the whole thing with just getSymbols("SPY"), there is no issues. But I would really like to be able to backtest using locally stored data. I live in Zambia, Africa and internet / power is not always reliable.
Thanks in advance for any hint.
I've submitted a pull request to the quantmod github. You now have four options with regard to Warning #1:
You can ignore the warning (if you're getting the data)
You can avoid Warning #1 by calling getSymbols (without the namespace designation) instead of quantmod::getSymbols
You can build the source from my github
You can wait for #Joshua-Ulrich to incorporate
my pull request.
Related
I am trying to use the applySignals function on my stock data and additional filter rule that I created since the TTR package does not provide a simple filter rule.
When I use the applyIndicators function I can see that an additional column appears with the filter (daily return) so I think this works. However when I add my long and short signal based on this filter and I use the applySignals function I get the following error:
test <- applySignals(strategy = strategy.st, mktdata = test_init)
Error in dimnames<-.xts(*tmp*, value = dn) :
length of 'dimnames' [2] not equal to array extent
In addition: Warning message:
In match.names(column, colnames(data)) :
all columns not located in filter for OTRK.Open OTRK.High OTRK.Low OTRK.Close FILTER.filter longfilter
I used the same example and code from a professional who provided the code but it doesn't seem to work for me. (chapter 9.3 Simple filter rule: https://bookdown.org/kochiuyu/technical-analysis-with-r-second-edition/simple-filter-rule.html)
I am new to R so I don't really understand the error message.
Thanks in advance!
This is my code as far as the error:
install.packages("quantstrat")
install.packages("TTR")
## Library
library("quantstrat")
library("TTR")
## Quantsrat not directly available: solution
install.packages("devtools")
require(devtools)
install_github("braverock/blotter") # dependency
install_github("braverock/quantstrat")
###############################################################################################################################
###############################################################################################################################
initdate <- "2003-12-15"
from <- "2018-12-15"
to <- "2021-04-07"
Sys.setenv(TZ = "UTC")
currency("USD")
getSymbols("OTRK", from = from, to = to, src= "yahoo", adjust = TRUE)
stock("OTRK", currency = "USD", multiplier=1)
tradesize <- 1000
initeq <- 1000
#txnfees <- -10
#orderqty <- 100
#nsamples <- 5
#Parameters indicators#
filterthreshold <- 0.05
#Initialise#
strategy.st <- portfolio.st <- account.st <- "FilterRule"
rm.strat(strategy.st)
initPortf(portfolio.st, symbols = "OTRK", initDate = initdate, currency = "USD")
initAcct(account.st, portfolios = portfolio.st, initDate = initdate, currency = "USD", initEq = initeq)
initOrders(portfolio.st, symbols = "OTRK", initDate = initdate)
strategy(strategy.st, store = TRUE)
#Adding indicators#
FILTER <- function(price) {
lagprice <- lag(price,1)
temp<- price/lagprice - 1
colnames(temp) <- "FILTER"
return(temp)
}
add.indicator(
strategy=strategy.st,
name = "FILTER",
arguments = list(price = quote(Cl(mktdata))),
label= "filter")
#Check test indicators work#
test_init <- applyIndicators(strategy = strategy.st, mktdata = OHLC(OTRK))
head(test_init, n=7)
tail(test_init, n=7)
#Adding signals#
add.signal(strategy.st,
name="sigThreshold",
arguments = list(threshold= filterthreshold,
column="filter",
relationship="gt",
cross=TRUE),
label="longfilter")
add.signal(strategy.st,
name="sigThreshold",
arguments = list(threshold= - filterthreshold,
column="filter",
relationship="lt",
cross=TRUE),
label="shortfilter")
#Test signals on data
test <- applySignals(strategy = strategy.st, mktdata = test_init)
I believe this is a formatting issue with my indicator. Can someone tell me what im doing wrong here?
#....omitted the portfolio initialization above
#returns change from past day, or NA if one of the values is invalid
changeDaily<-function(x,y){if(is.na(x+y)==T){return(NA)};ifelse(x-y>0,"UP","DOWN")}
#creates column called lagPredict which uses the function changeDaily
add.indicator(strat, name = "changeDaily",arguments = list(HLC = quote(mktdata),Cl(mktdata),Lag(Cl(mktdata))), label='lagPredict')
error:
Error in has.Cl(x) : object 'mktdata' not found
Traceback:
traceback()
3: has.Cl(x)
2: Cl(mktdata)
1: add.indicator(strat, name = "changeDaily", arguments = list(HLC = quote(mktdata),
Cl(mktdata), Lag(Cl(mktdata))), label = "lagPredict")
Complete code:
source("forex.functions.R")
startDate <- '2010-01-01' # start of data
endDate <- '2015-05-01' # end of data
symbols<-c("USD/EUR")
portfolio<-acct<-strat<-"simpleLookAhead"
initSetup(symbols,portfolio,acct,strat)
dump<-lapply(symbols,function(x)forex.weeklyOHLC(x))
symbols<-gsub("/","",symbols)
#############################################################
#returns change from past day, or NA if one of the values is invalid
changeDaily<-function(x,y){if(is.na(x+y)==T){return(NA)};ifelse(x-y>0,"UP","DOWN")}
#creates column called lagPredict which uses the function changeDaily to return UP or DOWN in reference to yesterdays price
add.indicator(strat, name = "changeDaily",arguments = list(HLC = quote(mktdata),Cl(mktdata),Lag(Cl(mktdata))), label='lagPredict')
forex.functions.R
library(PerformanceAnalytics)
library(quantmod)
library(lattice)
library(IKTrading)
library(quantstrat)
Sys.setenv(TZ="EST") # set time zone
if (!exists('.blotter')) .blotter <- new.env()
if (!exists('.strategy')) .strategy <- new.env()
forex.weeklyOHLC<-function(ss){
ss<-getSymbols(ss,src="oanda",from=startDate,to=endDate)
x<-get(ss)
#x<-adjustOHLC(x,symbol.name=symbol) #calls get Splits which calls getSymbols which fails bc src != oanda
x<-to.weekly(x,indexAt='lastof',drop.time=TRUE)
indexFormat(x)<-'%Y-%m-%d'
colnames(x)<-gsub("x",ss,colnames(x))
assign(ss,x)
}
initSetup<-function(symbols,portfolio, acct, strat){
initDate <- '2009-12-31'
initEq <- 1e6
currency("USD")
stock(symbols, currency="USD", multiplier=1)
rm.strat(strat) # remove portfolio, account, orderbook if re-run
initPortf(name=portfolio, symbols, initDate=Sys.Date())
initAcct(name=acct, portfolios=portfolio,initDate=Sys.Date(), initEq=initEq)
initOrders(portfolio=portfolio, initDate=Sys.Date())
strategy(strat, store=TRUE)
}
You need to quote all the objects in the arguments list in the call to add.indicator to prevent them from being evaluated. You also need to specify the correct arguments to pass to your changeDaily function. You pass HLC, but changeDaily does not have a HLC argument.
Your add.indicator call should look something like this:
add.indicator(strat, name = "changeDaily",
arguments = list(x = quote(Cl(mktdata)), y = quote(Lag(Cl(mktdata)))),
label = 'lagPredict')
I am trying to backtest a trading strategy with "quantstrat" package.
My strategy is composed by 4 indicators, 3 different EMAs and 1 lagged EMA.
I want to go long when: EMA1 > EMA2 & EMA1 > EMA3 & EMA1_lag < EMA1
I want to exit and go flat when: EMA1 < EMA3
It's pretty simple but I am not able to write it into quantstrat environment.
Here's a data integrity check function used in both examples:
# Data integrity check
checkBlotterUpdate <- function(port.st,account.st,verbose=TRUE)
{
ok <- TRUE
p <- getPortfolio(port.st)
a <- getAccount(account.st)
syms <- names(p$symbols)
port.tot <- sum(sapply(syms,FUN = function(x) eval(parse(
text=paste("sum(p$symbols",x,"posPL.USD$Net.Trading.PL)",sep="$")))))
port.sum.tot <- sum(p$summary$Net.Trading.PL)
if( !isTRUE(all.equal(port.tot,port.sum.tot)) ) {
ok <- FALSE
if( verbose )
print("portfolio P&L doesn't match sum of symbols P&L")
}
initEq <- as.numeric(first(a$summary$End.Eq))
endEq <- as.numeric(last(a$summary$End.Eq))
if( !isTRUE(all.equal(port.tot,endEq-initEq)) ) {
ok <- FALSE
if( verbose )
print("portfolio P&L doesn't match account P&L")
}
if( sum(duplicated(index(p$summary))) ) {
ok <- FALSE
if( verbose )
print("duplicate timestamps in portfolio summary")
}
if( sum(duplicated(index(a$summary))) ) {
ok <- FALSE
if( verbose )
print("duplicate timestamps in account summary")
}
return(ok)
}
Here is the blotter code that does what I want:
# Working Strategy
# it works well only with one portfolio
library(quantstrat)
suppressWarnings({
try(rm(list=ls(FinancialInstrument:::.instrument),
pos=FinancialInstrument:::.instrument), silent=TRUE)
try(rm(list=c("account.bGiulio","portfolio.bGiulio","order_book"),
pos=.blotter), silent=TRUE)
try(rm(list=c("b.strategy","myTheme","SPY",".getSymbols")), silent=TRUE)
})
#### all currency instruments must be defined
#### before instruments of other types can be defined
# Initialize a currency and a stock instrument
currency("USD")
stock("SPY",currency="USD",multiplier=1)
#Fetch historic data
# system settings
initDate <- '1997-12-31'
startDate <- '1998-01-01'
endDate <- '2014-06-30'
initEq <- 1e6
Sys.setenv(TZ="UTC")
getSymbols('SPY', from=startDate, to=endDate, index.class="POSIXct", adjust=T)
# convert data to weekly
SPY=to.weekly(SPY, indexAt='endof', drop.time=FALSE)
SPY$EMA_1<-EMA(na.locf(Cl(SPY)),10) # 10 o 3
SPY$EMA_2<-EMA(na.locf(Cl(SPY)),25) # 50 o 10
SPY$EMA_3<-EMA(na.locf(Cl(SPY)),30) # 200 o 50
SPY$EMA_1_lag<-lag(EMA(na.locf(Cl(SPY)),10),1) # 200 o 50
# inizialization on both
b.strategy <- "bGiulio"
initPortf(b.strategy, 'SPY', initDate=initDate)
initAcct(b.strategy, portfolios=b.strategy, initDate=initDate, initEq=initEq)
initOrders(portfolio=b.strategy,initDate=initDate)
# trading algo
for( i in 1:nrow(SPY) )
{
# update values for this date
CurrentDate <- time(SPY)[i]
equity = getEndEq(b.strategy, CurrentDate)
ClosePrice <- as.numeric(Cl(SPY[i,]))
Posn <- getPosQty(b.strategy, Symbol='SPY', Date=CurrentDate)
UnitSize = as.numeric(trunc(equity/ClosePrice))
EMA1 <- as.numeric(SPY[i,'EMA_1'])
EMA2 <- as.numeric(SPY[i,'EMA_2'])
EMA3 <- as.numeric(SPY[i,'EMA_3'])
EMA1_lag <- as.numeric(SPY[i,'EMA_1_lag'])
# change market position if necessary
if( !is.na(EMA1) & # if the moving average has begun
!is.na(EMA2) & # if the moving average has begun
!is.na(EMA3) &
!is.na(EMA1_lag) ) # if the moving average has begun
{
if( Posn == 0 ) { # No position, test to go Long
if( EMA1 > EMA2 & EMA1 > EMA3 & EMA1_lag<EMA1) {
# enter long position
addTxn(b.strategy, Symbol='SPY', TxnDate=CurrentDate,
TxnPrice=ClosePrice, TxnQty = UnitSize , TxnFees=0)
}
} else { # Have a position, so check exit
if( EMA1 < EMA3) {
# exit position
addTxn(b.strategy, Symbol='SPY', TxnDate=CurrentDate,
TxnPrice=ClosePrice, TxnQty = -Posn , TxnFees=0)
} else {
if( i==nrow(SPY) ) # exit on last day
addTxn(b.strategy, Symbol='SPY', TxnDate=CurrentDate,
TxnPrice=ClosePrice, TxnQty = -Posn , TxnFees=0)
}
}
}
updatePortf(b.strategy,Dates=CurrentDate)
updateAcct(b.strategy,Dates=CurrentDate)
updateEndEq(b.strategy,CurrentDate)
} # End dates loop
# transactions
#getTxns(Portfolio=b.strategy, Symbol="SPY")
checkBlotterUpdate(b.strategy,b.strategy)
## [1] TRUE
tstats <- t(tradeStats(b.strategy))
perTradeStats(b.strategy)
library(lattice)
a <- getAccount(b.strategy)
xyplot(a$summary,type="h",col=4)
equity <- a$summary$End.Eq
plot(equity,main="Giulio Strategy Equity Curve")
ret <- Return.calculate(equity,method="log")
charts.PerformanceSummary(ret, colorset = bluefocus,
main="Giulio Strategy Performance")
I tried to replicate the above strategy with quantstrat (using add.indicator, add.signal, add.rule), but the results are definitely different. Here the second code with quantstrat:
# Here the code that does not work
library(quantstrat)
#Initialize a currency and a stock instrument
currency("USD")
stock("SPY",currency="USD",multiplier=1)
# system settings
initDate <- '1997-12-31'
startDate <- '1998-01-01'
endDate <- '2014-06-30'
initEq <- 1e6
Sys.setenv(TZ="UTC")
getSymbols('SPY', from=startDate, to=endDate, index.class="POSIXct", adjust=T)
SPY <- to.weekly(SPY, indexAt='endof', drop.time=FALSE)
SPY$EMA1<-EMA(na.locf(Cl(SPY)),10) # 10 o 3
SPY$EMA2<-EMA(na.locf(Cl(SPY)),25) # 50 o 10
SPY$EMA3<-EMA(na.locf(Cl(SPY)),30) # 200 o 50
SPY$EMA1_lag<-lag(EMA(na.locf(Cl(SPY)),10)) # 200 o 50
# initialize portfolio/account
qs.strategy <- "qsGiulio"
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)
# initialize orders container
initOrders(portfolio=qs.strategy,initDate=initDate)
# instantiate a new strategy object
strategy(qs.strategy,store=TRUE)
strat <-getStrategy(qs.strategy)
add.indicator(strategy = qs.strategy, name = "EMA",
arguments = list(x = quote(na.locf(Cl(mktdata))), n=10), label="EMA1")
add.indicator(strategy = qs.strategy, name = "EMA",
arguments = list(x = quote(na.locf(Cl(mktdata))), n=25), label="EMA2")
add.indicator(strategy = qs.strategy, name = "EMA",
arguments = list(x = quote(na.locf(Cl(mktdata))), n=30), label="EMA3")
add.indicator(strategy = qs.strategy, name = "EMA",
arguments = list(x = quote(lag(na.locf(Cl(mktdata)))), n=10), label="EMA1_lag")
# entry signals
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA1","EMA2"),relationship="gt"),
label="EMA1.gt.EMA2")
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA1","EMA3"),relationship="gt"),
label="EMA1.gt.EMA3")
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA1","EMA1_lag"),relationship="gt"),
label="EMA1.gt.EMA1_lag")
add.signal(qs.strategy, name = "sigFormula",
arguments = list(formula="EMA1.gt.EMA2 & EMA1.gt.EMA3 & EMA1.gt.EMA1_lag"),
label="longEntry")
# exit signals
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA1","EMA3"),relationship="lt"),
label="EMA1.lt.EMA3")
# RULES
# go long when 3 condition
add.rule(qs.strategy, name='ruleSignal',
arguments =
list(sigcol="longEntry", sigval=TRUE, orderqty=900,
ordertype='market', orderside='long'),
type='enter')
# exit when 1 condition
add.rule(qs.strategy, name='ruleSignal',
arguments = list(sigcol="EMA1.lt.EMA3", sigval=TRUE, orderqty='all',
ordertype='market', orderside='long'),
type='exit')
applyStrategy(strategy=qs.strategy , portfolios=qs.strategy)
# transactions
#getTxns(Portfolio=qs.strategy, Symbol="SPY")
checkBlotterUpdate(b.strategy,b.strategy)
## [1] TRUE
# update portfolio/account
updatePortf(qs.strategy)
updateAcct(qs.strategy)
updateEndEq(qs.strategy)
tstats <- t(tradeStats(qs.strategy))
perTradeStats(qs.strategy)
library(lattice)
a <- getAccount(qs.strategy)
xyplot(a$summary,type="h",col=4)
equity <- a$summary$End.Eq
plot(equity,main="Giulio Strategy Equity Curve")
ret <- Return.calculate(equity,method="log")
charts.PerformanceSummary(ret, colorset = bluefocus,
main="Giulio Strategy Performance")
Could anyone help me to understand why the second code doesn't give identical results? I think my mistakes are within the add.indicator, add.signal, add.rule setup, but I am not able to figure out it precisely.
The quanstrat-based code will not provide identical results for several reasons. One is that your columns are not correct in your first 3 add.signal calls. All the columns need to have an "EMA." prefix:
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA.EMA1","EMA.EMA2"),relationship="gt"),
label="EMA1.gt.EMA2")
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA.EMA1","EMA.EMA3"),relationship="gt"),
label="EMA1.gt.EMA3")
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA.EMA1","EMA.EMA1_lag"),relationship="gt"),
label="EMA1.gt.EMA1_lag")
Another issue, and likely the biggest cause of differences, is the next signal:
add.signal(qs.strategy, name = "sigFormula",
arguments = list(formula="EMA1.gt.EMA2 & EMA1.gt.EMA3 & EMA1.gt.EMA1_lag"),
label="longEntry")
That creates a signal for every observation where the formula is true, not just the observations where the formula crosses from false to true. You only want the observations where the formula crosses, so you should use:
add.signal(qs.strategy, name = "sigFormula",
arguments = list(formula="EMA1.gt.EMA2 & EMA1.gt.EMA3 & EMA1.gt.EMA1_lag",
cross = TRUE),
label="longEntry")
Another source of differences is that you always use ~100% of your available equity for your opening long transaction in the blotter version, but you always buy 900 shares in the quantstrat version. You can do something similar in quantstrat by using a custom order sizing function (see osNoOp and osMaxPos for examples of how to write a custom order sizing function).
I am trying to backtest a trading strategy with "quantstrat" package.
My strategy is composed by 4 indicators, 3 different EMAs and 1 lagged EMA.
I want to go long when: EMA1 > EMA2 & EMA1 > EMA3 & EMA1_lag < EMA1
I want to exit and go flat when: EMA1 < EMA3
It's pretty simple but I am not able to write it into quantstrat environment.
Here's a data integrity check function used in both examples:
# Data integrity check
checkBlotterUpdate <- function(port.st,account.st,verbose=TRUE)
{
ok <- TRUE
p <- getPortfolio(port.st)
a <- getAccount(account.st)
syms <- names(p$symbols)
port.tot <- sum(sapply(syms,FUN = function(x) eval(parse(
text=paste("sum(p$symbols",x,"posPL.USD$Net.Trading.PL)",sep="$")))))
port.sum.tot <- sum(p$summary$Net.Trading.PL)
if( !isTRUE(all.equal(port.tot,port.sum.tot)) ) {
ok <- FALSE
if( verbose )
print("portfolio P&L doesn't match sum of symbols P&L")
}
initEq <- as.numeric(first(a$summary$End.Eq))
endEq <- as.numeric(last(a$summary$End.Eq))
if( !isTRUE(all.equal(port.tot,endEq-initEq)) ) {
ok <- FALSE
if( verbose )
print("portfolio P&L doesn't match account P&L")
}
if( sum(duplicated(index(p$summary))) ) {
ok <- FALSE
if( verbose )
print("duplicate timestamps in portfolio summary")
}
if( sum(duplicated(index(a$summary))) ) {
ok <- FALSE
if( verbose )
print("duplicate timestamps in account summary")
}
return(ok)
}
Here is the blotter code that does what I want:
# Working Strategy
# it works well only with one portfolio
library(quantstrat)
suppressWarnings({
try(rm(list=ls(FinancialInstrument:::.instrument),
pos=FinancialInstrument:::.instrument), silent=TRUE)
try(rm(list=c("account.bGiulio","portfolio.bGiulio","order_book"),
pos=.blotter), silent=TRUE)
try(rm(list=c("b.strategy","myTheme","SPY",".getSymbols")), silent=TRUE)
})
#### all currency instruments must be defined
#### before instruments of other types can be defined
# Initialize a currency and a stock instrument
currency("USD")
stock("SPY",currency="USD",multiplier=1)
#Fetch historic data
# system settings
initDate <- '1997-12-31'
startDate <- '1998-01-01'
endDate <- '2014-06-30'
initEq <- 1e6
Sys.setenv(TZ="UTC")
getSymbols('SPY', from=startDate, to=endDate, index.class="POSIXct", adjust=T)
# convert data to weekly
SPY=to.weekly(SPY, indexAt='endof', drop.time=FALSE)
SPY$EMA_1<-EMA(na.locf(Cl(SPY)),10) # 10 o 3
SPY$EMA_2<-EMA(na.locf(Cl(SPY)),25) # 50 o 10
SPY$EMA_3<-EMA(na.locf(Cl(SPY)),30) # 200 o 50
SPY$EMA_1_lag<-lag(EMA(na.locf(Cl(SPY)),10),1) # 200 o 50
# inizialization on both
b.strategy <- "bGiulio"
initPortf(b.strategy, 'SPY', initDate=initDate)
initAcct(b.strategy, portfolios=b.strategy, initDate=initDate, initEq=initEq)
initOrders(portfolio=b.strategy,initDate=initDate)
# trading algo
for( i in 1:nrow(SPY) )
{
# update values for this date
CurrentDate <- time(SPY)[i]
equity = getEndEq(b.strategy, CurrentDate)
ClosePrice <- as.numeric(Cl(SPY[i,]))
Posn <- getPosQty(b.strategy, Symbol='SPY', Date=CurrentDate)
UnitSize = as.numeric(trunc(equity/ClosePrice))
EMA1 <- as.numeric(SPY[i,'EMA_1'])
EMA2 <- as.numeric(SPY[i,'EMA_2'])
EMA3 <- as.numeric(SPY[i,'EMA_3'])
EMA1_lag <- as.numeric(SPY[i,'EMA_1_lag'])
# change market position if necessary
if( !is.na(EMA1) & # if the moving average has begun
!is.na(EMA2) & # if the moving average has begun
!is.na(EMA3) &
!is.na(EMA1_lag) ) # if the moving average has begun
{
if( Posn == 0 ) { # No position, test to go Long
if( EMA1 > EMA2 & EMA1 > EMA3 & EMA1_lag<EMA1) {
# enter long position
addTxn(b.strategy, Symbol='SPY', TxnDate=CurrentDate,
TxnPrice=ClosePrice, TxnQty = UnitSize , TxnFees=0)
}
} else { # Have a position, so check exit
if( EMA1 < EMA3) {
# exit position
addTxn(b.strategy, Symbol='SPY', TxnDate=CurrentDate,
TxnPrice=ClosePrice, TxnQty = -Posn , TxnFees=0)
} else {
if( i==nrow(SPY) ) # exit on last day
addTxn(b.strategy, Symbol='SPY', TxnDate=CurrentDate,
TxnPrice=ClosePrice, TxnQty = -Posn , TxnFees=0)
}
}
}
updatePortf(b.strategy,Dates=CurrentDate)
updateAcct(b.strategy,Dates=CurrentDate)
updateEndEq(b.strategy,CurrentDate)
} # End dates loop
# transactions
#getTxns(Portfolio=b.strategy, Symbol="SPY")
checkBlotterUpdate(b.strategy,b.strategy)
## [1] TRUE
tstats <- t(tradeStats(b.strategy))
perTradeStats(b.strategy)
library(lattice)
a <- getAccount(b.strategy)
xyplot(a$summary,type="h",col=4)
equity <- a$summary$End.Eq
plot(equity,main="Giulio Strategy Equity Curve")
ret <- Return.calculate(equity,method="log")
charts.PerformanceSummary(ret, colorset = bluefocus,
main="Giulio Strategy Performance")
I tried to replicate the above strategy with quantstrat (using add.indicator, add.signal, add.rule), but the results are definitely different. Here the second code with quantstrat:
# Here the code that does not work
library(quantstrat)
#Initialize a currency and a stock instrument
currency("USD")
stock("SPY",currency="USD",multiplier=1)
# system settings
initDate <- '1997-12-31'
startDate <- '1998-01-01'
endDate <- '2014-06-30'
initEq <- 1e6
Sys.setenv(TZ="UTC")
getSymbols('SPY', from=startDate, to=endDate, index.class="POSIXct", adjust=T)
SPY <- to.weekly(SPY, indexAt='endof', drop.time=FALSE)
SPY$EMA1<-EMA(na.locf(Cl(SPY)),10) # 10 o 3
SPY$EMA2<-EMA(na.locf(Cl(SPY)),25) # 50 o 10
SPY$EMA3<-EMA(na.locf(Cl(SPY)),30) # 200 o 50
SPY$EMA1_lag<-lag(EMA(na.locf(Cl(SPY)),10)) # 200 o 50
# initialize portfolio/account
qs.strategy <- "qsGiulio"
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)
# initialize orders container
initOrders(portfolio=qs.strategy,initDate=initDate)
# instantiate a new strategy object
strategy(qs.strategy,store=TRUE)
strat <-getStrategy(qs.strategy)
add.indicator(strategy = qs.strategy, name = "EMA",
arguments = list(x = quote(na.locf(Cl(mktdata))), n=10), label="EMA1")
add.indicator(strategy = qs.strategy, name = "EMA",
arguments = list(x = quote(na.locf(Cl(mktdata))), n=25), label="EMA2")
add.indicator(strategy = qs.strategy, name = "EMA",
arguments = list(x = quote(na.locf(Cl(mktdata))), n=30), label="EMA3")
add.indicator(strategy = qs.strategy, name = "EMA",
arguments = list(x = quote(lag(na.locf(Cl(mktdata)))), n=10), label="EMA1_lag")
# entry signals
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA1","EMA2"),relationship="gt"),
label="EMA1.gt.EMA2")
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA1","EMA3"),relationship="gt"),
label="EMA1.gt.EMA3")
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA1","EMA1_lag"),relationship="gt"),
label="EMA1.gt.EMA1_lag")
add.signal(qs.strategy, name = "sigFormula",
arguments = list(formula="EMA1.gt.EMA2 & EMA1.gt.EMA3 & EMA1.gt.EMA1_lag"),
label="longEntry")
# exit signals
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA1","EMA3"),relationship="lt"),
label="EMA1.lt.EMA3")
# RULES
# go long when 3 condition
add.rule(qs.strategy, name='ruleSignal',
arguments =
list(sigcol="longEntry", sigval=TRUE, orderqty=900,
ordertype='market', orderside='long'),
type='enter')
# exit when 1 condition
add.rule(qs.strategy, name='ruleSignal',
arguments = list(sigcol="EMA1.lt.EMA3", sigval=TRUE, orderqty='all',
ordertype='market', orderside='long'),
type='exit')
applyStrategy(strategy=qs.strategy , portfolios=qs.strategy)
# transactions
#getTxns(Portfolio=qs.strategy, Symbol="SPY")
checkBlotterUpdate(b.strategy,b.strategy)
## [1] TRUE
# update portfolio/account
updatePortf(qs.strategy)
updateAcct(qs.strategy)
updateEndEq(qs.strategy)
tstats <- t(tradeStats(qs.strategy))
perTradeStats(qs.strategy)
library(lattice)
a <- getAccount(qs.strategy)
xyplot(a$summary,type="h",col=4)
equity <- a$summary$End.Eq
plot(equity,main="Giulio Strategy Equity Curve")
ret <- Return.calculate(equity,method="log")
charts.PerformanceSummary(ret, colorset = bluefocus,
main="Giulio Strategy Performance")
Could anyone help me to understand why the second code doesn't give identical results? I think my mistakes are within the add.indicator, add.signal, add.rule setup, but I am not able to figure out it precisely.
The quanstrat-based code will not provide identical results for several reasons. One is that your columns are not correct in your first 3 add.signal calls. All the columns need to have an "EMA." prefix:
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA.EMA1","EMA.EMA2"),relationship="gt"),
label="EMA1.gt.EMA2")
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA.EMA1","EMA.EMA3"),relationship="gt"),
label="EMA1.gt.EMA3")
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA.EMA1","EMA.EMA1_lag"),relationship="gt"),
label="EMA1.gt.EMA1_lag")
Another issue, and likely the biggest cause of differences, is the next signal:
add.signal(qs.strategy, name = "sigFormula",
arguments = list(formula="EMA1.gt.EMA2 & EMA1.gt.EMA3 & EMA1.gt.EMA1_lag"),
label="longEntry")
That creates a signal for every observation where the formula is true, not just the observations where the formula crosses from false to true. You only want the observations where the formula crosses, so you should use:
add.signal(qs.strategy, name = "sigFormula",
arguments = list(formula="EMA1.gt.EMA2 & EMA1.gt.EMA3 & EMA1.gt.EMA1_lag",
cross = TRUE),
label="longEntry")
Another source of differences is that you always use ~100% of your available equity for your opening long transaction in the blotter version, but you always buy 900 shares in the quantstrat version. You can do something similar in quantstrat by using a custom order sizing function (see osNoOp and osMaxPos for examples of how to write a custom order sizing function).
I am attempting to apply a very basic strategy in quantstrat using the ADX indicator. The strategy looks for situations with a low but upwardly trending ADX, specifically:
Rule 1: ADX <20;
Rule 2: ADX today greater than ADX 5 periods ago;
Rule 3: Exit trade when ADX >35
I have tried to follow Guy Yollen's excellent guide, but am still stuck in the "Trading Logic" component:
library(blotter)
currency("USD")
stock("SPY",currency="USD",multiplier=1)
get("USD",envir=FinancialInstrument:::.instrument)
get("SPY",envir=FinancialInstrument:::.instrument)
Sys.setenv(TZ="UTC")
startDate <- '1998-01-01'
endDate <- '2014-07-31'
getSymbols('SPY',from=startDate,to=endDate,index.class=c("POSIXt","POSIXct"),
adjust=T)
SPY = to.monthly(SPY,indexAt='endof',drop.time=FALSE)
SPY$ADX <- ADX(HLC(SPY))
#rm.strat(a.strategy)
a.strategy <- "TestADX"
initPortf(a.strategy,'SPY',initDate='1997-12-31')
initAcct(a.strategy,portfolios=a.strategy,initDate='1997-12-31',initEq=1e6)
first(SPY)
myTheme <- chart_theme()
myTheme$col$dn.col <- 'lightblue'
myTheme$col$dn.border <- 'lightgray'
myTheme$col$up.border <- 'lightgray'
###Not sure why this one-liner does not work but not critical
chartSeries(x=SPY,theme=myTheme,name="SPY",TA="addADX()")
#####################Trading logic############################################
for( i in 1:nrow(SPY))
{
#update values for this date
CurrentDate <- time(SPY)[i]
equity = getEndEq(a.strategy,CurrentDate)
ClosePrice <- as.numeric(Cl(SPY[i,]))
Posn <- getPosQty(a.strategy,Symbol='SPY',Date=CurrentDate)
UnitSize = as.numeric(trunc(equity/ClosePrice))
adx <- as.numeric(SPY[i,'ADX'])
diffadx <- as.numeric(diff(SPY[i,'ADX',lag=5]))#####INSERT
#change mkt position if necessary
if( !is.na(adx) )#if the moving avg has begun
{
if(Posn == 0) {#No position test to go long
#if((adx < 20) && (diff(adx,lag=5) > 0.2)){
if((adx < 20) && (diffadx > 0.2)){ #####INSERT
#enter long position
addTxn(a.strategy,Symbol='SPY',TxnDate=CurrentDate,
TxnPrice=ClosePrice,TxnQty=UnitSize,TxnFees=0)}
}else {#Have a position so check exit
if ( adx > 35){
#exit position
addTxn(a.strategy,Symbol='SPY',TxnDate=CurrentDate,
TxnPrice = ClosePrice,TxnQty = -Posn,TxnFees=0)}
}
}
#Calculate P&L and resulting Equity with blotter
updatePortf(a.strategy,Dates=CurrentDate)
updateAcct(a.strategy,Dates=CurrentDate)
updateEndEq(a.strategy,Dates=CurrentDate)
}#end Dates loop
##End of trading logic piece####
At this stage R returns error:
Error in if ((adx < 20) && (diffadx > 0.2)) { :
missing value where TRUE/FALSE needed
chart.Posn(a.strategy,Symbol='SPY',Dates='1998::',theme=myTheme)
plot(add_ADX(n=14,col=4,on=1))