Open climbthemt opened 3 years ago
You probably want to create your own custom indicator. Referring to how indicators work in the demos should suffice. You can use ROC together with anyone of the rollsum functions with n=3 and if the last 3 observations are negative, trend is down and indicator will be = -3. The signal will look for a sigThreshold where threshold=-3 and the relationship is "lte" for less than or equal to. Hope that helps.
Something like this:
require(quantstrat)
require(blotter)
symbols = c("SPY")
for(symbol in symbols){ # establish trade-able instruments
stock(symbol, currency="USD",multiplier=1)
getSymbols(symbol,src='yahoo')
}
stratTrend <- strategy("trend.st")
startDate='1997-12-31'
initEq=100000
port.st<-'trend.st'
# trend indicator
require(RcppRoll) # use roll_sumr from RcppRoll
n=3
trend <- function(price, n=n) {
roc <- ROC(price)
roc <- ifelse(roc<0, -1, 1)
rollsum_roc <- roll_sumr(roc, n=3)
return(rollsum_roc)
}
stratTrend <- add.indicator(strategy = stratTrend, name = "trend", arguments = list(price = quote(getPrice(mktdata)),n=n), label="trend")
applyIndicators(strategy = stratTrend, mktdata = SPY)
Gives the below indicator values:
I was striving for something more user friendly using Lag. I thought I was going down the right path here, but there was a problem when combining signals.
# First Signal: sigComparison specifying when ticker closes Cl() 4 times lower than
# previous then closes Cl() above previous high Hi()
Tick5<-(lag(Cl(mktdata), k=4))
Tick4<-(lag(Cl(mktdata), k=3))
Tick3<-(lag(Cl(mktdata), k=2))
Tick2<-(lag(Cl(mktdata), k=1))
Tick1<-(Cl(mktdata))
Tick1_low<-(Lo(mktdata))
add.signal(strategy.st, name = 'sigComparison',
arguments = list(columns=c(Tick5, Tick4)),
relationship = "lt",
label = "drop5_4")
add.signal(strategy.st, name = 'sigComparison',
arguments = list(columns=c(Tick4, Tick3)),
relationship = "lt",
label = "drop4_3")
add.signal(strategy.st, name = 'sigComparison',
arguments = list(columns=c(Tick3, Tick2)),
relationship = "lt",
label = "drop3_2")
add.signal(strategy.st, name = 'sigComparison',
arguments = list(columns=c(Tick1_low, Tick2)),
relationship = "lt",
label = "drop2_1")
add.signal(strategy.st, name = 'sigComparison',
arguments = list(columns=c(Tick2, Tick1)),
relationship = "lt",
label = "bounce1")
add.signal(strategy.st, name = "sigFormula",
arguments = list(formula = "((drop5_4 & drop4_3) & (drop3_2 & drop2_1)) & bounce1",
cross = TRUE),
label = "longentry")
`
But it fails at 'sigFormula' because sigFormula cannot boolean compare more than 2 signals.
So how to combine multiple signals cleanly?
Once I can get this to work nicely, I'll refactor and see if I can make a new function out of it.
IMHO i dont think 5 signals is user friendly, and using sigFormula should be avoided where possible as it will be slower than the native signal functions. A custom indicator makes more sense i think. Also, you dont need to use ROC (from TTR) but can get away with diff() from base R.
Ultimately, I'm looking to make a function that has a simple input that is easy for low level coders. ie. For the original example of: " If SPY closes down 3 days in a row then buy. Then sell if SPY closes above the previous high." The imagined input would be
buy_signal <- price_action(totalticks=3, action=list( " Cl(-3) > Cl(-2)", "Cl(-2)>Cl(-1)", "Cl(-1)>Cl() " )
sell_signal <- price_action((totalticks=2, action=list( " Hi(-1)<Cl() " )
`
Well, with a custom indicator you can set n
to your preferred value, even using a range of values in parameter optimizations. A custom indicator is the best practice imo. Below is the same code, but with a signal added, from which you can add a rule to buy at the next bar, or whatever your data prescribes (BBO or bar data).
# Load Packages:
require(quantstrat)
require(blotter)
symbols = c("SPY")
for(symbol in symbols){ # establish trade-able instruments
stock(symbol, currency="USD",multiplier=1)
getSymbols(symbol,src='yahoo')
}
# Initialize Account, Portfolio, Strategy
stratTrend <- strategy("trend.st")
startDate='1997-12-31'
initEq=100000
port.st<-'trend.st' #use a string here for easier changing of parameters and re-trying
# trend indicator
require(RcppRoll) # use roll_sumr from RcppRoll
n=3
trend <- function(price, n=n) {
roc <- ROC(price)
roc <- ifelse(roc<0, -1, 1)
rollsum_roc <- roll_sumr(roc, n=3)
return(rollsum_roc)
}
# Indicator
stratTrend <- add.indicator(strategy = stratTrend, name = "trend", arguments = list(price = quote(getPrice(mktdata)),n=n), label="trend")
check_Indicators <- applyIndicators(strategy = stratTrend, mktdata = SPY)
check_Indicators
# Signal
stratTrend <- add.signal(strategy = stratTrend, name="sigThreshold",arguments = list(threshold=-3, column="trend",relationship="eq"),label="trend.eq.minus_three")
check_Signals <- applySignals(strategy = stratTrend, mktdata = applyIndicators(strategy=stratTrend, mktdata=SPY))
check_Signals
Signal output:
So first signal fires on 29/01/2007 which means you could open a trade the next day if your order was a market order, or whenever your limit price is reached in the case of a limit order.
Thank! That works. But as a demo example, I will need to add lots of comments to make it more universal and user friendly. I'm going to extend it out as a more broad example.
OK, so I'm hitting a few snags. It looks like there is a shortcoming in the documentation on applyIndicators I've taken a few days to hunt for clarity and found little.
In your code, you applyIndicators for the custom indicator. I was wondering what happens if we select several symbols like
symbols = c("SPY","IBM", "TSLA")
Then how do we apply to all of them? I couldn't find documentation on how how to format 'mktdata=(ALL)' parameter for applyindicators.
check_Indicators <- applyIndicators(strategy = stratTrend, mktdata = SPY)
I intend to update the documentation for applyIndicators, too. It is the minor knowledge holes that are such time eaters. I also thought it strange that the parameter was mktdata = SPY and not mktdata = 'SPY'
I for one am happy to see interest in the package, and willingness from the community to add to documentation is always encouraged, especially from the experience of someone newer to the package. We could always add to Tim Trice's document...we have reached out previously and asked if we could do so - https://timtrice.github.io/backtesting-strategies/
To run applyIndicators
across a range of symbols and store in a list object for example, you can do something like:
ls_Indicators <- list()
for(symbol in symbols) {
ls_Indicators[[symbol]] = applyIndicators(strategy = stratTrend, mktdata = get(symbol))
}
I tested it for 9 symbols of 3625 rows each and it still surprises me how remarkably quick the code is. I used symbols = c("XLF", "XLP", "XLE", "XLY", "XLV", "XLI", "XLB", "XLK", "XLU")
which is from the RSI demo.
Of course, applyIndicators
is predominantly for testing your code, and more specifically your indicators (and applySIgnals
for testing your signals and applyRules
for testing your rules). Therefore there are no portfolio considerations and position constraints etc that are taken into account yet. Those fire at the applyStrategy
stage.
Hold on here, is 'applyIndicators' just a testing function that indicators are working. That 'applyIndicators' doesn't need to be run once you have your code debugged because 'applyStrategy' executes all 3: applyIndicators, applySignals, and applyRules? Do I have this correct?
Correct. Try debugonce(applyStrategy)
before calling applyStrategy
amd step through the guts of the logic.
Apply indicators is here - https://github.com/braverock/quantstrat/blob/6c536dbe97d0efb622c853d8d9445f9b2933b040/R/strategy.R#L162
Apply signals is here - https://github.com/braverock/quantstrat/blob/6c536dbe97d0efb622c853d8d9445f9b2933b040/R/strategy.R#L170
And depending on whether you have path.dependent set to TRUE or FALSE, apply rules is here - https://github.com/braverock/quantstrat/blob/6c536dbe97d0efb622c853d8d9445f9b2933b040/R/strategy.R#L193 or here - https://github.com/braverock/quantstrat/blob/6c536dbe97d0efb622c853d8d9445f9b2933b040/R/strategy.R#L219
Oi vey. I may not be a great R coder, but I can contribute to the docs. And there is much work to be done...
Description
There are no known examples of coding price actions for quantstrat. After days of hunting, I've found nothing on the web, the presentations, or PDFs. The commercial software, AmiBroker, can do it easily in AFL. But quantstrat doesn't seem to support it easily.
An example, create a signal for an ETF, say, SPY. If SPY closes down 3 days in a row then buy. Then sell if SPY closes above the previous high.
I've done some awful kludgy coding to try to do this, but was unsuccessful. If someone knows how to code that, I can put it into a Luxor example format and submit it. However, I don't know how to solve this one.
I intend to contribute to the documentation on quantstrat, as I feel that is the weakest link.