这个例子展示了如何对在交易策略中包含投资信号的投资组合策略进行回溯测试。这个词信号包括策略作者需要在资产历史价格之外做出交易决策的任何信息。这些信息可以包括技术指标、机器学习模型的输出、情绪数据、宏观经济数据等。这个例子使用了三种基于衍生信号数据的简单投资策略:
均线交叉
移动平均线收敛/发散
相对强度指数
在本例中,您可以使用这些策略对一年的股票数据进行回溯测试。然后分析结果,比较每种策略的性能。
虽然技术指标通常不用作独立的交易策略,但本示例使用这些策略来演示如何在使用信号数据时基于信号数据构建投资策略backtestEngine
对象在MATLAB®。
加载2006年15只股票调整后的价格数据。为了提高可读性,本示例使用了一小部分可投资资产。
阅读2006年道琼斯工业平均指数股票每日调整收盘价的表格。
T =可读的(“dowPortfolio.xlsx”);
为了可读性,只使用30只大疆成分股中的15只。
符号= [“AA”,“猫”,“说”,“通用汽车”,“hp”,“公司”,“力”,“嗯”,“莫”,“期望”,“微软”,“工业”,“PG”,“T”,“XOM”];
修剪表格,只保留日期和选定的股票。
timeColumn =“日期”;T = T(:,[timeColumn符号]);
将数据转换为时间表。
pricesTT = table2时间表(T,“RowTimes”,“日期”);
查看价格时间表的结构。
头(pricesTT)
ans =8×15时间表日期AA猫说通用hp JNJ MCD嗯莫merck microsoft pfizer PG T XOM ___________ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ _____ 03 - 1月- 2006 28.72 55.86 24.18 17.82 28.35 59.08 32.72 75.93 52.27 30.73 26.19 22.16 56.38 22.7 56.64 04 -简- 2006 28.89 57.29 23.77 18.3 29.18 59.99 33.01 75.54 52.65 31.08 26.32 22.88 56.48 22.87 56.74 05 - 1月- 2006 29.12 57.29 24.19 19.34 28.97 59.74 33.05 74.85 52.52 31.13 26.34 22.9 56.3 22.92 56.45 06 - 1月- 2006 29.02 58.43 24.52 19.6129.8 60.01 33.25 75.47 52.95 31.08 26.26 23.16 56.24 23.21 57.57 09-Jan-2006 29.37 59.49 24.78 21.12 30.17 60.38 33.88 75.84 53.11 31.58 26.21 23.16 56.67 23.3 57.54 10-Jan-2006 28.44 59.25 25.09 20.79 30.33 60.49 33.91 75.37 53.04 31.27 26.35 22.77 56.45 23.16 57.99 11-Jan-2006 28.05 59.28 25.33 20.61 30.88 59.91 34.5 75.22 53.31 31.39 26.63 23.06 56.65 23.34 58.38 12-Jan-2006 27.68 60.13 25.41 19.76 30.57 59.63 33.96 74.57 53.23 31.41 26.48 22.9 56.02 23.24 57.77
可视化数据集中每个股票的相关性和总回报。
可视化15只股票之间的相关性。returns = tick2ret(pricesTT);stockCorr = corr(returns.Variables);热图(符号、符号、stockCorr“Colormap”, parula);
在价格数据范围内可视化每只股票的表现。。totalRet = ret2tick(返回);情节(totalRet.Dates totalRet.Variables);传奇(符号,“位置”,“西北”);标题(“每股增长1美元”) ylabel (“美元”)
获取数据集持续时间内每只股票的总回报率。totalRet(最终,:)
ans =1×15时间表日期AA猫说通用hp JNJ MCD嗯莫merck microsoft pfizer PG T XOM ___________ ______ ______ ______ ______ ______ ______ ______ ______ ______ ______ ______ ______ ______ ______ _____ 29日- 12月- 2006 1.0254 1.0781 1.4173 1.6852 1.4451 1.0965 1.3548 1.0087 1.1946 1.3856 1.1287 1.1304 1.1164 1.5181 1.336
除了历史调整的资产价格,回溯测试框架还允许您选择指定信号运行回测时的数据。使用MATLAB®以与价格类似的方式指定信号数据时间表
.的“时间”维度信号时间表必须与价格的时间表——也就是说,每个表的行必须具有匹配的datetime值时间
列。
这个例子构建了一个信号时间表来支持这三种投资策略:金宝app
简单移动平均交叉(SMA)策略
移动平均收敛/发散(MACD)策略
相对强度指数(RSI)策略
每种策略都有一个预先计算好的信号时间表。在运行回溯测试之前,您将三个单独的信号时间表合并为一个聚合信号时间表,以用于回溯测试。
平均线指标使用5天和20天简单移动平均线来做出买入和卖出的决定。当5天移动均线穿过20天移动均线(向上移动)时,股票被买入。当5天移动均线跌破20天移动均线时,股票被卖出。
使用movavg函数创建SMA时间表。sma5 = movavg(pricesTT,“简单”5);sma20 = movavg(价格,“简单”, 20);
创建SMA指标信号时间表。
smaSignalNameEnding =“_SMA5over20”;smassignal =时间表;为I = 1:数字(符号)symi =符号(I);为每个符号建立一个时间表,然后将它们聚合在一起。。smaSignali =时间表(价格。日期,...Double (sma5.(symi) > sma20.(symi)),...“VariableNames”{sprintf (' % s % s '、symi smaSignalNameEnding)});使用同步功能合并时间表。smaSignal = synchronize(smaSignal,smaSignali);结束
SMA信号时间表包含值为的指示器1
当5天移动平均线高于每项资产的20天移动平均线时,a0
否则。每个股票指标的列名为[股票代码]SMA5over20
.的backtestStrategy
对象根据这些交叉事件做出交易决策。
查看SMA信号时间表的结构。
头(smaSignal)
ans =8×15时间表时间AA_SMA5over20 CAT_SMA5over20 DIS_SMA5over20 GM_SMA5over20 HPQ_SMA5over20 JNJ_SMA5over20 MCD_SMA5over20 MMM_SMA5over20 MO_SMA5over20 MRK_SMA5over20 MSFT_SMA5over20 PFE_SMA5over20 PG_SMA5over20 T_SMA5over20 XOM_SMA5over20 ___________ _____________ ______________ ______________ _____________ ______________ ______________ ______________ ______________ _____________ ______________ _______________ ______________ _____________ ____________ ______________ 03 - 1月- 2006 0 0 0 0 0 0 0 0 0 0 0 0 0 0 004 -简- 2006 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 05 - 1月- 2006 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 06 - 1月- 2006 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 09 - 1月- 2006年1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 10 - 1月- 2006年1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 11 - 1月- 2006 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 12 - 1月- 2006 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1
绘制单个资产的信号以预览交易频率。
情节(smaSignal.Time smaSignal.CAT_SMA5over20);ylim ([-0.5, 1.5]);ylabel (' sma 5 > sma 20');标题(sprintf (CAT的SMA 5 / 20));
你可以以多种方式使用MACD指标。通常,MACD与它自己的指数移动平均线进行比较,但在这个例子中,当MACD高于MACD时,MACD作为买入信号的触发器0
.当MACD指标跌回下方时卖出0
.
使用MACD函数创建MACD指标的时间表。macdTT = macd(价格);
创建MACD指标信号时间表。
macdSignalNameEnding =“_MACD”;macdSignal =时间表;为I = 1:数字(符号)symi =符号(I);为每个符号建立一个时间表,然后将这些符号聚合在一起。。macdSignali =时间表(pricesTT。日期,...double(macdTT.(symi) > 0),...“VariableNames”{sprintf (' % s % s '、symi macdSignalNameEnding)});macdSignal =同步(macdSignal,macdSignali);结束
MACD信号表包含名称为[的每个资产的列股票代码]MACD
.每个信号的值为1
当股票的MACD值高于时0
.信号的值为0
当股票的MACD跌破时0
.
头(macdSignal)
ans =8×15时间表时间AA_MACD CAT_MACD DIS_MACD GM_MACD HPQ_MACD JNJ_MACD MCD_MACD MMM_MACD MO_MACD MRK_MACD MSFT_MACD PFE_MACD PG_MACD T_MACD XOM_MACD ___________ _______ ________ ________ _______ ________ ________ ________ ________ _______ ________ _________ ________ _______ ______ ________ 03 - 1月- 2006 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 04 - 1月- 2006 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 05 - 1月- 2006 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 06 - 1月- 2006 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 09 - 1月- 2006 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 - 1月- 2006 0000000000 0 0 0 0 0 11-Jan-2006 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12-Jan-2006 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
与SMA相似,绘制单个资产的信号以预览交易频率。
plot(macdSignal.Time,macdSignal.CAT_MACD) ylim([-0.5, 1.5]);ylabel (' macd > 0');标题(sprintf ('MACD > 0为CAT'));
RSI是捕捉动量的指标。一个常见的启发是在RSI跌破时买入30.
当RSI高于时卖出70
.
rsiSignalNameEnding =“_RSI”;rssignal =时间表;为I = 1:数字(符号)symi =符号(I);rsiValues = rsindex(pricesTT.(symi));rsiBuySell = 0(大小(rsiValues));rsiBuySell(rsiValues < 30) = 1;rsiBuySell(rsiValues > 70) = -1;为每个符号建立一个时间表,然后将这些符号聚合在一起。。rsiSignali =时间表(价格。日期,...rsiBuySell,...“VariableNames”{sprintf (' % s % s '、symi rsiSignalNameEnding)});rsissignal =同步(rsissignal,rsiSignali);结束
RSI信号的值为1
(表示买入信号)当股票的相对强弱指数低于时30.
.信号的值为-1
(表示卖出信号)当股票的相对强弱指数高于时70
.否则,信号的值为0
,表示没有行动。
绘制单个资产的信号以预览交易频率。
plot(rssignal . time, rssignal . cat_rsi) ylim([-1.5, 1.5]);ylabel (“RSI买入/卖出信号”);标题(sprintf (CAT的RSI买入/卖出信号));
建立策略backtestStrategy
对象中定义的rebalance函数本地函数部分。每种策略都使用再平衡功能,根据适当的信号做出交易决策。
这些信号需要足够的跟踪数据来计算交易信号(例如,计算SMA20
为一天X需要前20天的价格X).所有跟踪数据都被捕捉在预先计算的交易信号中。因此,实际的策略只需要2天的回溯窗口来做出交易决策,以评估信号何时超过交易阈值。
所有策略在买卖时支付25个基点的交易成本。
初始权重是根据20个交易日后的信号值计算的。回测在这20天初始化期之后开始。
交易成本= 0.0025;对两个SMA使用crosoverrebalancefunction%策略以及MACD策略。这是因为他们都在交易在各自的信号上以同样的方式购买(当信号从% 0->1,当信号从1->0时卖出)。建立一个匿名的策略的rebalance函数%共享crosoverrebalancefcn()与适当的信号名称字符串%为每个策略。每个匿名函数取当前的权重(w),价格(p),%和信号数据从回测引擎,并将其传递给% crosoverrebalancefcn函数与信号名称字符串。smaInitWeights = computeInitialWeights(smassignal (20,:));smaRebalanceFcn = @(w,p,s) crossoverRebalanceFcn(w,p,s,smaSignalNameEnding);smaStrategy = backtestStrategy(SMA的smaRebalanceFcn,...“TransactionCosts”tradingCosts,...“LookbackWindow”2,...“InitialWeights”, smaInitWeights);macdInitWeights = computeInitialWeights(macdSignal(20,:));macdRebalanceFcn = @(w,p,s) crosoverrebalancefcn (w,p,s,macdSignalNameEnding);macdStrategy = backtestStrategy(MACD的macdRebalanceFcn,...“TransactionCosts”tradingCosts,...“LookbackWindow”2,...“InitialWeights”, macdInitWeights);RSI策略以不同的方式使用其信号,在0->1买入%的过渡和出售在一个0->-1过渡。这个逻辑在%本地函数部分定义的rsiRebalanceFcn函数。rsiInitWeights = computeInitialWeights(rssignal (20,:));rsiStrategy = backtestStrategy(“肢体重复性劳损症”@rsiRebalanceFcn,...“TransactionCosts”tradingCosts,...“LookbackWindow”2,...“InitialWeights”, rsiInitWeights);
作为基准,这个例子还运行了一个简单的等权重策略,以确定交易信号是否为资产的未来回报提供了有价值的见解。基准策略每四周重新平衡一次。
相等权重策略不需要历史记录,因此将LookbackWindow设置为0。基准策略= backtestStrategy(“基准”@equalWeightFcn,...“TransactionCosts”tradingCosts,...“RebalanceFrequency”, 20岁,...“LookbackWindow”, 0);
将每个单独的信号时间表聚合为一个回测信号时间表。
结合三个信号时间表。signalTT =时间表;signalTT =同步(signalTT, smassignal);signalTT =同步(signalTT, macdSignal);signalTT =同步(signalTT, rsissignal);
使用backtestEngine
创建回测引擎,然后使用runBacktest
运行回测。未投资现金的无风险收益率是1%。
将基准策略和三个信号策略放入一个数组中。策略= [benchmarkStrategy smaStrategy macdStrategy rsiStrategy];创建回测引擎。bt = backtestEngine(策略,“RiskFreeRate”, 0.01)
bt = backtestEngine with properties: Strategies: [1x4 backtestStrategy] RiskFreeRate: 0.0100 CashBorrowRate: 0 rate convention: "Annualized" Basis: 0 InitialPortfolioValue: 10000 NumAssets:[]收益:[]头寸:[]周转率:[]BuyCost: [] SellCost: []
从初始权重计算热身期结束开始。startIdx = 20;%运行回测。bt = runBacktest(bt,pricesTT,signalTT,“开始”, startIdx);
使用equityCurve
绘制策略权益曲线,以可视化他们在回溯测试中的表现。
equityCurve (bt)
如前所述,这些策略通常不作为独立的交易信号使用。事实上,这三种策略在2006年的表现都不如简单的基准策略。您可以使用每日资产头寸的面积图来可视化策略配置如何随时间变化。要做到这一点,请使用assetAreaPlot
属性中定义的Helper函数本地函数部分。
strategyName =“基准”;strategyName assetAreaPlot (bt)
2006年下半年,整个股票市场经历了一个非常看涨的6个月,但这三种策略都未能完全抓住增长的机会,因为它们留下了太多的现金资本。虽然这些策略本身都没有很好的表现,但这个例子演示了如何构建基于信号的交易策略,并对其进行回溯测试以评估其性能。
初始权重计算函数和策略再平衡函数紧随其后。
函数initial_weights = computeInitialWeights(信号)根据最近的信号计算初始权重。nAssets = size(signals,2);Final_signal =信号{结束,:};购买= final_signal == 1;initial_weights = 0 (1,nAssets);initial_weights(买)= 1 / nAssets;结束
函数new_weights = crosoverrebalancefcn (current_weights, pricesTT, signalTT, signalNameEnding)信号交叉再平衡功能。构建与交叉信号对应的信号名称单元格数组。symbols = pricesTT.Properties.VariableNames;cellfun(@(s) sprintf(' % s % s '年代,signalNameEnding),符号,“UniformOutput”、假);抽取策略相关的信号数据。%crossoverSignals = signalTT(:,signalNames);从我们当前的权重开始。New_weights = current_weights;%在信号转为0时卖出任何现有多头头寸。idx = crossoverSignals{end,:} == 0;New_weights (idx) = 0;找到新的交叉点(信号从0变为1)。idx = crossoverSignals{end,:} == 1 & crossoverSignals{end-1,:} == 0;%下注规模,将可用资本分配到所有剩余资产,然后只投资于新的积极交叉资产。。这就剩下一些%成比例的未投资金额,用于未来的投资%零权重资产。availableCapital = 1 - sum(new_weights);uninstedassets = sum(new_weights == 0);new_weights(idx) = availableCapital / uninstedassets;结束
函数new_weights = rsiRebalanceFcn(current_weights, pricesTT, signalTT)%买卖1和-1的平衡函数。signalNameEnding =“_RSI”;构建与交叉信号对应的信号名称单元格数组。symbols = pricesTT.Properties.VariableNames;cellfun(@(s) sprintf(' % s % s '年代,signalNameEnding),符号,“UniformOutput”、假);抽取策略相关的信号数据。%buySellSignals = signalTT(:,signalNames);从当前的权重开始。New_weights = current_weights;卖出任何信号变为-1的多头头寸。。idx = buySellSignals{end,:} == -1;New_weights (idx) = 0;找到新的买入点(信号为1,权重为0)。idx = new_weights == 0 & buySellSignals{end,:} == 1;%下注规模,将可用资本分配到所有剩余资产,然后只投资于新的积极交叉资产。。这就剩下一些%成比例的未投资金额,用于未来的投资%零权重资产。availableCapital = 1 - sum(new_weights);uninstedassets = sum(new_weights == 0);new_weights(idx) = availableCapital / uninstedassets;结束
函数new_weights = equalWeightFcn(current_weights,~)%等加权投资组合配置。nAssets = numel(current_weights);new_weights = ones(1,nAssets);New_weights = New_weights / sum(New_weights);结束
函数strategyName assetAreaPlot (val)将资产分配绘制为面积图。t = backtest . positions .(strategyName). time;position = backtest . positions .(strategyName). variables;H =面积(t,位置);标题(sprintf (“% s的立场”strrep (strategyName“_”,' ')));包含(“日期”);ylabel (“资产头寸”);datetick (“x”,“mm / dd”,“keepticks”);Xlim ([t(1) t(end)]) oldylim = ylim;ylim ([0 oldylim (2)]);Cm = parula(数字(h));为I = 1: number (h) set(h(I),“FaceColor”厘米(我:));结束传奇(backtester.Positions (strategyName) .Properties.VariableNames)。结束
backtestStrategy
|backtestEngine
|runBacktest
|总结