cran-R quantstrat stoploss optimization error

37 Views Asked by At

script adapted from demo folder luxor in quantstrat package:

require(quantstrat)
require(foreach)


if (!"doMC" %in% installed.packages()[, 1]) {
  #install.packages("doMC")
}

#require(doMC)
#registerDoMC(cores = 2)
options(width = 240)
Sys.setenv(TZ = "UTC")

###

startDate = '2002-10-21'
.from = startDate
.to = '2002-10-31'

###

strategy.st = 'luxor'
portfolio.st = 'forex'
account.st = 'IB'

###

.orderqty = 100000
.threshold = 0.0005
.txnfees = -6     # round-trip fee

### Distributions for paramset analysis

.nsamples = 100

.FastSMA = (1:20)
.SlowSMA = (30:80)

.StopLoss = seq(0.05, 2.4, length.out = 48) / 100

# generate 24x24h ISO8601 timespan vector
# this includes non-ISO8601 elements (e.g. "T01:00/T00:59")
# that are discarded at processing

.timespans.start <- paste(sprintf("T%02d", 0:23), ':00', sep = '')
.timespans.stop <- paste(sprintf("T%02d", 0:23), ':59', sep = '')
.timespans <-
  outer(.timespans.start,
        .timespans.stop,
        FUN = paste,
        sep = '/')

# in order to run the full 24x24 hour scan above, comment out the following line:
.timespans <-
  c(
    'T06:00/T10:00',
    'T07:00/T11:00',
    'T08:00/T12:00',
    'T09:00/T13:00',
    'T10:00/T14:00',
    'T11:00/T15:00',
    'T12:00/T16:00'
  )

### Actual arameters

.fast = 1
.slow = 30

#.timespan = 'T09:00/T13:00'
.timespan = 'T00:00/T23:59'
#.timespan = NULL

.stoploss <- 0.40 / 100

### FinancialInstrument

currency(c('GBP', 'USD'))
exchange_rate('GBPUSD', tick_size = 0.0001)

### quantmod

# FinancialInstrument - method for loading data from split files

getSymbols.FI(
  Symbols = 'GBPUSD',
  dir = system.file('extdata', package = 'quantstrat'),
  #         dir='~/R/OHLC',
  from = .from,
  to = .to
  ,
  extension = 'rda'
  ,
  use_identifier = NA
)


### xts

GBPUSD = to.minutes30(GBPUSD)
GBPUSD = align.time(GBPUSD, 1800)

### define strategy

rm.strat(name = "luxor")
rm.strat(name = "forex")
rm.strat(name = "IB")

strategy(strategy.st, store = TRUE)

initPortf(portfolio.st, symbols = 'GBPUSD', currency = 'USD')

addPosLimit(
  portfolio = portfolio.st,
  symbol = 'GBPUSD',
  timestamp = startDate,
  maxpos = .orderqty
)

initAcct(account.st, portfolios = portfolio.st, currency = 'USD')

###

initOrders(portfolio.st)
### indicators

add.indicator(
  strategy.st,
  name = "SMA",
  arguments = list(x = quote(Cl(mktdata)[, 1]),
                   n = .fast),
  label = "nFast"
)

add.indicator(
  strategy.st,
  name = "SMA",
  arguments = list(x = quote(Cl(mktdata)[, 1]),
                   n = .slow),
  label = "nSlow"
)

### signals

add.signal(
  strategy.st,
  name = 'sigCrossover',
  arguments = list(
    columns = c("nFast", "nSlow"),
    relationship = "gte"
  ),
  label = 'long'
)

add.signal(
  strategy.st,
  name = 'sigCrossover',
  arguments = list(
    columns = c("nFast", "nSlow"),
    relationship = "lt"
  ),
  label = 'short'
)

### rules ############

# normal exit rules

add.rule(
  strategy.st,
  name = 'ruleSignal',
  arguments = list(
    sigcol = 'long' ,
    sigval = TRUE,
    replace = TRUE,
    orderside = 'short',
    ordertype = 'market',
    TxnFees = .txnfees,
    orderqty = 'all',
    orderset = 'ocoshort'
  ),
  type = 'exit',
  timespan = .timespan,
  label = 'Exit2LONG'
)

add.rule(
  strategy.st,
  name = 'ruleSignal',
  arguments = list(
    sigcol = 'short',
    sigval = TRUE,
    replace = TRUE,
    orderside = 'long' ,
    ordertype = 'market',
    TxnFees = .txnfees,
    orderqty = 'all',
    orderset = 'ocolong'
  ),
  type = 'exit',
  timespan = .timespan,
  label = 'Exit2SHORT'
)

# normal entry rules

add.rule(
  strategy.st,
  name = 'ruleSignal',
  arguments = list(
    sigcol = 'long' ,
    sigval = TRUE,
    replace = FALSE,
    orderside = 'long' ,
    ordertype = 'stoplimit',
    prefer = 'High',
    threshold = .threshold,
    TxnFees = 0,
    orderqty = +.orderqty,
    osFUN = osMaxPos,
    orderset = 'ocolong'
  ),
  type = 'enter',
  timespan = .timespan,
  label = 'EnterLONG'
)

add.rule(
  strategy.st,
  name = 'ruleSignal',
  arguments = list(
    sigcol = 'short',
    sigval = TRUE,
    replace = FALSE,
    orderside = 'short',
    ordertype = 'stoplimit',
    prefer = 'Low',
    threshold = -.threshold,
    TxnFees = 0,
    orderqty = -.orderqty,
    osFUN = osMaxPos,
    orderset = 'ocoshort'
  ),
  type = 'enter',
  timespan = .timespan,
  label = 'EnterSHORT'
)

### parameter sets




# stop-loss

add.rule(
  strategy.st,
  name = 'ruleSignal',
  arguments = list(
    sigcol = 'long' ,
    sigval = TRUE,
    replace = FALSE,
    orderside = 'long',
    ordertype = 'stoplimit',
    tmult = TRUE,
    threshold = quote(.stoploss),
    TxnFees = .txnfees,
    orderqty = 'all',
    orderset = 'ocolong'
  ),
  type = 'chain',
  parent = 'EnterLONG',
  label = 'StopLossLONG',
  timespan = .timespan,
  enabled = FALSE
)

add.rule(
  strategy.st,
  name = 'ruleSignal',
  arguments = list(
    sigcol = 'short' ,
    sigval = TRUE,
    replace = FALSE,
    orderside = 'short',
    ordertype = 'stoplimit',
    tmult = TRUE,
    threshold = quote(.stoploss),
    TxnFees = .txnfees,
    orderqty = 'all',
    orderset = 'ocoshort'
  ),
  type = 'chain',
  parent = 'EnterSHORT',
  label = 'StopLossSHORT',
  timespan = .timespan,
  enabled = FALSE
)

add.distribution(
  strategy.st,
  paramset.label = 'StopLoss',
  component.type = 'chain',
  component.label = 'StopLossLONG',
  variable = list(threshold = .StopLoss),
  label = 'StopLossLONG'
)

add.distribution(
  strategy.st,
  paramset.label = 'StopLoss',
  component.type = 'chain',
  component.label = 'StopLossSHORT',
  variable = list(threshold = .StopLoss),
  label = 'StopLossSHORT'
)

add.distribution.constraint(
  strategy.st,
  paramset.label = 'StopLoss',
  distribution.label.1 = 'StopLossLONG',
  distribution.label.2 = 'StopLossSHORT',
  operator = '==',
  label = 'StopLoss'
)


enable.rule('luxor', 'chain', 'StopLoss')


results <-
  apply.paramset(
    strategy.st,
    paramset.label = 'StopLoss',
    portfolio.st = portfolio.st,
    account.st = account.st,
    nsamples = .nsamples,
    verbose = TRUE
  )
stats <- results$tradeStats
print(t(stats))
plot(
  100 * stats$StopLossLONG,
  stats$Net.Trading.PL,
  type = 'b',
  xlab = 'Stoploss %',
  ylab = 'Net.Trading.PL',
  main = 'Luxor'
)

results generates error for all samples:

print(results$error) [[1]] <simpleError in variable.name %in% c("timespan"): object 'variable.name' not found>

the same error results if .timespan = NULL in above script is changed to .timespan = 'T00:00/T23:59' and timespan=.timespan is added to entry/exit/stop loss rules in above script

R version 4.2.3 (2023-03-15)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 22.04.2 LTS

Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.10.0
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.10.0

locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8 LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C

attached base packages:
[1] parallel stats graphics grDevices utils datasets methods base

other attached packages:
[1] doMC_1.3.8 iterators_1.0.14 quantstrat_0.24 foreach_1.5.2 blotter_0.16.2 PerformanceAnalytics_2.0.4 FinancialInstrument_1.3.1 quantmod_0.4.24
[9] TTR_0.24.3 xts_0.13.1 zoo_1.8-12

loaded via a namespace (and not attached):
[1] quadprog_1.5-8 lattice_0.20-45 codetools_0.2-19 MASS_7.3-58.3 grid_4.2.3 jsonlite_1.8.7 curl_5.0.1 boot_1.3-28 tools_4.2.3 compiler_4.2.3
1

There are 1 best solutions below

1
Joshua Ulrich On

This was a bug in quantstrat that is now fixed in version 0.25 on GitHub. See the commit for gory details.