braverock / quantstrat

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

error: "walk.forward()": unable to extend position sizing to test set

kvekka opened this issue · comments

###Obtain the following error on running the code, detailed below -
###Error in osFUN(strategy = strategy, data = mktdata, timestamp = timestamp, : no position limit defined for portfolio test.opt
It seems that the position sizing function applied to the training set is not being applied to the test set. Is there a better way to solve this?

###code
require(foreach,quietly=TRUE)
require(iterators)
library(doParallel)
library(quantstrat)

Sys.setenv(TZ = "UTC") #UTC

currency('USD')
fast = 10
slow = 73
fastMA <- (5:10)
slowMA <- (70:75)
init_date <- "2000-12-31"
start_date <- "2008-01-01"
end_date <- "2018-12-31"
init_equity <- 1e6 # $10,000
adjustment <- TRUE
.orderqty <- 100
start <- 1.035
end <- 1.1
Perc_diff <- seq(start,end,length.out = 1)
nsamples <- 2
k.training <- 3
k.testing <- 1

basic_symbols <- function() {
symbols <- c(
"SPY" # SPDR S&P 500 ETF Trust
)
}

symbols <- basic_symbols()

getSymbols(Symbols = symbols,
src = "yahoo",
auto.assign=T,
index.class = "POSIXct",
from = start_date,
to = end_date,
adjust = adjustment)

rm(list = ls(.blotter))
strat.st <- "ACT_NOW"
suppressWarnings(rm.strat(strat.st))
strategy(strat.st, store=TRUE)
summary(getStrategy(strat.st))

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

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

Mark_Price <- function(HLC) {
Mark_Price <- Cl(HLC)*01.035
colnames(Mark_Price) <- "Mark_Price"
return(Mark_Price)
}

add.indicator(strategy = strat.st, #correction to be made here
name = "Mark_Price",
arguments = list(HLC = quote(HLC(mktdata))),
label = "Mark_Price")

add.signal(strat.st,
name = "sigComparison",
arguments = list(column = c("nFast", "Mark_Price"),
relationship = "gt"),
label = "fastMAexit")

add.signal(strat.st,
name = "sigComparison",
arguments = list(column = c("nFast", "Mark_Price"),
relationship = "lt"),
label = "fastMAltMarket")

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

add.signal(strat.st, name = "sigFormula",
arguments = list(formula = "long & fastMAltMarket",
cross = FALSE),
label = "longentry")

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

add.rule(strategy = strat.st,
name = "ruleSignal",
arguments = list(sigcol = "longentry",
sigval = TRUE,
orderqty = .orderqty,
ordertype = "market",
orderside = "long",
# threshold = 0.0005,
prefer = "High",
TxnFees = -10,
osFUN = osMaxPos, #osMaxPos osNoOp
replace = TRUE),
type = "enter",
label = "EnterLONG")

add.rule(strat.st,
name = "ruleSignal",
arguments = list(sigcol = "short",
sigval = TRUE,
orderside = "long",
ordertype = "market",
orderqty = "all",
TxnFees = -10,
replace = FALSE),
type = "exit",
label = "Exit2SHORT")

add.rule(strat.st,
name = "ruleSignal",
arguments = list(sigcol = "fastMAexit",
sigval = TRUE,
orderside = "long",
ordertype = "market",
orderqty = "all",
TxnFees = -10,
replace = FALSE),
type = "exit",
label = "Exit2FASTMA")

add.distribution(strat.st,
paramset.label = 'SMA1',
component.type = 'indicator',
component.label = 'nFast',
variable = list(n= fastMA),
label = 'FAST'
)
add.distribution(strat.st,
paramset.label = 'SMA1',
component.type = 'indicator',
component.label = 'nSlow',
variable = list(n = slowMA),
label = 'SLOW'
)

suppressWarnings(rm.strat("opt"))
rm.strat("test.opt")
initPortf(name="opt", symbol, initDate=init_date)
initAcct(name="opt", portfolios="opt",
initDate=init_date, initEq=init_equity)
initOrders(portfolio="opt", initDate=init_date)

for(symbol in symbols){
addPosLimit(portfolio = "opt",
symbol = symbol,
timestamp = init_date,
maxpos = .orderqty)
}

walk.forward

results <- walk.forward(
strategy.st=strat.st,
paramset.label='SMA1',
portfolio.st="opt",
account.st="opt",
period='years',
k.training=k.training,
k.testing=k.testing,
obj.func = function(x){which(x==max(x))},
obj.args = list(x=quote(tradeStats.list$Max.Drawdown)),
audit.prefix='wfa',
nsamples = nsamples,
anchored=FALSE,
verbose=TRUE,
savewf=TRUE
)

@kvekka what is your serssionInfo() ?

clone.portfolio should be copying position limits in current code.

your example does not appear to be replicable.

I had to define the variable 'symbol', which is more or less fine, but get the error:

Error in walk.forward(strategy.st = strat.st, paramset.label = "SMA1",  : 
  obj.func() returned empty result

because your objective function appears to be malformed.

Thank you @braverock for your help.

I was trying to modify an earlier code from #103 and ensure that there is a maximum position of 200 that I hold at all times.
The modified code is provided here.
wfa_quantdevhacks_troubleShoot.txt

The modification to that post included adding osFUN=osMaxPos, in line 54. The function for maximum position is detailed in line 92-97.
The works well for osFUN=osNoOp, but when osMaxPos is included then I get the following error:

Error in osFUN(strategy = strategy, data = mktdata, timestamp = timestamp, :
no position limit defined for portfolio test.opt
In addition: Warning message:
executing %dopar% sequentially: no parallel backend registered

I am thinking that probably it is looking for a portfolio - "test.opt" to apply the position condition as detailed in line 92-97

sessionInfo() is as follows
sessionInfo.txt

The code provided in the issue, earlier, has been modified to show it works well with apply.paramset. However for WFA analysis, it provides the following error for osFUN=osNoOp as well as osMaxPos, as changed in line 113, with position limitation detailed in line 166-171.
wfa_2braverock.txt

Error in osFUN(strategy = strategy, data = mktdata, timestamp = timestamp, :
no position limit defined for portfolio test.opt

The sessionInfo() remains the same for both the cases.

Hi @kvekka, thanks for the report.

You are using quantstrat version 0.15.10, and the error you report was fixed with the commit 4f3a348 after which the version was bumped to 0.15.11. The latest version is 0.16.20. If you update to that version, it should resolve your issue?

Thank you @jaymon0703 for the suggestion. However, updating to quantstrat_0.16.2 did not result in a solution.
The earlier code in #103 led to error

Error in walk.forward(strategy.st = strat.st, paramset.label = "BBOPT", :
obj.func() returned empty result
In addition: Warning message:
In max(x) : no non-missing arguments to max; returning -Inf

On trying to run the WFA demo codes in quantstrat, too, there were errors encountered.

demo(package="quantstrat", topic = "luxor.8.walk.forward")

Error in walk.forward(strategy.st, paramset.label = "WFA", portfolio.st = portfolio.st, :
obj.func() returned empty result
In addition: Warning message:
In max(x$user.func$GBPUSD.DailyEndEq) :
no non-missing arguments to max; returning -Inf

My sessionInfo() is attached herein.
sessionInfo.txt

Please do advise if there is something that I am missing out on?

Ok, so the position limit error is resolved. Thats progress.

The error from the WFA demo relates to a small test dataset, so not an error per se. We just need to load a longer USDGBP dataset and i believe the problem will be resolved and the obj.func() will return a result.

With regards to the WFA code in #103, so presumably the first code snippet, i am able to run the code without error. Are you running that exact snippet?

With regards to the WFA code in #103, (the first snippet), it runs successfully in quantstrat version _0._15.10 but not in version 0.16.2. I am running the exact snippet.

Yes, small dataset for USDGBP could be the reason for obj.func() error. I will re-check on a longer dataset and revert.

Thanks @kvekka. Please share the snippet that is producing the error here and i will run that specifically and see if i can reproduce the error?

Ok, i was on blotter v 0.14.4 and not getting the error you report, but when upgrading to 0.14.6 i get the same error. I will investigate.

Ok so it seems the reason the script in #103 is breaking is due to the last commit in blotter (which bumped the version up to 0.14.6, braverock/blotter@9851c42), as i can run the script fine on version 0.14.5 which is the version before that change. I will pick it up with @braverock and revert. Thanks again for the report @kvekka.

FYI @kvekka - if you would like to run the script on version 0.14.5 then append the SHA for that commit to the install command - devtools::install_github("braverock/blotter@62e9edbae98e297b280d46e838da088e30b64118") - that will get you onto a version which will let you resume WFA analysis on that script.

For more info, the error that gets reported in the result object after the first training call with apply.paramset() gives a hint to the issue, which is part of the commit in braverock/blotter@9851c42.

Browse[2]> result$apply.paramset$error
[[1]]
<simpleError in mget(symbols, Portfolio$symbols): second argument must be an environment>

I suspect the 2nd argument in mget() is incorrectly specified, but will need to confirm...

Thank you @jaymon0703 , with blotter version 0.14.5 the snippet code is working fine.

apply.paramset() gives a hint to the issue, which is part of the commit in braverock/blotter@9851c42.

It could explain the reason why. As, for previous trials with blotter version 0.14.6 on apply.paramsets function, even with other codes, resulted in the return of NULL environment for me.

so, Portfolio$symbols should be an environment.

I suspect a problem in clone.portfolio where this may not be happening correctly

investigating ...

@kvekka , @jaymon0703 I believe that braverock/blotter@16349ee should resolve this issue. please update blotter, test, and report.

the issue was caused because quantstrat:::clone.portfolio gets a copy of the portfolio object, and then uses blottter::put.portfolio to create new portfolio objects for the train and test portfolios. put.portfolio was turning the main object into an environment, but was not constructing the symbols slot as an environment, and the named instrument slots under the symbols slot were also not environments.

The commit that @jaymon0703 references earlier was expecting the symbols slot and the named instrument slots to be environments to allow for the efficiency of mget.

Hopefully this resolves the issue. Thanks for your help in reporting and debugging it.

Thanks @braverock, the script from #103 runs fine with blotter 0.14.7. Look forward to @kvekka's confirmation.

Thank you @braverock and @jaymon0703, the script from #103 runs fine with blotter ver 0.14.7.

However, I seem to encounter errors when sending multiple symbols to analyze. This is being encountered with applyStrategy, and apply.paramset.

With applyStrategy function, errors are displayed if position limits are being added and multiple stocks are being passed.

Error in osFUN(strategy = strategy, data = mktdata, timestamp = timestamp, :
no position limit defined for portfolio opt.port

The apply.paramset() is delivering fine results for a single symbol but does not produce the results environment if multiple stock symbols are passed to it.

My sessionInfo() is attached herein for your kind perusal.
20190825_sessionInfo.blotter_0.14.7.txt

The codes that i am trying to run is as follows.

@kvekka It would help if you could look at the portfolio object before calling walk.forward or apply.paramset

Position limits are in an xts slot in the portfolio at:

portf$symbols[[symbol]]$PosLimit

where portf is the portfolio object in the .blotter environment (or whatever environment you're storing portfolios in).

So it would really help if you verified that the PosLimit slots are filled as expected with the position limits defined by osMaxPos or similar.

If they aren't set correctly, then the problem is likely not in apply.paramset.

If they are set correctly, then we probably have some other problem in quantstrat::clone.portfolio or blotter::put.portfolio or in apply.paramset itself, though I thought I made sure the PosLimit slots were copied correctly.

Hi @kvekka, thanks for the report and posting your script. It would appear you only set a PosLimit for 'SPY':

for(symbol in symbols){addPosLimit(portfolio = portfolio.st, symbol = 'SPY', timestamp = init_date, maxpos = .orderqty)}

If i replace 'SPY' with symbol i get tradeStats results. Please let us know?

Thank you again @braverock and @jaymon0703
The condition in PosLimit was leading to erroneous results. Thank you for highlighting it.

I think everything should be fine now :)

thanks @kvekka. closing this issue as resolved.