I am trying to test some trading strategies involving digital currency. One such strategy involves MACD crossovers, but I would like to optimize the nSlow
& nFast
parameters.
Here's a reproducible example (which runs):
library(httr)
library(plyr)
library(quantstrat)
library(PerformanceAnalytics)
library(IKTrading) # install_github("IlyaKipnis/IKTrading")
poloniex.ohlc.30m <- content(GET("https://poloniex.com/public?command=returnChartData¤cyPair=BTC_ETH&start=1439010600&end=9999999999&period=1800")) # https://poloniex.com/support/api/
ETHBTC.30m <- ldply(poloniex.ohlc.30m, data.frame) # Convert OHLCV to data.frame
ETHBTC.30m$date <- as.POSIXct(ETHBTC.30m$date, origin = "1970-01-01")
# Create 'xts' object:
ethbtc.30m.xts <- xts(ETHBTC.30m[, 2:8], order.by = ETHBTC.30m$date) # is.OHLCV(ETHBTC.30m)
# Rebuild empty environments if RStudio's "Clear All" has been used:
if (!exists('.instrument')) .instrument <- new.env()
if (!exists('.blotter')) .blotter <- new.env()
if (!exists('.strategy')) .strategy <- new.env()
## Optional: Subset timeframe
ETHBTC <- ethbtc.30m.xts[,c("open", "high", "low", "close", "volume")]["2016-02-01::"]
## Define instruments
currency(c('BTC', 'ETH')) # ls_currencies()
exchange_rate('ETHBTC', currency = 'BTC', counter_currency = 'ETH', tick_size = 0.00001)
initDate = '2016-02-01'
initBTC <- 100
initETH <- 0
portfolio.name <- "crypto"
account.name <- "poloniex"
strategy.name <- "accumulator"
symbols <- "ETHBTC"
## To rerun
rm.strat(portfolio.name)
rm.strat(account.name)
rm.strat(strategy.name)
## Initialize Portfolio, Account, and Orderbook
initPortf(name = portfolio.name, symbols = symbols, initPosQty = 0, initDate = initDate, currency = "BTC") # getPortfolio(portfolio.name)
initAcct(name = account.name, portfolios = portfolio.name, initDate = initDate, initEq = 0, currency = "BTC") # getAccount(account.name)
initOrders(portfolio = portfolio.name, symbols = symbols, initDate = initDate) # getOrderBook(portfolio.name)
strategy(strategy.name, store = TRUE) # summary(getStrategy(strategy.name))
## Indicators
# Parameters
.nFast = 60 # 90
.nSlow = 130
.nSig = 45 # 75
add.indicator(strategy.name, name = "MACD", arguments = list(x=quote(Cl(mktdata))), label=NULL)
## Signals
# See Also: applySignals add.indicator link{add.rule} sigComparison sigCrossover sigFormula sigPeak sigThreshold
# MACD
add.signal(strategy.name, "sigCrossover",
arguments = list(columns = c("macd.MACD.ind", "signal.MACD.ind"), relationship = "gt"),
label = 'longEntry')
add.signal(strategy.name, "sigCrossover",
arguments = list(columns = c("signal.MACD.ind", "macd.MACD.ind"), relationship = "gt"),
label = 'signal.gt.macd')
add.signal(strategy.name, "sigThreshold",
arguments = list(column = "macd.MACD.ind", threshold = 0, relationship = "gte"),
label = 'macd.gte.threshold')
add.signal(strategy.name, "sigAND",
arguments=list(columns=c('signal.gt.macd', 'macd.gte.threshold'), cross=FALSE),
label="longExit")
# Order sizing
osFixedDollar <- function(timestamp, orderqty, portfolio, symbol, ruletype, ...)
{
ClosePrice <- as.numeric(Cl(mktdata[timestamp,]))
orderqty <- round(tradeSize/ClosePrice,-2)
return(orderqty)
}
tradeSize <- initBTC/2
## Rules
# Entry
add.rule(strategy.name,name='ruleSignal',
arguments = list(sigcol="longEntry",
sigval=TRUE,
orderqty=1000,
ordertype='market',
orderside='long',
osFUN='osFixedDollar'),
type='enter',
label='EnterLONG',
storefun=FALSE)
# Exit
add.rule(strategy.name,name='ruleSignal',
arguments = list(sigcol="longExit",
sigval=TRUE,
orderqty='all',
ordertype='market',
orderside='long',
osFUN='osFixedDollar'),
type='exit',
label='ExitLONG',
storefun=FALSE)
## Run it
applyStrategy(strategy.name,
portfolios=portfolio.name,
parameters=list(nFast = .nFast, nSlow = .nSlow, nSig = .nSig, maType = 'EMA'),
verbose=TRUE)
updatePortf(Portfolio=portfolio.name,Dates=paste('::',as.Date(Sys.time()),sep=''))
updateAcct(account.name)
updateEndEq(account.name)
## Evaluate
t(tradeStats(portfolio.name))
getTxns(portfolio.name, Symbol = 'ETHBTC')
perTradeStats(portfolio.name, "ETHBTC")
chart.Posn(Portfolio=portfolio.name,Symbol=symbols, type = "line", log.scale = T)
plot(add_Vo())
plot(add_MACD(fast=.nFast, slow=.nSlow, signal=.nSig,maType="EMA")) # nFast = 60, nSlow = 180, nSig = 40, maType = 'EMA'
The above runs perfectly-fine. However, I want to vary the nFast
and nSlow
parameters to the MACD()
function:
## Parameter distribution testing
add.distribution(strategy.name,
paramset.label = 'optEMA',
component.type = 'indicator',
component.label = 'nFast',
variable = list(nFast = 60:80),
label = 'NFAST')
add.distribution(strategy.name,
paramset.label = 'optEMA',
component.type = 'indicator',
component.label = 'nSlow',
variable = list(nFast = 180:200),
label = 'NSLOW')
library(doMC)
registerDoMC(cores=detectCores())
results <- apply.paramset(strategy.name, paramset.label = "optEMA", portfolio=portfolio.name, account=account.name, nsamples=0)
This gives me the following error, which I'm not sure how to debug:
error calling combine function:
<simpleError in fun(result.1, result.2, result.3, result.4, result.5, result.6, ... attempt to select less than one element>
What am I doing wrong? FWIW, I am using Ubuntu 12.04/14.04. Any help is much appreciated. Thanks!!
OK, I figured this out.
The component.label
argument in the add.distribution
function needs to match the label
argument from add.indicator
. So in this particular case, I changed my add.indicator
to:
add.indicator(strategy.name, name = "MACD", arguments = list(x=quote(Cl(mktdata))), label='MACD')
And then changed my add.distribution
to:
add.distribution(strategy.name,
paramset.label = 'optEMA',
component.type = 'indicator',
component.label = 'MACD',
variable = list(nFast = 60:80),
label = 'NFAST')
add.distribution(strategy.name,
paramset.label = 'optEMA',
component.type = 'indicator',
component.label = 'MACD',
variable = list(nSlow = 180:200),
label = 'NSLOW')
And it runs. Leaving this up here in case anybody else stumbles into a similar error.