Design a long-only trading strategy for BIST100 index that bets on reversals after the indes becomes oversold
Buy Signal
Sell Signal
Optimization Perform a walk forward (WFA) analysis with the following parameters;
*Present your results using graphs and tables
In [2]:
library(zoo)
library(TTR)
library(xts)
library(xtsExtra)
library(quantmod)
library(quantstrat)
library(doParallel)
In [3]:
currency("USD")
stock("BIST",currency="USD",multiplier=1)
load("../../database/database.RData")
.from="2000-01-03"
.to="2016-09-30"
BIST<-as.xts(na.omit(merge(Open[,"XU100"], High[,"XU100"], Low[,"XU100"], Close[,"XU100"])))
BIST<-window(BIST,start = .from,end=.to)
colnames(BIST)<-c("Open","High","Low","Close")
BIST<-xts(coredata(BIST), as.POSIXct(time(BIST)))#Must be POSIXct
index(BIST)<-as.POSIXct(round(index(BIST),"day"))
In [54]:
strategy.st = 'OverSold'
portfolio.st = 'MeanReversion'
account.st = 'Investiphi'
#Defined as a function to apply strategies with different rules easily
setup_strat <- function(.open_period = 21, .std_open = 2, .std_close = 2, .rsi_t = 30, .close_period=21, .rsi_period=21) {
rm.strat(strategy.st)
rm.strat(portfolio.st)
rm.strat(account.st)
if (!exists('.blotter')) .blotter <- new.env()
if (!exists('.strategy')) .strategy <- new.env()
initDate<-as.character(as.Date(.from)-1) # One day before data starts
initEq<-30000
initPortf(portfolio.st, symbols='BIST', 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)
#.period = 21
#.std_open = 2.5
#.std_close = 1
add.indicator(strategy.st,
name = "BBands",
arguments = list(
HLC = quote(Cl(mktdata)[,1]),
n = .open_period,
sd = .std_open
),
label="bband_open"
)
add.indicator(strategy.st,
name = "BBands",
arguments = list(
HLC = quote(Cl(mktdata)[,1]),
n = .close_period,
sd = .std_close
),
label="bband_close"
)
add.signal(strategy.st, name='sigCrossover',
arguments = list(
columns=c("Close","dn.bband_open"),
relationship="lt"
),
label='bblong'
)
## Close Signal
add.signal(strategy.st, name='sigCrossover',
arguments = list(
columns=c("Close","dn.bband_close"),
relationship="gte"
),
label='short'
)
add.indicator(strategy.st,
name = "RSI",
arguments = list(
price = quote(Cl(mktdata)[,1]),
n = .rsi_period
),
label="rsi"
)
#.rsi_t = 40 # rsi threshold
add.signal(strategy.st, name='sigThreshold',
arguments = list(
column= "EMA.rsi",
relationship = "lt",
threshold = .rsi_t
),
label='rsilong'
)
add.signal(strategy.st, name='sigFormula',
arguments = list(
columns=c("bblong","rsilong"),
formula= "(bblong == TRUE) & (rsilong == 1)"
),
label='long'
)
.orderqty = 1
.txnfees = 0 # round-trip fee
add.rule(strategy.st,
name='ruleSignal',
arguments=list(sigcol='long' ,
sigval=TRUE,
orderside='long' ,
ordertype='market',
prefer='Open',
tmult=TRUE,
orderqty=+.orderqty,
replace=FALSE
),
type='enter',
label='EnterLONG'
)
add.rule(strategy.st, name='ruleSignal',
arguments=list(sigcol='short',
sigval=TRUE,
orderside='long' ,
ordertype='market',
orderqty='all',
prefer='Open',
TxnFees=.txnfees, #Only on exits
replace=TRUE #Replace any pending open orders
),
type='exit',
label='Exit2SHORT'
)
}
In [26]:
setup_strat()
applyStrategy(strategy.st, portfolio.st)
# Update portfolio & account
updatePortf(portfolio.st)
updateAcct(account.st)
updateEndEq(account.st)
# Analyze performance
chart.Posn(portfolio.st, "BIST")
getEndEq(account.st,.to)
In [36]:
param_s = c(1, 1.5, 2, 2.5, 3)
param_s2 = c(1, 1.5, 2, 2.5, 3)
param_c = c(10,20,30,40)
In [37]:
add.distribution(strategy.st,
paramset.label = 'Paramset',
component.type = 'indicator',
component.label = 'bband_open',
variable = list(sd = param_s),
label = 'bband_open_dist'
)
add.distribution(strategy.st,
paramset.label = 'Paramset',
component.type = 'indicator',
component.label = 'bband_close',
variable = list(sd = param_s2),
label = 'bband_close_dist'
)
add.distribution(strategy.st,
paramset.label = 'Paramset',
component.type = 'signal',
component.label = 'rsilong',
variable = list(threshold = param_c),
label = 'rsilong_dist'
)
In [38]:
add.distribution.constraint(strategy.st,
paramset.label = 'Paramset',
distribution.label.1 = 'bband_open_dist',
distribution.label.2 = 'bband_close_dist',
operator = '>=',
label = 'bband_constraint'
)
In [30]:
summary(get("OverSold",envir = .strategy))
In [31]:
detectCores()
In [32]:
registerDoParallel(cores=8) # Parallel computing
# Use nsamples if you want random samples from the parameter space
results <- apply.paramset(strategy.st,
paramset.label='Paramset',
portfolio.st=portfolio.st,
account.st=account.st,
verbose=FALSE)
In [33]:
stats <- results$tradeStats
stats[order(stats[,"Profit.To.Max.Draw"],decreasing = T),
c("bband_open_dist","bband_close_dist","rsilong_dist","Profit.To.Max.Draw","Ann.Sharpe","End.Equity")]
In [34]:
setup_strat(21, 2.5, 1, 40)
applyStrategy(strategy.st, portfolio.st)
# Update portfolio & account
updatePortf(portfolio.st)
updateAcct(account.st)
updateEndEq(account.st)
# Analyze performance
chart.Posn(portfolio.st, "BIST")
getEndEq(account.st,.to) # We have nearly doubled the equity
In [39]:
wfa_results <-walk.forward(
strategy.st=strategy.st,
portfolio.st=portfolio.st,
account.st=account.st,
paramset.label='Paramset', # Use this paramset
period='months',
k.training=36, # Optimize over last 36 months
k.testing=12, # Trade with optimized params during next 12 months
#nsamples=10, # Only search for 10 param combos
#obj.func=function(x){which(x == max(x,na.rm=T))},
#obj.args=list(x=quote(tradeStats.list$Profit.To.Max.Draw)), #Obj fnc
#audit.prefix='wfa', # Will be used in creating RData filenames
anchored=FALSE) # Rolling WFA, not anchored
In [40]:
wfa_stats = wfa_results[[1]]$apply.paramset$tradeStats
wfa_stats[order(wfa_stats[,"Profit.To.Max.Draw"],decreasing = T),
c("bband_open_dist","bband_close_dist","rsilong_dist","Profit.To.Max.Draw","Ann.Sharpe","End.Equity")]
In [46]:
setup_strat(21, 2, 1.5, 30)
applyStrategy(strategy.st, portfolio.st)
# Update portfolio & account
updatePortf(portfolio.st)
updateAcct(account.st)
updateEndEq(account.st)
# Analyze performance
chart.Posn(portfolio.st, "BIST")
getEndEq(account.st,.to)
In [48]:
setup_strat() #Reset
param_s = c(1, 1.5, 2, 2.5, 3)
param_s2 = c(1, 1.5, 2, 2.5, 3)
param_c = c(10,20,30,40)
param_period = c(7,21,65)
add.distribution(strategy.st,
paramset.label = 'Paramset',
component.type = 'indicator',
component.label = 'bband_open',
variable = list(sd = param_s),
label = 'bband_open_dist'
)
add.distribution(strategy.st,
paramset.label = 'Paramset',
component.type = 'indicator',
component.label = 'bband_open',
variable = list(n = param_period),
label = 'bband_open_period_dist'
)
add.distribution(strategy.st,
paramset.label = 'Paramset',
component.type = 'indicator',
component.label = 'bband_close',
variable = list(sd = param_s2),
label = 'bband_close_dist'
)
add.distribution(strategy.st,
paramset.label = 'Paramset',
component.type = 'indicator',
component.label = 'bband_close',
variable = list(n = param_period),
label = 'bband_close_period_dist'
)
add.distribution(strategy.st,
paramset.label = 'Paramset',
component.type = 'indicator',
component.label = 'rsi',
variable = list(n = param_period),
label = 'rsi_period_dist'
)
add.distribution(strategy.st,
paramset.label = 'Paramset',
component.type = 'signal',
component.label = 'rsilong',
variable = list(threshold = param_c),
label = 'rsilong_dist'
)
add.distribution.constraint(strategy.st,
paramset.label = 'Paramset',
distribution.label.1 = 'bband_open_dist',
distribution.label.2 = 'bband_close_dist',
operator = '>=',
label = 'bband_constraint'
)
In [49]:
results <- apply.paramset(strategy.st,
paramset.label='Paramset',
portfolio.st=portfolio.st,
account.st=account.st,
verbose=FALSE)
In [53]:
stats <- results$tradeStats
stats[order(stats[,"End.Equity"],decreasing = T),
c("bband_open_period_dist","bband_open_dist","bband_close_period_dist","bband_close_dist","rsi_period_dist","rsilong_dist","Profit.To.Max.Draw","Ann.Sharpe","End.Equity")]
In [56]:
setup_strat(7,1.5,1,40,65,21)
applyStrategy(strategy.st, portfolio.st)
# Update portfolio & account
updatePortf(portfolio.st)
updateAcct(account.st)
updateEndEq(account.st)
# Analyze performance
chart.Posn(portfolio.st, "BIST")
getEndEq(account.st,.to)