General info:
R-Version: 3.1.0
blotter: 0.8.19
Problem description:
I am trying to implement a quantstrat account which uses multiple portofolios with different currencies.
So here's my basic setup:
- 1 account in EUR
- 1 portfolio in USD
So in order for this to work I have to setup an exchange rate, which I based on data retrieved from yahoo. Then I should run my basic strategy and the conversion will be done automatically in the last step via the updateAcct function.
Now here's the rub... I think the updateAcct function has a bug.
MyCode:
initDate="1990-01-01"
from="2007-01-01"
to="2012-12-31"
options(width=70)
options("getSymbols.warning4.0"=FALSE)
currency(c('USD','EUR'))
exchange_rate("USDEUR", tick_size = 0.01)
USDEUR <- Cl(getSymbols("EUR=X",src="yahoo", auto.assign = FALSE))
Sys.setenv(TZ="UTC")
#not sure why this might work
.blotter <- new.env()
.strategy <- new.env()
symbols <- c("^IXIC" #Nasdaq
)
if(!"XLB" %in% ls()) {
suppressMessages(getSymbols(symbols, from=from, to=to, src="yahoo", adjust=TRUE))
}
#need this to remove index call symbol (yahoo.) from string. I.e. get ^IXIC, but named IXIC
symbols<-gsub("\\^", "", symbols)
stock(symbols, currency="USD", multiplier=1)
#trade sizing and initial equity settings
tradeSize <- 10000
initEq <- tradeSize*length(symbols)
strategy.st <- portfolio.st <- account.st <- "TradeNasdaq100"
#clear old strategies etc.
suppressWarnings(try(rm.strat(strategy.st), silent=TRUE))
#initialize portfolio and account
initPortf(portfolio.st, symbols=symbols, initDate=initDate, currency='USD')
initAcct(account.st, portfolios=portfolio.st, initDate=initDate, currency='EUR',initEq=initEq)
initOrders(portfolio.st, initDate=initDate)
strategy(strategy.st, store=TRUE)
Then I use some indicators, signals, rules etc....
#apply strategy
t1 <- Sys.time()
out <- applyStrategy(strategy=strategy.st,portfolios=portfolio.st)
t2 <- Sys.time()
print(t2-t1)
#set up analytics
updatePortf(portfolio.st)
dateRange <- time(getPortfolio(portfolio.st)$summary)[-1]
updateAcct(account.st,dateRange)
Everything works until the code reaches the last line.
The last line will be give the error message: Error in isTRUE(invert) : object 'invert' not found
Possible bug: So I decided to check out the updateAcct function try a little debug here... I am pretty sure that there is a mistake in code. The if clause in line 63 queries for isTRUE(invert), but invert is only created if it is actually true (see else clause line 46). But invert is not initialized, thus if it is actually false the code will fail.
Here's the source code blotter (original)
function (name = "default", Dates = NULL)
{
Account <- getAccount(name)
if (!is.null(attr(Account, "currency"))) {
a.ccy.str <- attr(Account, "currency")
}
Portfolios = names(Account$portfolios)
if (is.null(Dates))
Dates <- unique(do.call(c, c(lapply(Portfolios, function(x) index(.getPortfolio(x)$summary)),
use.names = FALSE, recursive = FALSE)))[-1]
if (!length(Dates))
return(name)
if (last(index(Account$summary)) > .parseISO8601(Dates)$first.time) {
whichi <- first(Account$summary[paste(.parseISO8601(Dates)$first.time,
"::", sep = ""), which.i = TRUE])
if (!is.null(whichi))
whichi = whichi - 1
if (whichi < 1)
whichi = 1
Account$summary = Account$summary[1:whichi, ]
}
for (pname in Portfolios) {
Portfolio = .getPortfolio(pname)
if (!is.null(attr(Portfolio, "currency"))) {
p.ccy.str <- attr(Portfolio, "currency")
}
psummary = Portfolio$summary[Dates]
if (a.ccy.str != p.ccy.str) {
CcyMult <- NA
port_currency <- try(getInstrument(p.ccy.str), silent = TRUE)
if (inherits(port_currency, "try-error") | !is.instrument(port_currency)) {
warning("Currency", p.ccy.str, " not found, using currency multiplier of 1")
CcyMult <- 1
}
else {
FXrate.str <- paste(p.ccy.str, a.ccy.str, sep = "")
FXrate <- try(get(FXrate.str), silent = TRUE)
if (inherits(FXrate, "try-error")) {
FXrate.str <- paste(a.ccy.str, p.ccy.str, sep = "")
FXrate <- try(get(FXrate.str), silent = TRUE)
if (inherits(FXrate, "try-error")) {
warning("Exchange Rate", FXrate.str, " not found for symbol,',Symbol,' using currency multiplier of 1")
CcyMult <- 1
}
else {
invert = TRUE
}
}
}
if (is.na(CcyMult) && !is.na(FXrate)) {
if (inherits(FXrate, "xts")) {
CcyMult <- FXrate[Dates]
CcyMult <- na.locf(merge(CcyMult, index(psummary)))
CcyMult <- drop(CcyMult[index(psummary)])
}
else {
CcyMult <- as.numeric(FXrate)
}
}
else {
CcyMult <- 1
}
if (isTRUE(invert)) {
CcyMult <- 1/CcyMult
}
psummary <- psummary * CcyMult
}
Account$portfolios[[pname]] = rbind(Account$portfolios[[pname]],
psummary)
}
summary = NULL
table = .getByPortf(Account, "Net.Trading.PL", Dates)
obsLength = length(index(table))
obsDates = index(table)
if (obsLength > 1)
on = periodicity(table)$units
else on = "none"
Attributes = c("Additions", "Withdrawals", "Realized.PL",
"Unrealized.PL", "Interest", "Gross.Trading.PL", "Txn.Fees",
"Net.Trading.PL", "Advisory.Fees", "Net.Performance",
"End.Eq")
for (Attribute in Attributes) {
switch(Attribute, Realized.PL = , Unrealized.PL = , Gross.Trading.PL = ,
Txn.Fees = , Net.Trading.PL = {
table = .getByPortf(Account, Attribute, Dates)
result = xts(rowSums(table, na.rm = TRUE), order.by = index(table))
}, Additions = {
result = if (on == "none") as.xts(sum(Account$Additions[paste("::",
obsDates, sep = "")]), order.by = index(table)) else {
if (length(Account$Additions[obsDates]) > 0) period.apply(Account$Additions[obsDates],
endpoints(Account$Additions[obsDates], on = on),
sum) else xts(rep(0, obsLength), order.by = obsDates)
}
}, Withdrawals = {
result = if (on == "none") as.xts(sum(Account$Withdrawals[paste("::",
obsDates, sep = "")]), order.by = index(table)) else {
if (length(Account$Additions[obsDates]) > 0) period.apply(Account$Withdrawals[obsDates],
endpoints(Account$Withdrawals[obsDates],
on = periodicity(table)$units), sum) else xts(rep(0,
obsLength), order.by = obsDates)
}
}, Interest = {
result = if (on == "none") as.xts(sum(Account$Interest[paste("::",
obsDates, sep = "")]), , order.by = index(table)) else {
if (length(Account$Additions[obsDates]) > 0) period.apply(Account$Interest[obsDates],
endpoints(Account$Interest[obsDates], on = periodicity(table)$units),
sum) else xts(rep(0, obsLength), order.by = obsDates)
}
}, Advisory.Fees = , Net.Performance = , End.Eq = {
result = xts(rep(0, obsLength), order.by = obsDates)
})
colnames(result) = Attribute
if (is.null(summary)) {
summary = result
}
else {
summary = cbind(summary, result)
}
}
summary[is.na(summary)] <- 0
Account$summary <- rbind(Account$summary, summary)
assign(paste("account", name, sep = "."), Account, envir = .blotter)
return(name)
}
Here's what I think it should look like (snippet line 28-50)...
if (a.ccy.str != p.ccy.str) {
CcyMult <- NA
port_currency <- try(getInstrument(p.ccy.str), silent = TRUE)
if (inherits(port_currency, "try-error") | !is.instrument(port_currency)) {
warning("Currency", p.ccy.str, " not found, using currency multiplier of 1")
CcyMult <- 1
}
else {
FXrate.str <- paste(p.ccy.str, a.ccy.str, sep = "")
FXrate <- try(get(FXrate.str), silent = TRUE)
invert=FALSE #THIS IS THE LINE NEEDED FOR FIXING
if (inherits(FXrate, "try-error")) {
FXrate.str <- paste(a.ccy.str, p.ccy.str, sep = "")
FXrate <- try(get(FXrate.str), silent = TRUE)
if (inherits(FXrate, "try-error")) {
warning("Exchange Rate", FXrate.str, " not found for symbol,',Symbol,' using currency multiplier of 1")
CcyMult <- 1
}
else {
invert = TRUE
}
}
}
TL;DR
I think there's bug in blotter:updateAcct which occurs when the currency conversion does not need to invert the exchange rate...
Question: Am I right is this a bug? Or am I missing something?
P.S.:
I would normally file this as a bug but A) I don't know how to file the bug with the authors B) I still a newbie with quantstrat, blotter and Co. and I think somebody else should check this out as well (and the authors are hanging out here quite frequently as well)...