(I know this is a long and messy question, but I really hope someone can help me since I have been working on this supposingly simple stuff for about two weeks...)
Two questions
1) In quantstrat, I find the function rulePctEquity that can be used for rebalancing portfolios. When I read the code inside this function, I find that this function is mainly about having a new addPosLimit. May I know is it correct to understand the add.rule(qs.strategy, 'rulePctEquity',...) as a tool to set the frequencies of checking other rules and the max positions to get in? In other words, Does it mean no trades will be made if it is the only rule in the strategy?
2) Before showing my code, a summary of my testing strategy is as follow. In the start of each month, the portfolio will sell all of the stocks it has and buy the top 3 (ranking ) selected stocks at that time. Equal weight is used to test.
However, my testing result is very different from what I think it should show. To save time, I think the problem is from the section 4. Functions indicating buy, sell short, sell and buy to cover
library(quantstrat)
library(lattice)
quotes.file <- "testfile.csv" #Need editing
quotes.file2 <- "testfile2.csv" #Need editing
#
# 1. Instrument: read csv file into xts object
#
code <- read.csv(quotes.file2,head=TRUE)
data2 <- read.csv(quotes.file, head=TRUE)
code=apply(code,1,"toString")
matsplitter<-function(M, r, c) {
rg <- (row(M)-1)%/%r+1
cg <- (col(M)-1)%/%c+1
rci <- (rg-1)*max(cg) + cg
N <- prod(dim(M))/r/c
cv <- unlist(lapply(1:N, function(x) M[rci==x]))
dim(cv)<-c(r,c,N)
cv
}
data=matsplitter(data2[,-1],nrow(data2),4)
colnames(data)=c("close","pe","pe_avg","pe_sd")
dates <- as.Date(data2$Date,"%d/%m/%Y")
C=list()
i=1
for (i in 1:(dim(data)[3])){
C[[i]] <- xts(x=data[,,i], order.by=dates)
}
#
# 2. Support Functions
#
osFixedDollar <- function(timestamp,orderqty, portfolio, symbol, ruletype, ...)
{
ClosePrice <- as.numeric((mktdata[timestamp,"close"]))
orderqty <- round(tradeSize/ClosePrice,-2)
return(orderqty)
}
pause <- function() {
cat ("Press [enter] to continue")
line <- readline()
}
Zscore <- function(x0,y0,z0) {
x <- as.numeric(x0)
y <- as.numeric(y0)
z <- as.numeric(z0)
res <- (x-y)/z
res
}
for (i in 1:(dim(data)[3])){
C[[i]]$Zscore <-Zscore(C[[i]]$pe,C[[i]]$pe_avg,C[[i]]$pe_sd)
}
for (j in 1:(nrow(data))){
temp=0
for (i in 1:(dim(data)[3])){
temp=c(temp,C[[i]][j,"Zscore"])
}
temp=temp[-1]
temp=as.numeric(temp)
temp2=rank(temp)
for (i in 1:(dim(data)[3])){
C[[i]]$ranking[j]=temp2[i]
}
}
for (i in 1:(dim(data)[3])){
assign(code[i],C[[i]])
}
#
# 3. Initializations
#
# Currency:
currency("HKD")
# Instrument:
big.point.value <- 1
for (i in 1:(dim(data)[3])){
stock(code[i], currency="HKD", multiplier=big.point.value)
}
# Strategy, portfolio and account names:
qs.strategy <- "PE.on.HKex"
qs.portfolio <- "Equities"
qs.account <- "My.account"
# Remove previous strategy, portfolio and account:
suppressWarnings(rm(list = c(paste("account", qs.account, sep='.'), paste("portfolio", qs.portfolio, sep='.')), pos=.blotter))
suppressWarnings(rm(list = c(qs.strategy, paste("order_book", qs.portfolio, sep='.')), pos=.strategy))
rm.strat(qs.strategy) # remove strategy etc. if this is a re-run
# Portfolio:
initDate <- '2015-01-01'
initPortf(qs.portfolio, code, initDate=initDate, currency='HKD')
# Account:
initEq <- 100000
initAcct(qs.account, portfolios=qs.portfolio, initDate=initDate, currency='HKD', initEq=initEq)
# Orders:
initOrders(portfolio=qs.portfolio, initDate=initDate)
# Strategy:
strategy(qs.strategy, store=TRUE)
#
# 4. Functions indicating buy, sell short, sell and buy to cover
#
nb.bars.with.no.trade <- 0
topchoice=3
buy.function <- function(x) {
N <- nrow(x)
buy.signals <- numeric(N)
# buy.signals[1:nb.bars.with.no.trade]<- 0
for (i in (nb.bars.with.no.trade+1):N) {
if ( (x[i]<=topchoice)
&& (index(x)[i] < index(get(code[1]))[nrow(data)]) # trick to avoid an open trade at the end
) {
buy.signals[i] <- 1
} else {
buy.signals[i] <- 0
}
}
xts(x=buy.signals, order.by=index(x))
}
sell.function <- function(x) {
N <- nrow(x)
sell.signals <- numeric(N)
#sell.signals[1:nb.bars.with.no.trade]<- 0
for (i in (nb.bars.with.no.trade+1):N) {
#if ( (x[i]<=topchoice) && (index(x)[i] < index(get(code[1]))[nrow(data)])){
# sell.signals[i] <- 0}
#else{
sell.signals[i] <-1
#}
}
xts(x=sell.signals, order.by=index(x))
}
#
# 5. Indicators for buy, sell short, sell and buy to cover
#
add.indicator(strategy = qs.strategy,
name = "buy.function",
arguments = list(x = quote(mktdata[,"ranking"])),
label="Buy")
add.indicator(strategy = qs.strategy,
name = "sell.function",
arguments = list(x = quote(mktdata[,"ranking"])),
label="Sell")
#
# 6. Signals for buy, sell short, sell and buy to cover
#
add.signal(qs.strategy,
name="sigFormula",
arguments = list(formula="X1.Buy == 1"),
label="Buy",
cross=FALSE)
add.signal(qs.strategy,
name="sigFormula",
arguments = list(formula="X1.Sell == 1"),
label="Sell",
cross=FALSE)
#
# 7. Rules for buy, sell short, sell and buy to cover
#
nb.contracts <- 2
round.trip.commission.for.one.contract <- 6
round.trip.commission.for.total.contracts <- round.trip.commission.for.one.contract * nb.contracts
add.rule(qs.strategy, 'rulePctEquity',
arguments=list(rebalance_on='months',
trade.percent=1/topchoice,
refprice=quote(last(mktdata[,"price"][paste('::',curIndex,sep='')])),
digits=0
),
type='rebalance',
label='rebalance')
add.rule(qs.strategy,
name='ruleSignal',
arguments = list(sigcol="Sell",
sigval=TRUE,
orderqty='all',
ordertype='market',
orderside='long',
pricemethod='market',
replace=FALSE,TxnFees=-0),
type='exit',
label="LongExit")
add.rule(qs.strategy,
name='ruleSignal',
arguments = list(sigcol="Buy",
sigval=TRUE,
orderqty=1000000000,
ordertype='market',
orderside='long',
replace=FALSE,pricemethod='market',TxnFees=-0,osFUN='osMaxPos'),
type='enter',
label="LongEntry")
posval<-initEq/topchoice
for(symbol in code){
pos<-round((posval/first(get(symbol)[,1])[,1]),0)
addPosLimit(qs.portfolio,symbol,initDate, maxpos=pos,minpos=-pos)
}
#
# 8. Apply Strategy to Porfolio
#
tradeSize <- initEq/topchoice
applyStrategy.rebalancing(strategy = qs.strategy,
portfolios = qs.portfolio)
updatePortf(qs.portfolio, Symbols=code, Dates=paste('::',as.Date(Sys.time()),sep=''))
updateAcct(qs.account)
updateEndEq(qs.account)
[1] "2015-01-02 00:00:00 HK3 3052 @ 10.92020872"
[1] "2015-01-02 00:00:00 HK3 -3052 @ 10.92020872"
[1] "2015-01-02 00:00:00 HK6 1325 @ 25.16659456"
[1] "2015-01-02 00:00:00 HK6 -1325 @ 25.16659456"
[1] "2015-01-02 00:00:00 HK7 1189 @ 28.02736734"
[1] "2015-01-02 00:00:00 HK7 -1189 @ 28.02736734"
[1] "2015-02-01 00:00:00 HK3 33000 @ 11.05592774"
[1] "2015-02-01 00:00:00 HK3 -33000 @ 11.05592774"
[1] "2015-02-01 00:00:00 HK4 33000 @ 16.49518325"
[1] "2015-02-01 00:00:00 HK6 33000 @ 26.05446399"
[1] "2015-02-01 00:00:00 HK6 -33000 @ 26.05446399"
[1] "2015-03-01 00:00:00 HK3 33000 @ 11.90615998"
[1] "2015-03-01 00:00:00 HK3 -33000 @ 11.90615998"
[1] "2015-03-01 00:00:00 HK4 -33000 @ 16.04816942"
[1] "2015-03-01 00:00:00 HK4 33000 @ 16.04816942"
[1] "2015-03-01 00:00:00 HK6 33000 @ 22.68828416"
[1] "2015-03-01 00:00:00 HK6 -33000 @ 22.68828416"
[1] "2015-04-01 00:00:00 HK4 -33000 @ 16.54012275"
[1] "2015-04-01 00:00:00 HK4 -4868 @ 16.54012275"
[1] "2015-04-01 00:00:00 HK4 4868 @ 16.54012275"
[1] "2015-04-01 00:00:00 HK4 23264 @ 16.54012275"
[1] "2015-04-01 00:00:00 HK6 28132 @ 22.04928995"
[1] "2015-04-01 00:00:00 HK6 -28132 @ 22.04928995"
[1] "2015-04-01 00:00:00 HK8 28132 @ 31.03327848"
[1] "2015-05-01 00:00:00 HK4 -23264 @ 14.2995661"
[1] "2015-05-01 00:00:00 HK4 10225 @ 14.2995661"
[1] "2015-05-01 00:00:00 HK4 -10225 @ 14.2995661"
[1] "2015-05-01 00:00:00 HK4 23264 @ 14.2995661"
[1] "2015-05-01 00:00:00 HK6 33489 @ 24.21335116"
[1] "2015-05-01 00:00:00 HK6 -33489 @ 24.21335116"
[1] "2015-05-01 00:00:00 HK8 -28132 @ 32.12345799"
[1] "2015-05-01 00:00:00 HK8 5357 @ 32.12345799"
[1] "2015-05-01 00:00:00 HK8 -5357 @ 32.12345799"
[1] "2015-05-01 00:00:00 HK8 28132 @ 32.12345799"
Two problems:
1) How can I let the portfolio to sell all of the holdings before start buying? (The portfolio is now doing something opposite)
2) I cannot understand why the quantities become so large when it comes to the second or later months while the first month is ok.
(3052*10.92+1325*25.03+1189*28.03=~100000=InitEq