Your code has some odd settings which I'll point out in case it is part of your problem:
1) You have set enabled = FALSE
for the stop rule with label StopLossLONG
, so this rule would not be applied regardless.
2) It seems SPY
never reaches 208 in your data period, so you won't get any long entry trades.
You ask if you can set a stop at a specific price. After reading your question carefully I think you mean can I set a stop at some absolute level, like 0.005 here? Rather than some absolute level of "entry price - some threshold amount". Yes, you can, if you modify ruleSignal
. This is why name = 'ruleSignal
is exposed in the add.rule
function ... so you can make your own tweaks to how the orders are generated.
Here is an example, where your entry is now at 202 which permits one trade in your example. It sets a stop level at 9.999 (you could set any absolute price level below your entry price). I've added comments to the function ruleSignalAbsoluteStopPrice
, which will replace ruleSignal
. You can use any function you want, provided you have the correct expected arguments (like those for ruleSignal), and you call addOrder
(or something like addOrder
if you want to use a modified version of this function too ... but you need to understand the quantstrat source before doing this).
If you want to understand how to modify ruleSignal
in a way that is correct in relation to the rest of the existing quantstrat code, try placing browser()
inside ruleSignal and step line by line to see what you need to change to make things work correctly.
See the comments I've added, where I've tweaked parts of the existing ruleSignal
code.
library('TTR')
library('blotter')
library("quantmod")
require(quantstrat)
# Define new function to replace 'ruleSignal' for long stoplimit orders:
ruleSignalAbsoluteStopPrice <- function (mktdata = mktdata, timestamp, sigcol, sigval, orderqty = 0,
ordertype, orderside = NULL, orderset = NULL, threshold = NULL,
tmult = FALSE, replace = TRUE, delay = 1e-04, osFUN = "osNoOp",
pricemethod = c("market", "opside", "active"), portfolio,
symbol, ..., ruletype, TxnFees = 0, prefer = NULL, sethold = FALSE,
label = "", order.price = NULL, chain.price = NULL, time.in.force = "",
absoluteStopPrice = 9.999)
{
if (!is.function(osFUN))
osFUN <- match.fun(osFUN)
if (hasArg(curIndex))
curIndex <- eval(match.call(expand.dots = TRUE)$curIndex,
parent.frame())
else curIndex <- mktdata[timestamp, which.i = TRUE]
# Just for long orderside and stoplimit order types.
if (curIndex > 0 && curIndex <= nrow(mktdata) && ordertype == "stoplimit" && orderside == "long" && (ruletype ==
"chain" )) {
pricemethod <- pricemethod[1]
if (hasArg(prefer))
prefer = match.call(expand.dots = TRUE)$prefer
else prefer = NULL
# chain.price is the transaction price of the long trade.
# Handle the case where the price may be less than your absolute stop level (here we skip entering a long position):
if (chain.price <= absoluteStopPrice)
return()
threshold <- chain.price - absoluteStopPrice
# Ensure that tmult = FALSE when using this approach.
if (is.null(orderside) & !isTRUE(orderqty == 0)) {
curqty <- getPosQty(Portfolio = portfolio, Symbol = symbol,
Date = timestamp)
if (curqty > 0) {
orderside <- "long"
}
else if (curqty < 0) {
orderside <- "short"
}
else {
if (orderqty > 0)
orderside <- "long"
else orderside <- "short"
}
}
if (orderqty == "all") {
if (orderside == "long") {
tmpqty <- 1
}
else {
tmpqty <- -1
}
}
else {
tmpqty <- orderqty
}
if (!is.null(order.price)) {
orderprice <- order.price
}
else if (!is.null(chain.price)) {
orderprice <- chain.price
}
else {
}
if (is.null(orderset))
orderset = NA
if (orderqty != "all") {
orderqty <- osFUN(strategy = strategy, data = mktdata,
timestamp = timestamp, orderqty = orderqty, ordertype = ordertype,
orderside = orderside, portfolio = portfolio,
symbol = symbol, ... = ..., ruletype = ruletype,
orderprice = as.numeric(orderprice))
}
if (!is.null(orderqty) && orderqty != 0 && length(orderprice)) {
# All the arguments passed to `addOrder` are reasonable, and similar to what ruleSignal expects
addOrder(portfolio = portfolio, symbol = symbol,
timestamp = timestamp, qty = orderqty, price = as.numeric(orderprice),
ordertype = ordertype, side = orderside, orderset = orderset,
threshold = threshold, status = "open", replace = replace,
delay = delay, tmult = tmult, ... = ..., prefer = prefer,
TxnFees = TxnFees, label = label, time.in.force = time.in.force)
}
}
if (sethold)
hold <<- TRUE
}
from <- "2016-04-01"
to <- "2016-07-01"
SPY <- getSymbols.yahoo('SPY',
env = .GlobalEnv,
return.class = 'xts',
index.class = 'Date',
from = from,
to = to,
periodicity = "daily",
auto.assign = FALSE)
SPY <- SPY[, c(1, 2, 3, 4, 5)]
names(SPY) <- c('Open','High','Low','Close','Volume')
level <- function(ts, level) {
ts$level <- level
res <- ts$level
names(res) <- c("")
return(res)
}
rm(list = ls(.blotter), envir = .blotter)
symbols = "SPY"
currency('USD')
initDate = from
from = from
to = to
initEq = 100000
strName = "test"
stock(symbols, currency = "USD", tick_size = 0.001, multiplier = 1)
getInstrument(symbols, type = "instrument")
strategy.st <- strName
portfolio.st <- strName
account.st <- strName
rm.strat(portfolio.st)
rm.strat(strategy.st)
initPortf(portfolio.st, symbols = symbols, initDate = initDate, currency = 'USD')
initAcct(account.st, portfolios = portfolio.st, initDate = initDate, currency = 'USD', initEq = initEq)
initOrders(portfolio.st, initDate = initDate)
strategy(strategy.st, store = TRUE)
addPosLimit(portfolio.st, symbols, timestamp = initDate, maxpos = 1, minpos = -1)
# indicators
# Set the level to 202 to allow one entry trade at least:
add.indicator(strategy.st, name = "level",
arguments = list(ts = quote((mktdata)), level = 202.0),
label = "LEV202")
add.indicator(strategy.st, name = "level",
arguments = list(ts = quote((mktdata)), level = 212.0),
label = "LEV212")
add.indicator(strategy.st, name = "level",
arguments = list(ts = quote((mktdata)), level = 207.0),
label = "LEV207")
# signals
add.signal(strategy.st, name = "sigCrossover",
arguments = list(columns = c("Close", "LEV202"),
relationship = "gt"),
label = "OPEN")
add.signal(strategy.st, name = "sigCrossover",
arguments = list(columns = c("Close", "LEV212"),
relationship = "gt"),
label = "CLOSE")
# rules
add.rule(strategy.st, name = "ruleSignal",
arguments = list(sigcol = "OPEN", sigval = TRUE,
orderside = "long",
ordertype = "market",
prefer = "Open",
orderqty = 1,
replace = FALSE,
osFUN = osMaxPos
),
type = "enter",
label = "LE"
)
add.rule(strategy.st, name = "ruleSignal",
arguments = list(sigcol = "CLOSE", sigval = TRUE,
orderside = "long",
ordertype = "market",
prefer = "Open",
orderqty = "all",
replace = FALSE
),
type = "exit",
label = "LX"
)
add.rule(strategy.st,
name = "ruleSignalAbsoluteStopPrice",
arguments = list(sigcol = "OPEN",
sigval = TRUE,
replace = FALSE,
orderside = "long",
ordertype = "stoplimit",
#threshold = quote(0.005), don't bother setting threshold argument as ruleSignalAbsoluteStopPrice won't used the passed in argument 'threshold'.
absoluteStopPrice = 9.999, # Demonstrat that we can use new arguments related to the `ruleSignalAbsoluteStopPrice`` function
tmult = FALSE, # tmult is potentially used in `addOrder`
orderqty = "all",
orderset = "ocolong"),
type = "chain",
parent = "LE",
label = "StopLossLONG",
enabled = TRUE # Enable this rule
)
applyStrategy(strategy.st, portfolio.st)
save.strategy(strategy.st)
orderbook <- getOrderBook(portfolio.st)
orderbook
Now check you get what you expected (a stoplimit at the price 9.999):
> orderbook
$test
$test$SPY
Order.Qty Order.Price Order.Type Order.Side Order.Threshold Order.Status Order.StatusTime Prefer Order.Set Txn.Fees Rule Time.In.Force
2016-04-13 "1" "201.827" "market" "long" NA "closed" "2016-04-14 00:00:00" "Open" NA "0" "LE" ""
2016-04-14 "all" "9.999" "stoplimit" "long" "-192.812707" "open" NA "" "ocolong" "0" "StopLossLONG" ""
And we see the trade is still open:
> getTxns(portfolio.st, "SPY")
Txn.Qty Txn.Price Txn.Fees Txn.Value Txn.Avg.Cost Net.Txn.Realized.PL
2016-04-01 00:00:00 0 0.00 0 0.00 0.00 0
2016-04-13 20:00:00 1 202.87 0 202.87 202.87 0
Hope this helps.