“从文档来看,我认为我应该使用类型顺序规则来实现这一点,但我似乎找不到任何文档或使用这种类型规则及其工作原理的示例。”
规则类型“订单”用于处理未结订单,如果您使用开箱即用的 quantstrat 函数ruleOrderProc 处理未结订单(这是 quantstrat 中处理未结订单的默认功能,但您可以插入您自己的“订单”处理功能也可以替换ruleOrderProc)。 ruleOrderProc 不提供移动限价订单的功能,例如它对止损订单所做的方式。
要回答您的问题,您可以创建一个自定义规则函数来更新限价单。以下是使用简单策略的方法。
- 工具为GBPUSD,数据来自quantstrat。
- 当 MACD 信号从下方穿过 0 时,策略进入多头头寸。
- 添加了一个止盈订单,设置为比输入交易的价格高 0.25%。
- 还包括一个止损跟踪订单,以使示例更加现实(对于纯定向交易),该订单设置为比进入交易的价格低 0.5%。
- 如果出现另一个 MACD 信号从下方穿过 0 并且多头头寸处于打开状态,并且自上次更新此止盈以来已过去至少 60 分钟,则限价单将移动到新水平 0.5%目前的价格。 (您没有在您的问题中具体说明如何对时间延迟进行建模,因此我正在合理猜测您希望如何做到这一点)。
请注意,规则类型设置为“风险”以更新限价订单,因此在使用ruletype == "order" 处理订单之前评估此自定义规则(如果不清楚,请参阅 quantstrat 中applyRules 的来源)。这意味着如果在当前柱上触发了更新限价单的信号,并且如果价格偶然也触及现有的止盈限价单,则该仓位将不会退出并止盈,而是会更新到新的止盈利润水平。 (因为止盈限价单是否触发的处理发生在之后我们检查更新限价单的信号是否已经触发)。
希望这个示例有助于了解制定自定义规则如何让生活变得更轻松。
library(quantstrat)
from <- "2002-10-20"
to <- "2002-10-24"
symbols <- "GBPUSD"
# Load 1 minute data stored in the quantstrat package
getSymbols.FI(Symbols = symbols,
dir=system.file('extdata',package='quantstrat'),
from=from,
to=to
)
currency(c('GBP', 'USD'))
exchange_rate('GBPUSD', tick_size=0.0001)
strategy.st <- "switchOrderSignal"
portfolio.st <- "switchOrderSignal"
account.st <- "switchOrderSignal"
rm.strat(strategy.st)
initPortf(portfolio.st, symbols = symbols)
initAcct(account.st, portfolios = portfolio.st, initEq = 1e5)
initOrders(portfolio.st)
strategy(strategy.st, store = TRUE)
tradeSize <- 1000
for (sym in symbols) {
addPosLimit(portfolio.st, sym, start(get(sym)), tradeSize)
}
strategy(strategy.st, store=TRUE)
fastMA = 12
slowMA = 26
signalMA = 9
maType = "EMA"
add.indicator(strategy.st, name = "MACD",
arguments = list(x=quote(Cl(mktdata)),
nFast=fastMA,
nSlow=slowMA),
label='co'
)
add.signal(strategy.st,name="sigThreshold",
arguments = list(column="signal.co",
relationship="gt",
threshold=0,
cross=TRUE),
label="signal.gt.zero"
)
add.signal(strategy.st,name="sigThreshold",
arguments = list(column="signal.co",
relationship="lt",
threshold=0,
cross=TRUE),
label="signal.lt.zero"
)
add.rule(strategy.st,name='ruleSignal',
arguments = list(sigcol="signal.gt.zero",
sigval=TRUE,
orderqty=tradeSize,
ordertype='market',
orderside='long',
osFUN = "osMaxPos",
threshold=NULL),
type='enter',
label='enterL',
storefun=FALSE
)
targetThres <- 0.0025
add.rule(strategy.st,name='ruleSignal',
arguments = list(sigcol="signal.gt.zero",
sigval=TRUE,
orderqty="all",
ordertype='limit',
orderside='long',
threshold= targetThres,
tmult = TRUE,
orderset='sysMACD',
replace = FALSE),
type='chain',
label='profitTarget',
parent = "enterL"
)
# add.rule(strategy.st,name='ruleSignal',
# arguments = list(sigcol="signal.lt.zero",
# sigval=TRUE,
# orderqty='all',
# ordertype='market',
# orderside='long',
# threshold=NULL,
# orderset='sysMACD',
# replace = TRUE),
# type='exit',
# label='exitL',
# enable = FALSE
# )
stopThreshold <- 0.005
add.rule(strategy.st,name='ruleSignal',
arguments = list(sigcol="signal.gt.zero",
sigval=TRUE,
orderqty='all',
ordertype='stoptrailing',
orderside='long',
threshold=-stopThreshold,
tmult=TRUE,
orderset='sysMACD',
replace = FALSE),
type='chain',
parent='enterL',
label='movingStop')
# If a position is on, update the limit order price (the take profit) if another
# entry signal is fired (macd signal crosses above 0 again), but only if more
# than the holding.period.secs has passed. Define a custom rule function to handle this logic:
update_profit_target <- function(mktdata = mktdata,
timestamp,
sigcol,
sigval,
orderqty=0,
ordertype,
orderside=NULL,
orderset=NULL,
threshold=NULL,
tmult=FALSE,
replace=TRUE,
delay=0.0001,
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='',
holding.period.secs
) {
# First, we do not process this "ruleSignal" function if the position quantity
# is not 0, because its purpose is only to modify the stoptrailing on a
# position already open:
if (ruletype!='risk' || getPosQty(portfolio.st, symbol, timestamp) == 0) {
return()
}
if(hasArg(curIndex))
curIndex <- eval(match.call(expand.dots=TRUE)$curIndex, parent.frame())
else
curIndex <- mktdata[timestamp,which.i=TRUE]
if(hasArg(prefer)) prefer=match.call(expand.dots=TRUE)$prefer
else prefer = NULL
if (!is.na(mktdata[curIndex,sigcol]) && mktdata[curIndex,sigcol] == sigval) {
#browser()
orderbook <- getOrderBook(portfolio)
ordersubset <- orderbook[[portfolio]][[symbol]]
# Use quantstrat helper function to identify which row in orderbook for this symbol (ordersubset) has the order we want to change:
ii <- getOrders(portfolio=portfolio,
symbol=symbol,
status="open",
timespan=timespan,
ordertype="limit",
side = orderside,
which.i = TRUE)
if (length(ii) > 0) {
# Check first condition, that a specific amount of time has passed:
end.of.holding <- index(ordersubset[ii, ]) + holding.period.secs
if (timestamp < end.of.holding) return()
if (length(ii) > 1)
stop("Have not got logic for handling case with more than one open limit order on orderside of the open position.")
ordersubset[ii, "Order.Status"] <- 'replaced'
ordersubset[ii, "Order.StatusTime"] <- format(timestamp, "%Y-%m-%d %H:%M:%S")
price <- mktdata[curIndex, "Close"]
orderSide <- ordersubset[ii,"Order.Side"]
# Calculate the new limit order price:
if(isTRUE(tmult))
{
threshold = price*threshold
if (orderSide == "long" && threshold < 0)
threshold <- -threshold
else if (orderSide == "Short" && threshold > 0)
threshold <- -threshold
}
price <- price + threshold
if(hasArg(prefer)) prefer=match.call(expand.dots=TRUE)$prefer
else prefer = NULL
neworder <- addOrder(portfolio=portfolio,
symbol=symbol,
timestamp=timestamp,
qty=ordersubset[ii,"Order.Qty"],
price= price - threshold,
ordertype="limit",
prefer=prefer,
side=ordersubset[ii,"Order.Side"],
threshold = threshold,
status="open",
replace=FALSE,
return=TRUE,
orderset=ordersubset[ii,"Order.Set"],
label=label,
...=...,
TxnFees=TxnFees)
# ^ Do not set the statustimestamp because any new orders start with statustimestamp = NA.
ordersubset<-rbind(ordersubset, neworder)
# we we have updated the orderbook for this symbol, we should reflect this
# where the orderbook is stored (in the .strategy environment):
orderbook[[portfolio]][[symbol]] <- ordersubset
put.orderbook(portfolio, orderbook)
}
}
}
add.rule(strategy.st, name = 'update_profit_target',
arguments = list(sigcol="signal.gt.zero",
sigval=TRUE,
orderqty='all',
ordertype='limit',
orderside='long',
threshold=targetThres,
tmult=TRUE,
orderset='sysMACD',
# Set the minimum amount of time that must pass before the current active limit order can be updated again:
holding.period.secs = 3600),
# Setting type as risk means we will update the limit order price on the current bar before processing whether the take profit price (limit price) was touched on this bar.
type = 'risk', # process and update this order after processing whether the trailing stop was touched, any chain exit and entry orders
label='movingProfitTarget')
out<-applyStrategy(strategy.st, portfolios=portfolio.st, verbose=TRUE)
tx <- getTxns(portfolio.st, "GBPUSD")
sum(tx$Net.Txn.Realized.PL)
tx
# Txn.Qty Txn.Price Txn.Fees Txn.Value Txn.Avg.Cost Net.Txn.Realized.PL
# 1950-01-01 00:00:00 0 0.000000 0 0.000 0.000000 0.0000
# 2002-10-20 21:31:00 1000 1.547700 0 1547.700 1.547700 0.0000
# 2002-10-21 05:10:00 -1000 1.542361 0 -1542.361 1.542361 -5.3385
# 2002-10-21 06:22:00 1000 1.542600 0 1542.600 1.542600 0.0000
# 2002-10-22 22:39:00 -1000 1.548863 0 -1548.862 1.548863 6.2625
# 2002-10-22 23:40:00 1000 1.549000 0 1549.000 1.549000 0.0000
# 2002-10-24 09:28:00 -1000 1.552271 0 -1552.271 1.552271 3.2710
# 2002-10-24 11:33:00 1000 1.554200 0 1554.200 1.554200 0.0000
ob <- getOrderBook(portfolio.st)
# Print part of the order book:
ob$switchOrderSignal$GBPUSD[1:20, ]
# Order.Qty Order.Price Order.Type Order.Side Order.Threshold Order.Status Order.StatusTime Prefer Order.Set Txn.Fees Rule Time.In.Force
# 2002-10-20 21:30:00.00010 "1000" "1.5478" "market" "long" NA "closed" "2002-10-20 21:31:00" "" NA "0" "enterL" ""
# 2002-10-20 21:31:00.00010 "all" "1.55156925" "limit" "long" "0.00386925" "replaced" "2002-10-20 23:38:00" "" "sysMACD" "0" "profitTarget" ""
# 2002-10-20 21:31:00.00010 "all" "1.5399615" "stoptrailing" "long" "-0.0077385" "replaced" "2002-10-20 21:33:00" "" "sysMACD" "0" "movingStop" ""
# 2002-10-20 21:33:00.00001 "all" "1.5400615" "stoptrailing" "long" "-0.0077385" "replaced" "2002-10-20 21:34:00" "" "sysMACD" "0" "movingStop" ""
# 2002-10-20 21:34:00.00001 "all" "1.5403615" "stoptrailing" "long" "-0.0077385" "replaced" "2002-10-20 22:03:00" "" "sysMACD" "0" "movingStop" ""
# 2002-10-20 22:03:00.00001 "all" "1.5404615" "stoptrailing" "long" "-0.0077385" "replaced" "2002-10-20 22:06:00" "" "sysMACD" "0" "movingStop" ""
# 2002-10-20 22:06:00.00001 "all" "1.5408615" "stoptrailing" "long" "-0.0077385" "replaced" "2002-10-20 22:20:00" "" "sysMACD" "0" "movingStop" ""
# 2002-10-20 22:20:00.00001 "all" "1.5409615" "stoptrailing" "long" "-0.0077385" "replaced" "2002-10-20 22:23:00" "" "sysMACD" "0" "movingStop" ""
# 2002-10-20 22:23:00.00001 "all" "1.5413615" "stoptrailing" "long" "-0.0077385" "replaced" "2002-10-20 22:24:00" "" "sysMACD" "0" "movingStop" ""
# 2002-10-20 22:24:00.00001 "all" "1.5416615" "stoptrailing" "long" "-0.0077385" "replaced" "2002-10-20 22:25:00" "" "sysMACD" "0" "movingStop" ""
# 2002-10-20 22:25:00.00001 "all" "1.5418615" "stoptrailing" "long" "-0.0077385" "replaced" "2002-10-20 22:26:00" "" "sysMACD" "0" "movingStop" ""
# 2002-10-20 22:26:00.00001 "all" "1.5423615" "stoptrailing" "long" "-0.0077385" "closed" "2002-10-21 05:10:00" "" "sysMACD" "0" "movingStop" ""
# 2002-10-20 23:38:00.00001 "all" "1.55277225" "limit" "long" "0.00387225" "replaced" "2002-10-21 01:58:00" "" "sysMACD" "0" "movingProfitTarget" ""
# 2002-10-21 01:58:00.00001 "all" "1.551469" "limit" "long" "0.003869" "replaced" "2002-10-21 03:09:00" "" "sysMACD" "0" "movingProfitTarget" ""
# 2002-10-21 03:09:00.00001 "all" "1.5508675" "limit" "long" "0.0038675" "canceled" "2002-10-21 05:10:00" "" "sysMACD" "0" "movingProfitTarget" ""
# 2002-10-21 06:21:00.00010 "1000" "1.5427" "market" "long" NA "closed" "2002-10-21 06:22:00" "" NA "0" "enterL" ""
# 2002-10-21 06:22:00.00010 "all" "1.5464565" "limit" "long" "0.0038565" "replaced" "2002-10-21 08:46:00" "" "sysMACD" "0" "profitTarget" ""
# 2002-10-21 06:22:00.00010 "all" "1.534887" "stoptrailing" "long" "-0.007713" "replaced" "2002-10-21 07:01:00" "" "sysMACD" "0" "movingStop" ""
# 2002-10-21 07:01:00.00001 "all" "1.534987" "stoptrailing" "long" "-0.007713" "replaced" "2002-10-21 07:02:00" "" "sysMACD" "0" "movingStop" ""
# 2002-10-21 07:02:00.00001 "all" "1.535387" "stoptrailing" "long" "-0.007713" "replaced" "2002-10-21 07:04:00" "" "sysMACD" "0" "movingStop" ""
# Reasonablness checks: Let's check the results make sense. Consider first trade entered at 2002-10-20 21:30:00. Here is what the signals look like after the trade has been on for 2 hours:
mktdata["2002-10-20 23:36/2002-10-21 00:10"]
# Open High Low Close Volume macd.co signal.co signal.gt.zero signal.lt.zero
# 2002-10-20 23:36:00 1.5489 1.5492 1.5489 1.5492 0 1.414846e-03 -9.644645e-04 0 0
# 2002-10-20 23:37:00 1.5492 1.5492 1.5492 1.5492 0 3.160196e-03 -1.395325e-04 0 0
# 2002-10-20 23:38:00 1.5489 1.5489 1.5489 1.5489 0 2.946509e-03 4.776759e-04 1 0 **
# 2002-10-20 23:39:00 1.5489 1.5489 1.5489 1.5489 0 2.745513e-03 9.312433e-04 0 0
# 2002-10-20 23:40:00 1.5488 1.5488 1.5488 1.5488 0 2.041720e-03 1.153339e-03 0 0
# 2002-10-20 23:41:00 1.5487 1.5487 1.5487 1.5487 0 9.520094e-04 1.113073e-03 0 0
# 2002-10-20 23:42:00 1.5487 1.5487 1.5487 1.5487 0 8.738715e-05 9.079357e-04 0 0
# 2002-10-20 23:43:00 1.5487 1.5487 1.5487 1.5487 0 -5.910279e-04 6.081430e-04 0 0
# 2002-10-20 23:44:00 1.5484 1.5484 1.5484 1.5484 0 -2.661021e-03 -4.568975e-05 0 1
# 2002-10-20 23:45:00 1.5484 1.5484 1.5484 1.5484 0 -4.252555e-03 -8.870627e-04 0 0
# 2002-10-20 23:46:00 1.5485 1.5485 1.5485 1.5485 0 -4.935972e-03 -1.696845e-03 0 0
# 2002-10-20 23:47:00 1.5484 1.5484 1.5484 1.5484 0 -5.930296e-03 -2.543535e-03 0 0
# 2002-10-20 23:48:00 1.5484 1.5484 1.5484 1.5484 0 -6.641779e-03 -3.363184e-03 0 0
# 2002-10-20 23:49:00 1.5483 1.5483 1.5483 1.5483 0 -7.638686e-03 -4.218284e-03 0 0
# 2002-10-20 23:50:00 1.5483 1.5483 1.5483 1.5483 0 -8.332735e-03 -5.041174e-03 0 0
# 2002-10-20 23:51:00 1.5483 1.5483 1.5483 1.5483 0 -8.781583e-03 -5.789256e-03 0 0
# 2002-10-20 23:52:00 1.5483 1.5483 1.5483 1.5483 0 -9.033199e-03 -6.438045e-03 0 0
# 2002-10-20 23:53:00 1.5483 1.5483 1.5482 1.5482 0 -9.642596e-03 -7.078955e-03 0 0
# 2002-10-20 23:54:00 1.5485 1.5486 1.5485 1.5486 0 -7.949460e-03 -7.253056e-03 0 0
# 2002-10-20 23:55:00 1.5486 1.5486 1.5486 1.5486 0 -6.532340e-03 -7.108913e-03 0 0
# 2002-10-20 23:56:00 1.5486 1.5486 1.5486 1.5486 0 -5.347620e-03 -6.756654e-03 0 0
# 2002-10-20 23:57:00 1.5486 1.5486 1.5486 1.5486 0 -4.358482e-03 -6.277020e-03 0 0
# 2002-10-20 23:58:00 1.5486 1.5486 1.5486 1.5486 0 -3.533847e-03 -5.728385e-03 0 0
# 2002-10-20 23:59:00 1.5489 1.5492 1.5489 1.5492 0 2.432916e-04 -4.534050e-03 0 0
# 2002-10-21 00:00:00 1.5492 1.5492 1.5492 1.5492 0 3.199644e-03 -2.987311e-03 0 0
# 2002-10-21 00:01:00 1.5493 1.5493 1.5493 1.5493 0 5.994337e-03 -1.190982e-03 0 0
# 2002-10-21 00:02:00 1.5493 1.5493 1.5492 1.5492 0 7.600410e-03 5.672967e-04 1 0 **
# 2002-10-21 00:03:00 1.5492 1.5492 1.5492 1.5492 0 8.772034e-03 2.208244e-03 0 0
# 2002-10-21 00:04:00 1.5491 1.5491 1.5491 1.5491 0 9.074934e-03 3.581582e-03 0 0
# 2002-10-21 00:05:00 1.5490 1.5490 1.5490 1.5490 0 8.693797e-03 4.604025e-03 0 0
# 2002-10-21 00:06:00 1.5490 1.5490 1.5490 1.5490 0 8.296106e-03 5.342441e-03 0 0
# 2002-10-21 00:07:00 1.5489 1.5489 1.5489 1.5489 0 7.374970e-03 5.748947e-03 0 0
# 2002-10-21 00:08:00 1.5488 1.5488 1.5488 1.5488 0 6.054223e-03 5.810002e-03 0 0
# 2002-10-21 00:09:00 1.5487 1.5487 1.5487 1.5487 0 4.435429e-03 5.535087e-03 0 0
# 2002-10-21 00:10:00 1.5488 1.5488 1.5487 1.5487 0 3.116584e-03 5.051387e-03 0 0
# See that the limit order was updated on a `signal.gt.zero` signal fired at 2002-10-20
# 23:38:00. Then you can see there was another `signal.gt.zero`` signal at 2002-10-21 00:02:00
# which, as expected, did not result in updating the limit order again. The limit
# order updates at 2002-10-21 01:58:00, 2 hours and 20 minutes after the previous limit order update (at 2002-10-20 23:38:00).