用于阈值水平优化的回测 R 包
Backtesting R package for threshold levels optimization
有人知道可以用于回测以优化阈值水平而不是参数输入的 R 包吗?
例如说只想在 ADX(14) > 30 时使用趋势信号进行交易。quantstrat
允许使用 apply.paramset
而不是参数阈值 (30) 来优化参数输入 (14)。
如果这样的包不存在,也许有人可以提供指向要查看哪些包以开始破解这样的任务的指示?
您实际上可以在 quantstrat 中优化信号的阈值水平!事实上,几乎所有指标、信号甚至订单(对于规则)的参数都可以优化。有关优化停止阈值级别的示例,请参阅 quantstrat 演示文件夹。
下面是一个完全可重现的示例,探讨了当 RSI 低于某个 RSI 阈值水平时购买 SPY
的最佳阈值(30 是默认值,并探索我们是否将其更改为 c(20, 25, 30, 35, 60)
).如果 RSI 从下方超过 70,则该策略涉及退出头寸(如果我们继续从下方越过 RSI 阈值,则需要金字塔形 3 个级别)。
library(quantstrat)
strategy.st <- "RSI"
stratRSI <- strategy(strategy.st, store = TRUE)
add.indicator(strategy = strategy.st, name = "RSI", arguments = list(price = quote(getPrice(mktdata))), label="RSI")
add.signal(strategy = strategy.st, name="sigThreshold",arguments = list(threshold=70, column="RSI",relationship="gt", cross=TRUE),label="RSI.gt.70")
add.signal(strategy = strategy.st, name="sigThreshold",arguments = list(threshold=30, column="RSI",relationship="lt",cross=TRUE),label="RSI.lt.30")
add.rule(strategy = strategy.st, name='ruleSignal', arguments = list(sigcol="RSI.lt.30", sigval=TRUE, orderqty= 1000, ordertype='market', orderside='long', pricemethod='market', replace=FALSE, osFUN=osMaxPos), type='enter', path.dep=TRUE)
add.rule(strategy = strategy.st, name='ruleSignal', arguments = list(sigcol="RSI.gt.70", sigval=TRUE, orderqty='all', ordertype='market', orderside='long', pricemethod='market', replace=FALSE), type='exit', path.dep=TRUE)
currency("USD")
symbols = c("SPY")
for(symbol in symbols){
stock(symbol, currency="USD",multiplier=1)
}
getSymbols(symbols, src='yahoo', index.class=c("POSIXt","POSIXct"), from='2015-01-01')
startDate='2005-12-31'
initEq=100000
port.st<-'RSI'
initPortf(port.st, symbols=symbols)
initAcct(port.st, portfolios=port.st, initEq=initEq)
initOrders(portfolio=port.st)
for(symbol in symbols){ addPosLimit(port.st, symbol, startDate, 300, 3 ) } #set max pos
# If you want to run the "base" simulation with standard RSI threshold parameter above of 30:
# out<-try(applyStrategy(strategy=strategy.st , portfolios=port.st, parameters=list(n=2) ) )
#updatePortf(Portfolio=port.st,Dates=paste('::',as.Date(Sys.time()),sep=''))
# Do optimisation on RSI threshold:
add.distribution(strategy.st,
paramset.label = 'testin',
component.type = 'signal',
component.label = 'RSI.lt.30', #this is the label given to the indicator in the strat
variable = list(threshold = c(20, 25, 30, 35, 60) ),
label = 'RSIEntryThreshold'
)
results <- apply.paramset(strategy.st,
paramset.label='testin',
portfolio.st=port.st,
account.st=port.st,
# nsamples=.nsamples,
verbose=TRUE)
stats <- results$tradeStats
# > stats
# RSIEntryThreshold Portfolio Symbol Num.Txns Num.Trades Net.Trading.PL Avg.Trade.PL Med.Trade.PL Largest.Winner Largest.Loser Gross.Profits Gross.Losses Std.Dev.Trade.PL Percent.Positive Percent.Negative Profit.Factor Avg.Win.Trade
# 1 20 RSI.1 SPY 2 1 2319.885 2319.885 2319.885 2319.885 0 2319.885 0 NA 100 0 NA 2319.885
# 2 25 RSI.2 SPY 2 1 2319.885 2319.885 2319.885 2319.885 0 2319.885 0 NA 100 0 NA 2319.885
# 3 30 RSI.3 SPY 8 3 8035.114 2678.371 2105.968 4754.004 0 8035.114 0 1856.8247 100 0 NA 2678.371
# 4 35 RSI.4 SPY 12 3 13614.221 4538.074 4990.450 5153.644 0 13614.221 0 928.4609 100 0 NA 4538.074
# 5 60 RSI.5 SPY 24 6 10832.882 1727.480 1239.326 4971.405 0 10364.882 0 1739.6692 100 0 NA 1727.480
# Med.Win.Trade Avg.Losing.Trade Med.Losing.Trade Avg.Daily.PL Med.Daily.PL Std.Dev.Daily.PL Ann.Sharpe Max.Drawdown Profit.To.Max.Draw Avg.WinLoss.Ratio Med.WinLoss.Ratio Max.Equity Min.Equity End.Equity
# 1 2319.885 NaN NA 2319.885 2319.885 NA NA -1073.679 2.160689 NA NA 2381.600 0.0000 2319.885
# 2 2319.885 NaN NA 2319.885 2319.885 NA NA -1073.679 2.160689 NA NA 2381.600 0.0000 2319.885
# 3 2105.968 NaN NA 2678.371 2105.968 1856.8247 22.89814 -3160.282 2.542531 NA NA 8137.556 -213.9176 8035.114
# 4 4990.450 NaN NA 4538.074 4990.450 928.4609 77.59044 -2147.357 6.339989 NA NA 13921.550 -1464.3554 13614.221
# 5 1239.326 NaN NA 1727.480 1239.326 1739.6692 15.76328 -7426.328 1.458713 NA NA 10832.882 -6419.0599 10832.882
好的,所以结果表明,自 2015 年以来,如果阈值为 35,您就可以做得很好。
现在,检查模拟结果的合理性是个好主意,所以这里有一些您可以做的快速检查(作为激励您为自己的策略做类似事情的例子):
# -------------------------------------------------------------------------------------------------------------------------------------------------
# Do a check on the reasonableness of the results:
# This is the result from entering a trade when RSI crosses above 30 from below:
# We can quickly plot RSI neatly, using the default n = 14 RSI value as a check:
chart_Series(mktdata[, 1:4])
add_RSI(n = 14)
# A safer way to chart the results, using the actual signal data in the `mktdata` object that the simulation ran (we can be confident here that the plain vanilla `RSI(n = 14)` function above using `add_RSI` would be ok too, and quicker, which is why I show it above (which recomputes the signal)):
chart_Series(mktdata[, 1:4])
add_TA(mktdata[, "EMA.RSI"])
add_TA(xts(x = rep(20, NROW(mktdata)), order.by = index(mktdata)), on = 2, col = "blue", lty = 2)
add_TA(xts(x = rep(25, NROW(mktdata)), order.by = index(mktdata)), on = 2, col = "red", lty = 2)
add_TA(xts(x = rep(30, NROW(mktdata)), order.by = index(mktdata)), on = 2, col = "purple", lty = 2)
现在快速检查其中一个模拟发生的交易 --RSI.3
此处对应于 30 的进入阈值 -- 并将它们与实际数据进行比较 and/or 上图。 (您可以在 results$RSI.1$portfolio$symbols$SPY$txn
中看到第一次模拟的结果(RSI 低于 20),在 results$RSI.2$portfolio$symbols$SPY$txn
中看到 RSI 低于 25 的结果,等等)
txns1 <- results$RSI.3$portfolio$symbols$SPY$txn
indexTZ(txns1) <- "UTC" # avoid timezone related issue by keeping all in UTC time
# > txns1
# Txn.Qty Txn.Price Txn.Value Txn.Avg.Cost Pos.Qty Pos.Avg.Cost Gross.Txn.Realized.PL Txn.Fees Net.Txn.Realized.PL Con.Mult
# 1950-01-01 05:00:00 0 0.0000 0.00 0.0000 0 0.0000 0.000 0 0.000 0
# 2015-08-24 00:00:00 100 181.7837 18178.37 181.7837 100 181.7837 0.000 0 0.000 1
# 2015-11-04 00:00:00 -100 202.8433 -20284.33 202.8433 0 0.0000 2105.968 0 2105.968 1
# 2016-01-11 00:00:00 100 186.3479 18634.79 186.3479 100 186.3479 0.000 0 0.000 1
# 2016-01-14 00:00:00 100 186.1733 18617.33 186.1733 200 186.2606 0.000 0 0.000 1
# 2016-01-21 00:00:00 100 181.0905 18109.05 181.0905 300 184.5373 0.000 0 0.000 1
# 2016-03-31 00:00:00 -300 200.3839 -60115.18 200.3839 0 0.0000 4754.004 0 4754.004 1
# 2016-11-04 00:00:00 100 205.4281 20542.81 205.4281 100 205.4281 0.000 0 0.000 1
# 2016-11-28 00:00:00 -100 217.1796 -21717.96 217.1796 0 0.0000 1175.142 0 1175.142 1
# > mktdata["2015-08-20/2015-08"]
# SPY.Open SPY.High SPY.Low SPY.Close SPY.Volume SPY.Adjusted EMA.RSI RSI.gt.70 RSI.lt.30
# 2015-08-20 198.101 199.809 195.597 195.6645 194327900 203.97 34.89513 0 0
# 2015-08-21 193.516 195.636 189.477 189.7745 346588500 197.83 25.55762 0 0
# 2015-08-24 179.856 189.439 174.973 181.7837 507244300 189.50 18.37412 0 0
# 2015-08-25 187.472 187.491 179.309 179.6445 369833100 187.27 16.99684 0 0
# 2015-08-26 184.259 186.858 180.700 186.5417 339257000 194.46 34.13877 0 0
# 2015-08-27 188.997 191.300 187.261 191.1558 274143900 199.27 42.66887 0 0
# 2015-08-28 190.417 191.703 189.861 191.1654 160414400 199.28 42.68550 0 0
# 2015-08-31 190.043 191.022 188.988 189.6210 163298800 197.67 40.64265 0 0
您可以看到 RSI 在 2015-08-21
下穿 30,在下一个交易日 2014-08-24
发出进场信号,在 2014-08-24
的收盘价进场181.7837。所以结果看起来很合理。
这个例子虽然有点长,但希望对您有所帮助。
有人知道可以用于回测以优化阈值水平而不是参数输入的 R 包吗?
例如说只想在 ADX(14) > 30 时使用趋势信号进行交易。quantstrat
允许使用 apply.paramset
而不是参数阈值 (30) 来优化参数输入 (14)。
如果这样的包不存在,也许有人可以提供指向要查看哪些包以开始破解这样的任务的指示?
您实际上可以在 quantstrat 中优化信号的阈值水平!事实上,几乎所有指标、信号甚至订单(对于规则)的参数都可以优化。有关优化停止阈值级别的示例,请参阅 quantstrat 演示文件夹。
下面是一个完全可重现的示例,探讨了当 RSI 低于某个 RSI 阈值水平时购买 SPY
的最佳阈值(30 是默认值,并探索我们是否将其更改为 c(20, 25, 30, 35, 60)
).如果 RSI 从下方超过 70,则该策略涉及退出头寸(如果我们继续从下方越过 RSI 阈值,则需要金字塔形 3 个级别)。
library(quantstrat)
strategy.st <- "RSI"
stratRSI <- strategy(strategy.st, store = TRUE)
add.indicator(strategy = strategy.st, name = "RSI", arguments = list(price = quote(getPrice(mktdata))), label="RSI")
add.signal(strategy = strategy.st, name="sigThreshold",arguments = list(threshold=70, column="RSI",relationship="gt", cross=TRUE),label="RSI.gt.70")
add.signal(strategy = strategy.st, name="sigThreshold",arguments = list(threshold=30, column="RSI",relationship="lt",cross=TRUE),label="RSI.lt.30")
add.rule(strategy = strategy.st, name='ruleSignal', arguments = list(sigcol="RSI.lt.30", sigval=TRUE, orderqty= 1000, ordertype='market', orderside='long', pricemethod='market', replace=FALSE, osFUN=osMaxPos), type='enter', path.dep=TRUE)
add.rule(strategy = strategy.st, name='ruleSignal', arguments = list(sigcol="RSI.gt.70", sigval=TRUE, orderqty='all', ordertype='market', orderside='long', pricemethod='market', replace=FALSE), type='exit', path.dep=TRUE)
currency("USD")
symbols = c("SPY")
for(symbol in symbols){
stock(symbol, currency="USD",multiplier=1)
}
getSymbols(symbols, src='yahoo', index.class=c("POSIXt","POSIXct"), from='2015-01-01')
startDate='2005-12-31'
initEq=100000
port.st<-'RSI'
initPortf(port.st, symbols=symbols)
initAcct(port.st, portfolios=port.st, initEq=initEq)
initOrders(portfolio=port.st)
for(symbol in symbols){ addPosLimit(port.st, symbol, startDate, 300, 3 ) } #set max pos
# If you want to run the "base" simulation with standard RSI threshold parameter above of 30:
# out<-try(applyStrategy(strategy=strategy.st , portfolios=port.st, parameters=list(n=2) ) )
#updatePortf(Portfolio=port.st,Dates=paste('::',as.Date(Sys.time()),sep=''))
# Do optimisation on RSI threshold:
add.distribution(strategy.st,
paramset.label = 'testin',
component.type = 'signal',
component.label = 'RSI.lt.30', #this is the label given to the indicator in the strat
variable = list(threshold = c(20, 25, 30, 35, 60) ),
label = 'RSIEntryThreshold'
)
results <- apply.paramset(strategy.st,
paramset.label='testin',
portfolio.st=port.st,
account.st=port.st,
# nsamples=.nsamples,
verbose=TRUE)
stats <- results$tradeStats
# > stats
# RSIEntryThreshold Portfolio Symbol Num.Txns Num.Trades Net.Trading.PL Avg.Trade.PL Med.Trade.PL Largest.Winner Largest.Loser Gross.Profits Gross.Losses Std.Dev.Trade.PL Percent.Positive Percent.Negative Profit.Factor Avg.Win.Trade
# 1 20 RSI.1 SPY 2 1 2319.885 2319.885 2319.885 2319.885 0 2319.885 0 NA 100 0 NA 2319.885
# 2 25 RSI.2 SPY 2 1 2319.885 2319.885 2319.885 2319.885 0 2319.885 0 NA 100 0 NA 2319.885
# 3 30 RSI.3 SPY 8 3 8035.114 2678.371 2105.968 4754.004 0 8035.114 0 1856.8247 100 0 NA 2678.371
# 4 35 RSI.4 SPY 12 3 13614.221 4538.074 4990.450 5153.644 0 13614.221 0 928.4609 100 0 NA 4538.074
# 5 60 RSI.5 SPY 24 6 10832.882 1727.480 1239.326 4971.405 0 10364.882 0 1739.6692 100 0 NA 1727.480
# Med.Win.Trade Avg.Losing.Trade Med.Losing.Trade Avg.Daily.PL Med.Daily.PL Std.Dev.Daily.PL Ann.Sharpe Max.Drawdown Profit.To.Max.Draw Avg.WinLoss.Ratio Med.WinLoss.Ratio Max.Equity Min.Equity End.Equity
# 1 2319.885 NaN NA 2319.885 2319.885 NA NA -1073.679 2.160689 NA NA 2381.600 0.0000 2319.885
# 2 2319.885 NaN NA 2319.885 2319.885 NA NA -1073.679 2.160689 NA NA 2381.600 0.0000 2319.885
# 3 2105.968 NaN NA 2678.371 2105.968 1856.8247 22.89814 -3160.282 2.542531 NA NA 8137.556 -213.9176 8035.114
# 4 4990.450 NaN NA 4538.074 4990.450 928.4609 77.59044 -2147.357 6.339989 NA NA 13921.550 -1464.3554 13614.221
# 5 1239.326 NaN NA 1727.480 1239.326 1739.6692 15.76328 -7426.328 1.458713 NA NA 10832.882 -6419.0599 10832.882
好的,所以结果表明,自 2015 年以来,如果阈值为 35,您就可以做得很好。
现在,检查模拟结果的合理性是个好主意,所以这里有一些您可以做的快速检查(作为激励您为自己的策略做类似事情的例子):
# -------------------------------------------------------------------------------------------------------------------------------------------------
# Do a check on the reasonableness of the results:
# This is the result from entering a trade when RSI crosses above 30 from below:
# We can quickly plot RSI neatly, using the default n = 14 RSI value as a check:
chart_Series(mktdata[, 1:4])
add_RSI(n = 14)
# A safer way to chart the results, using the actual signal data in the `mktdata` object that the simulation ran (we can be confident here that the plain vanilla `RSI(n = 14)` function above using `add_RSI` would be ok too, and quicker, which is why I show it above (which recomputes the signal)):
chart_Series(mktdata[, 1:4])
add_TA(mktdata[, "EMA.RSI"])
add_TA(xts(x = rep(20, NROW(mktdata)), order.by = index(mktdata)), on = 2, col = "blue", lty = 2)
add_TA(xts(x = rep(25, NROW(mktdata)), order.by = index(mktdata)), on = 2, col = "red", lty = 2)
add_TA(xts(x = rep(30, NROW(mktdata)), order.by = index(mktdata)), on = 2, col = "purple", lty = 2)
现在快速检查其中一个模拟发生的交易 --RSI.3
此处对应于 30 的进入阈值 -- 并将它们与实际数据进行比较 and/or 上图。 (您可以在 results$RSI.1$portfolio$symbols$SPY$txn
中看到第一次模拟的结果(RSI 低于 20),在 results$RSI.2$portfolio$symbols$SPY$txn
中看到 RSI 低于 25 的结果,等等)
txns1 <- results$RSI.3$portfolio$symbols$SPY$txn
indexTZ(txns1) <- "UTC" # avoid timezone related issue by keeping all in UTC time
# > txns1
# Txn.Qty Txn.Price Txn.Value Txn.Avg.Cost Pos.Qty Pos.Avg.Cost Gross.Txn.Realized.PL Txn.Fees Net.Txn.Realized.PL Con.Mult
# 1950-01-01 05:00:00 0 0.0000 0.00 0.0000 0 0.0000 0.000 0 0.000 0
# 2015-08-24 00:00:00 100 181.7837 18178.37 181.7837 100 181.7837 0.000 0 0.000 1
# 2015-11-04 00:00:00 -100 202.8433 -20284.33 202.8433 0 0.0000 2105.968 0 2105.968 1
# 2016-01-11 00:00:00 100 186.3479 18634.79 186.3479 100 186.3479 0.000 0 0.000 1
# 2016-01-14 00:00:00 100 186.1733 18617.33 186.1733 200 186.2606 0.000 0 0.000 1
# 2016-01-21 00:00:00 100 181.0905 18109.05 181.0905 300 184.5373 0.000 0 0.000 1
# 2016-03-31 00:00:00 -300 200.3839 -60115.18 200.3839 0 0.0000 4754.004 0 4754.004 1
# 2016-11-04 00:00:00 100 205.4281 20542.81 205.4281 100 205.4281 0.000 0 0.000 1
# 2016-11-28 00:00:00 -100 217.1796 -21717.96 217.1796 0 0.0000 1175.142 0 1175.142 1
# > mktdata["2015-08-20/2015-08"]
# SPY.Open SPY.High SPY.Low SPY.Close SPY.Volume SPY.Adjusted EMA.RSI RSI.gt.70 RSI.lt.30
# 2015-08-20 198.101 199.809 195.597 195.6645 194327900 203.97 34.89513 0 0
# 2015-08-21 193.516 195.636 189.477 189.7745 346588500 197.83 25.55762 0 0
# 2015-08-24 179.856 189.439 174.973 181.7837 507244300 189.50 18.37412 0 0
# 2015-08-25 187.472 187.491 179.309 179.6445 369833100 187.27 16.99684 0 0
# 2015-08-26 184.259 186.858 180.700 186.5417 339257000 194.46 34.13877 0 0
# 2015-08-27 188.997 191.300 187.261 191.1558 274143900 199.27 42.66887 0 0
# 2015-08-28 190.417 191.703 189.861 191.1654 160414400 199.28 42.68550 0 0
# 2015-08-31 190.043 191.022 188.988 189.6210 163298800 197.67 40.64265 0 0
您可以看到 RSI 在 2015-08-21
下穿 30,在下一个交易日 2014-08-24
发出进场信号,在 2014-08-24
的收盘价进场181.7837。所以结果看起来很合理。
这个例子虽然有点长,但希望对您有所帮助。