Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

quantstrat: Avoding consecutive entry orders #120

Closed
rfinfun opened this issue May 31, 2020 · 5 comments
Closed

quantstrat: Avoding consecutive entry orders #120

rfinfun opened this issue May 31, 2020 · 5 comments

Comments

@rfinfun
Copy link

rfinfun commented May 31, 2020

Dear all, and thanks to Jasen for inviting me to post my question and a reproducable example here. I am an engineer by education, marketer by profession and R fan for about five years counting.

This is the first attempt at coding a strategy, utilizing a filter for trend direction based on Bollinger Bands and a Stochastic cross over for entry and exit. The strategy is far from complete - I am still learning to code and study specific behaviors of indicators. There is no problem with the code, it does what the code says.

  1. Please note consecutive entry orders on the chart in 2015 March and April with no closing order in between and again in December. I trust that this could happen with any strategy, where exit orders had no signal to activate. What is the quantstrat-way of eliminating this behavior of multiple entries?

  2. The filter is obviously not very responsive, the second order in December occurs after both lower highs and lower lows. Is there a quantstrat way of identifying a trend with higher/lower lows and highs or is that too much charting and not enough technical analysis?

Thanks in advance and kind regards,
Andreas

# BBTM Strategy
# AH
# Strategy utilizing stochastic for entry and exit filtered via moving average
# Long only for now. 
# Based on rocema.R 
# Issues for discussion:
#   - How to prevent multiple entry orders: StochEntryLong


# Initialize --------------------------------------------------------------

Sys.setenv(TZ="UTC")
initEq = 1000000
.txnfees <- -5
.stoploss <- 0.02
.stoptrailing <- 0.03
orderQ <- 100

# BBands params
n = 45
sd = 1.8

# Stoch params
nFastK = 9
nFastD = 2

# Custom Function --------------------------------------------------------

BBTMDir = function(HLC, n = 10, sd = 1, nFastK = 5, nFastD = 2) {
  require(quantmod)
  # BBTM Trend Following Model
  bb <- TTR::BBands(HLC(HLC), n = n, sd = sd )
  x <- Cl(HLC)
  bb_dir <- ifelse(x > bb$up & lag(x) > lag(bb$up),  1, 
                   ifelse(x < bb$dn & lag(x) < lag(bb$dn), -1, 
                          NA
                   )
  )
  bb_dir <- na.locf(bb_dir)
  colnames(bb_dir) <- "BBDir"
  
  sto <- TTR::stoch(HLC(HLC), nFastK = nFastK, nFastD = nFastD)  
  # d <- merge(HLC, bb_dir, sto)
  
  sto_sig <- sigThreshold(label = "StochSig", data = sto, column = "fastD", 
                          threshold = 0.25, relationship = "gt", cross = TRUE) * 1 + 
    sigThreshold(label = "StochSig", data = sto, column = "fastD", 
                 threshold = 0.75, relationship = "lt", cross = TRUE) * -1
  merge(bb_dir, sto_sig)
}

# Main --------------------------------------------------------------------

require(quantstrat)

currency('USD')
symbol_str <- "XLK"
stock( symbol_str, currency = "USD")
getSymbols(symbol_str)


p <- "bbtm_p"
a <- "etf_a"

suppressWarnings( rm( list = c(paste0("portfolio.", p), paste0("account.", a)), pos = .blotter) )
suppressWarnings( rm( list = paste0("order_book.", p), pos = .strategy) )

initPortf(p, symbols=symbol_str, currency="USD")
initAcct(a, portfolios=p, currency="USD") 

# Strategy Setup ----------------------------------------------------------
s <- strategy(p)

s <- add.indicator( s, 'BBTMDir', label='BBTM',
                    arguments=list( HLC = quote(HLC(mktdata))) )

# Signals for order entry on bb_dir
s <- add.signal( s, 'sigThreshold', label='ChgDirLong', 
                 arguments = list(column="BBDir.BBTM", relationship="gt", threshold=0, cross=TRUE))
s <- add.signal( s, 'sigThreshold', label='ChgDirShort', 
                 arguments = list(column="BBDir.BBTM", relationship="lt", threshold=0, cross=TRUE))

# Stoch Entry and Exit ----------------------------------------------------

s <- add.signal( s, 'sigFormula', label='StochEntryLong', 
                 arguments = list(cross = TRUE, formula = "BBDir.BBTM == 1 & StochSig.BBTM == 1"))
s <- add.signal( s, 'sigFormula', label='StochExitLong', 
                 arguments = list(cross = TRUE, formula = "BBDir.BBTM == 1 & StochSig.BBTM == -1"))

s <- add.rule(   
  s, name='ruleSignal', type='enter', label='StochEntryLong', 
  arguments = list(
    sigcol="StochEntryLong", sigval=TRUE, orderqty=orderQ,
    TxnFees=.txnfees, 
    ordertype='market', orderside='long', orderset = "ocolong"
  )
)

s <- add.rule( 
  s, name='ruleSignal', type='exit', label='StochExitLong',
  arguments = list(
    sigcol="StochExitLong", sigval=TRUE, replace = TRUE, orderqty="all",
    ordertype='market', orderside='long', orderset = "ocolong"
  )
)

# Run Simulation ----------------------------------------------------------

initOrders(p)
applyStrategy(s, p, parameters=list(n = n, sd = sd, nFastK = nFastK, nFastD = nFastD), 
              verbose = TRUE, prefer = "open")
updatePortf(p)

chart.Posn(p, symbol_str, Dates = "2015-02::2016-02")
add_TA(mktdata$BBDir.BBTM)
@jaymon0703
Copy link
Collaborator

jaymon0703 commented May 31, 2020

Thanks @rfinfun for the reproducible example. Let me see if i can assist.

  1. You could add a Position Limit with addPosLimit and specify osFUN=osMaxPos in your rule arguments list, for example below:
# Add this line after you initialize the account and portfolio
addPosLimit(portfolio=p, symbol_str, timestamp = first(index(get(symbol_str))), 100, 1 ) #set max pos

# Add to your rules so they become:
s <- add.rule(   
  s, name='ruleSignal', type='enter', label='StochEntryLong', 
  arguments = list(
    sigcol="StochEntryLong", sigval=TRUE, orderqty=orderQ,
    TxnFees=.txnfees, 
    ordertype='market', orderside='long', orderset = "ocolong", osFUN=osMaxPos
  )
)

s <- add.rule( 
  s, name='ruleSignal', type='exit', label='StochExitLong',
  arguments = list(
    sigcol="StochExitLong", sigval=TRUE, replace = TRUE, orderqty="all",
    ordertype='market', orderside='long', orderset = "ocolong", osFUN=osMaxPos
  )
)

Now, I have a similar problem in a Turtles strategy i am adding to quantstrat. See the issue at #119. There must be a better solution than a maxPos, and i think i have solved for this with custom signals or custom indicators in the past. If i come up with something more elegant i will let you know.

  1. I am not sure which December trade you are referring to and not sure i understand the question exactly. @braverock might be able to advise on the wider range of filters available out there. I have been meaning to look into Kalman filters, not sure if they will be useful for you in this instance.

@rfinfun
Copy link
Author

rfinfun commented Jun 3, 2020

Hi @jahmon0703 I will try that. It is not so elegant. I will be glad to look at your turtles implementation if that is helpful. I will research Kaiman filters, too. That sounds very elegant. Regards, Andreas

@jaymon0703
Copy link
Collaborator

jaymon0703 commented Jun 3, 2020

I should add the osFUN param is probably where you want to solve your problem, and worth a look is some posts and code from @IlyaKipnis.

https://quantstrattrader.wordpress.com/2014/09/02/the-limit-of-atr-order-sizing/
https://quantstrattrader.wordpress.com/2014/08/29/comparing-atr-order-sizing-to-max-dollar-order-sizing/

In Ilya's osDollarATR function for example (see https://github.com/IlyaKipnis/IKTrading/blob/master/R/osDollarATR.R), he takes the current position as an argument for determining the subsequent trade size. This example should be sufficient for you to define your own position management.

[EDIT] Should probably ask @IlyaKipnis if we can include his osDollarATR function and example demo in quantstrat...

@rfinfun
Copy link
Author

rfinfun commented Jun 6, 2020

Thanks, osFUN works as expected. IKTrading does not run on R 3.6.3, while DSTrading does. Maybe there are some additional functions that would be worth including in quantstrat. To build conditions a generic function would help: Allowing to evaluate multiple variables e.g. AND, OR, XOR, XAND, NOT or the like. I have seen comments frin @IlyaKipnis that the internals of R slow down sigFormula significantly. I have observed that, too and switched to generating the signals in my own function. Thanks.

@rfinfun rfinfun closed this as completed Jun 6, 2020
@braverock
Copy link
Owner

sigFormula is really convenient as a prototyping tool, allowing you to construct a formula using the column names. This uses the formula functionality inherent in R itself. Unfortunately, that formula code requires a data.frame, so there is a conversion as.data.frame when sigFormula is run. For large or high-frequency data, this can be very slow.

Custom signal functions will always be faster, as they don't need to parse the whole object, just the information of interest, and you can take care not to make copies.

An in-between approach would be to convert to data.table, which is more efficient from xts than data.frame. Then you could do the signal query in data.table, and return a vector of signals to be appended as a column to the xts time series.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants