网站建设需要些什么资料,品牌建设 凝心,资源采集网站如何做,微信app下载官网在第三章6到9节#xff0c;我们学习和实践了大部分股票软件指标#xff0c;且这些指标是backtrader内置指标实践中没有讲到过的。然后#xff0c;在进行策略综合之前#xff0c;我们先热个身#xff0c;把一些可能比较有参考意义的股票软件内置指标在backtrader里给实现了…在第三章6到9节我们学习和实践了大部分股票软件指标且这些指标是backtrader内置指标实践中没有讲到过的。然后在进行策略综合之前我们先热个身把一些可能比较有参考意义的股票软件内置指标在backtrader里给实现了。同时也开始考虑每个指标的特点以及应该使用什么样的综合策略。
目前backtrader只接收开、高、低、收以及成交量这五个数据因此一些跟涨跌家数相关的大势型指标一些与流通股本相关的参数等都无法在backtrader中简单的实现那些我们就先忽略了。
01_超买超卖型
CC_01_MFI指标与CJ03_VRSI指标
MFI指标
MFI指标是基于RSI指标在成交量上的应用所以是否可以直接继承RSI的指标并在类中添加成交量的计算这个我们还必须进到RSI的源码中去查看。
class RelativeStrengthIndex(Indicator):alias (RSI, RSI_SMMA, RSI_Wilder,)lines (rsi,)params ((period, 14),(movav, MovAv.Smoothed),(safediv, False),)def __init__(self):upday UpDay(self.data, period1)downday DownDay(self.data, period1)maup self.p.movav(upday, periodself.p.period)madown self.p.movav(downday, periodself.p.period)if not self.p.safediv:rs maup / madownself.lines.rsi 100.0 - 100.0 / (1.0 rs)class UpDay(Indicator):lines (upday,)params ((period, 1),)def __init__(self):self.lines.upday Max(self.data - self.data(-self.p.period), 0.0)class DownDay(Indicator):lines (downday,)params ((period, 1),)def __init__(self):self.lines.downday Max(self.data(-self.p.period) - self.data, 0.0)
根据源码首先计算UpDay和DownDay是判断收盘价与昨收价若今收高于昨收(上涨)则把差值记录到upday中否则把下跌的差值记录到downday中。这里出现了一个问题就是默认写死了为self.data也就是默认是收盘价所以我们不能直接把upday和downday用在TYP的值计算里也就不能直接继承它来计算MFI。
这里说明一下我们从AI里获取的MFI的指标在backtrader中代码可能是有错误的这时在next中打印MFI的值会出现全是NaN的情况遇到这样的问题需要自己想办法分析和寻找方法解决。
class MFI2(Indicator):lines (mfi2, ) # 定义一个输出线命名为mfiparams ((period,14),)def __init__(self):self.tp (self.data.high self.data.low self.data.close) / 3.0 # 计算典型价格self.rmf self.tp * self.data.volume # 计算资金流量Raw Money Flowupday bt.And(Max(self.tp - self.tp(-1),0),1) # 正向因子dnday bt.And(Max(self.tp(-1)-self.tp,0),1) # 负向因子# 由于这里不需要C-REF(C,1)的差值只需要记录10即可# 这里使用bt.And()函数将最大值与上1,即如果最大值不为0就会输出1self.pmf self.rmf*upday # 计算正向和负向资金流量self.nmf self.rmf*dndayposmfi bt.indicators.SumN(self.pmf,period14) #使用indicators.SumN()进行累加negmfi bt.indicators.SumN(self.nmf,periodself.p.period) self.mfr posmfi / negmfi # 计算资金流动比率Money Flow Ratioself.lines.mfi2 100 -100 / (1 self.mfr) # 计算MFI值
这里我们用到两个函数来解决过程中的2个问题第1个问题是RSI要计算正向差值和负向差值而MFI只需要计算是正向还是负向然后乘上VOL * TYP的值因此用了bt.And()函数将正向的都放在upday中作为正向因子来解决它当然我们也可以用bt.If()函数来进行。第2个问题是股票软件公式用的SUM(C,N)的函数其实在backtrader中的indicators\basicops.py中有SumN的函数完全对应的。期间AI助手给出的例如cumcum()是无法正常使用的。 将MFI2的自定义指标放到最简单的策略类中进行构建和运行绘图由于还没有添加买卖策略只有主图和副图绘制显示这里我们还添加了RSI的曲线与MFI进行比较当然这里是为了确认我们的自定义指标输出的数量级基本是正确的没有发生过程中的错误。
我们看到MFI与RSI的走势会比较相似这一点在第6节实践股票软件中的MFI指标时已经确认过
量化交易backtrader实践(三)_指标与策略篇(6)_股票软件指标参考A VRSI指标
在解决了MFI的指标问题后我们再来看一下VRSI指标从指标公式上看VRSI才是严格的成交量在RSI指标上的应用它与RSI的不同仅仅是从C-REF(C,1)变成了VOL-REF(VOL,1)。
但是由于这个改变是在RSI指标过程中的改变而并非输入或输出的再加工所以我们仍然不能直接继承RSI指标来使用仍然需要自定义指标
class VRSI_test(Indicator):lines (vrsi,) # 定义一个输出线命名为vrsiparams ((period,6),)plotinfo dict(plotymargin0.05, plotyhlines[0, 100])def __init__(self):Vupday Max(self.data.volume - self.data.volume(-1),0.0)Vdnday Max(self.data.volume(-1) - self.data.volume,0.0)#Vmaup bt.indicators.SMA(Vupday, periodself.p.period)Vmadn bt.indicators.SMA(Vdnday, periodself.p.period)#rs Vmaup/Vmadnself.lines.vrsi 100.0-100.0/(1.0rs)super(VRSI_test, self).__init__()
不过在自定义指标时可以大部分借鉴RSI的代码于是我们只需要把C-REF(C,1)改为V-REF(V,1)即可。从代码上可以很清楚的了解到VRSI这个指标只跟成交量的变化有关量增为正量减为负周期内的量增平均值/量减平均值即量的相对强弱指标数值越低表示缩量连续且缩量差值越大数值越高则表示放量连续且放量数值越大。
再回过头来看MFIMFI是TYP的值乘上VOL的值它相当于典型股价的成交额它即有了量的因素又乘上了价的因素属于量价合一的指标。而VRSI只有量的因素RSI只有价的因素。不过反而单纯的RSI与VRSI才能组成一组比较严格的量价关系指标组合。 CC_02_BIAS指标
Backtrader中并没有内置的BIAS指标。BIAS指标即乖离率指标是一个超买超卖指标其含义是收盘价与某一周期均线的乖离率算法很简单close-ma/ma。如果需要在Backtrader中使用BIAS指标需要自定义实现。
class myBIAS(Indicator):lines (bias,bima) # 定义输出线params ((p1,20),(p2,6))plotinfo dict(plotymargin0.05, )def __init__(self):ma1 bt.indicators.SMA(self.data.close, periodself.p.p1)bias (self.data.close - ma1)/ma1 * 100.0self.lines.bias biasself.lines.bima bt.indicators.SMA(bias, period self.p.p2)super(myBIAS, self).__init__() 02_均线型与成交量型
JX02_ACD升降线指标
在第三章第7节中讨论过ACD升降线指标公式ACD即Accumulation Distribution Line所谓的累积分布线。这个公式的特点是根据当前收盘价是否大于昨收(即当天上涨还是下跌)来选择用TrueHigh(Max(HIGH, REF(C,1)))还是用TrueLow(Min(LOW,REF(C,1)))从实际的情况下看上涨取最低值下跌取最高值。然后用收盘价CLOSE去减就得到所谓的多空力量或买卖盘力量。
M:20;
LC:REF(CLOSE,1);
DIF:CLOSE-IF(CLOSELC,MIN(LOW,LC),MAX(HIGH,LC));
ACD:SUM(IF(CLOSELC,0,DIF),0);
MAACD:EXPMEMA(ACD,M);
前面也讨论过由于ACD的数值没有做归一化它在不同股价时取值范围是不同的所以单是ACD没有定量的策略仅仅可以支持双均线的金叉/死叉策略。不过粗略地看ACD的稳健性与KAMA差不多所以这里我们就把ACD的指标做到backtrader中来进行回测检验一下。
from backtrader.indicators import Min, Max
class myACD(Indicator):lines (acd,maacd) # 定义输出线params ((p1,20),)plotinfo dict(plotymargin0.05,)# plotyhlines[-30, 30])def __init__(self):lcself.data.close(-1)vmin Min(self.data.low, lc)vmax Max(self.data.high, lc)upday Max(self.data.close - vmin, 0.0)dnday Min(self.data.close - vmax, 0.0)cd upday dndayself.lines.acd bt.indicators.CumulativeSum(cd)self.lines.maacd bt.indicators.EMA(self.lines.acd, periodself.p.p1)super(myACD, self).__init__()
这里有一个问题要注意一下单独使用Max()好像不会报错但只要使用Min()的时候就会报错 NameError: name Min is not defined 由于Max和Min都是backtrader的内置函数需要从bt.indicators模块中导入。
另外__init__中的每一次赋值都是相当于给整列数据赋值所以可以利用这一点分别给正值项做一列、负值项做一列最后两列相加即可。
从backtrader的ACD图线与股票软件的进行比较大致的曲线是近似的但取值范围稍微有些不一样暂时不知道不完全相同的原因在哪但并不影响我们的进度。 在这里添加简单的交叉策略进行回测结果与我们之前的预估差不多大部分的股票ACD交叉策略的确与KAMA类似但也有少数差别很多的例如西部、蓝色等。 如果仔细的进入单支股票的回测进行观察就不难发现有些股票在震荡段时ACD与MAACD之间一直是纠缠在一起两者并没有出现明显的分离情况那么在这一段里并没有进入交易的必要(椭圆圈内)只有当ACD与MAACD有了明显的分离时才需要进行交易(见右侧3个黄箭头)。 但是对于原生的ACD而言是没有办法对不同股价的票进行区分的它在股票软件里图线显示是人进行观察而得出的结论人为根据经验觉得某一段ACD两线没有分开则可以不交易。
所以要正常的使用这个指标就需要再进一步对原始指标进行一些更改比如归一化使其能用于各个股票再比如计算两条线的分离程度当大于某一值的时候才满足买入条件等。到这里我还是懒惰了只想赶紧进行下一条这些进阶的内容就先搁置在这里什么时候有兴趣了再折腾吧。 JX08_BBIBOLL指标
在第三章第7节的JX03中我们讨论了BBI多空均线指标当时我们查到的资料是BBIBull and Bear Index如果以这样的名字命名的话按理这就是我们国家的硬翻译了。但后来在BBIBOLL的介绍中又发现了一种解释BBI Breakout Bands Indicator中文叫“突破带指标”。到底哪一个是对的呢不过争论名称并没有什么意义我们需要知道它怎么用才行。
在前面的章节里我们研究过BBI的指标的计算逻辑与UDL引力线指标是一模一样的只是4个参数周期的取值不同而已。并且我们发现UDL可以近似的使用EMA9来替代但BBI与EMA11仍有小部分的差距在里面。典型的BBI用法是收盘价站上BBI买入下破BBI卖出并且BBI本身有支撑和阻力的作用(触及不破反向)这样的策略也可以放在UDL引力线上应用也就相当于直接可以用EMA9来做单均线策略这些都是几个指标之间的替代和近似关系暂时就讨论这么多还是回到BBIBOLL的应用上来。
BBIBOLL多空布林线指标是一种结合了多空线BBI和布林线BOLL的技术分析工具它用BBI做了中轨这就与标准BOLL线的中轨使用简单移动平均线MA会有一些差别。 根据上图主图为BBIBOLL的指标副图1是标准BOLL指标副图2是计算了两者的极限带宽度的曲线。可以看到首先中轨BBI会比MA要更贴近于K线的运动它不会明显的远离K线。其次求标准差的时候BOLL的标准差是STD(C,20)即求20周期内收盘价的标准差而BBIBOLL的标准差为STD(BBIBOLL,11)为什么使用11这个是不是跟我们前面拟合的BBI大约是EMA11有关也不能确定但似乎还真有那么一点关系所以BBIBOLL的周期11比BOLL的周期20要短它的上下轨的变化就更明显。然后标准布林是中轨加减2倍的标准差BBIBOLL是中轨加减6倍的标准差这也就决定了BBIBOLL的上下轨的幅度要选大于标准BOLL带。这个也可以在副图2的两者极限宽度看出来桃红的BBIBOLL宽度大部分都在标准BOLL带宽度之上。从这一点来说BBIBOLL的上下轨并不适合用来做买卖点反而是上下轨的距离(带宽度)是用来做辅助判断的。
对于BBIBOLL指标中轨是BBI它是不会变的可以更改的2个参数都是对于上下轨的幅度和宽度的调节通过调节参数我们发现当默认11的参数减少则STD(BBI,N)的标准差样本数减少上下轨的变化速度更快了。而要让STD(BBI,N)的值变小就是其标准差(与BBI的偏差)变小就意味着BBI本身是斜率变缓或者走平。此时我们再来理解一下这几句引文 高价区收盘价跌破BBI线并且上下轨相距非常远时为卖出信号。 低价区收盘价突破BBI线并且上下轨相距非常近时为买入信号。 轨道收敛说明行情即将变盘向上或向下突破。 轨道发散表明将向上或向下扩大趋势。 上下轨相距远则代表BBI斜率大也就是这段时间股价单边幅度大于是跌破BBI代表当前为下跌趋势是卖出止损的最后机会轨道收敛则代表BBI斜率变小或走平代表股价进入一个阻力区那么突破BBI向上则相当于突破阻力区开启一波行情可以认为是买点。
可以尝试把BBIBOLL的N周期由11减小(比如6)并综合考虑BOLL的策略进行综合例如BOLL可以在下轨买入但如果发现BOLL的WID以及WIDBBI都在增大时代表下跌行情没有完成则不能买入需要等到WID低于0.15并且WIDBBI拐头下跌时BOLL下轨才可以买入。又例如经典的布林策略可以添加突破中轨的策略把它跟收盘站上BBI结合起来作为一个分支策略的买点。但要注意一个问题BBIBOLL收口即WIDBBI非常小的时候站上BBI上涨的可能性比较大但也会遇到站下BBI只是一个陷阱之后股价疯狂下跌的情况。
关于BBIBOLLBOLL的策略研究后续再重点讨论这里先研究到这里我们先把这个指标在backtrader中实现了。BBIBOLL是主图指标需要加上subplotFalse的。
class myBBIBOLL(Indicator):lines (bbi,upr,dwn) # 定义输出线params ((p1,11),(p2,6))plotinfo dict(plotymargin0.05, )plotinfo dict(subplotFalse) def __init__(self):cv self.data.closema1 bt.indicators.SMA(cv,period3)ma2 bt.indicators.SMA(cv,period6)ma3 bt.indicators.SMA(cv,period12)ma4 bt.indicators.SMA(cv,period24)bbi (ma1ma2ma3ma4)/4.0std1 bt.indicators.StdDev(bbi,period self.p.p1)mstd1 self.p.p2 * std1self.l.upr bbimstd1self.l.dwn bbi-mstd1self.l.bbi bbisuper(myBBIBOLL, self).__init__()
另外我们再制作一个BBIBOLL线宽度的指标由于这个指标是放副图的还需要把上面的代码复制一下但把plotinfo dict(subplotTrue) 写出来。完成后在策略中同时加载BBIBOLL和BBIBOLLWID两个指标就可以分别在主图和副图上显示了。 CJ02_OBV能量潮指标
能量潮OBV是典型的成交量指标它只与成交量相关当上涨时取成交量正值下跌时取成交量负值然后进行累加。这是最简洁但又非常暴力的分析指标。
M:30;
VA:IF(CLOSEREF(CLOSE,1),VOL,-VOL);
OBV:SUM(IF(CLOSEREF(CLOSE,1),0,VA),0);
MAOBV:MA(OBV,M);
在backtrader中没有内置的OBV指标前面我们在review内置指标时的确也很少看到与成交量相关的指标这里需要把指标公式移植过来。这里我们使用bt.If()函数来得到成交量正负的line的值。
class myOBV(Indicator):lines (obv,maobv,) # 定义输出线params ((p1,30),)plotinfo dict(plotymargin0.05, )def __init__(self):cv self.data.close - self.data.close(-1)va bt.If(cv0, self.data.volume, -1*self.data.volume)obv bt.indicators.CumulativeSum(va)maobv bt.indicators.SMA(obv,periodself.p.p1)self.lines.obv obvself.lines.maobv maobvsuper(myOBV, self).__init__() 以上就把OBV的指标在backtrader中实现了。 03_趋势型指标
趋势型指标中有几个是与成交量相关的这些指标再叠加上多空力量差值或比值来显示上涨的力量或趋势。
QS02_CHO佳庆指标
在OBV中以今收昨收为判断条件只要是上涨的成交量取正值(多头动能)只要是下跌的成交量取负值(空头动能)。
对于佳庆指标则使用收盘价减最低价表示多方力量而使用最高价减收盘价表示空方力量。
N1:10;
N2:20;
M:6;MID:SUM(VOL*(2*CLOSE-HIGH-LOW)/(HIGHLOW),0);
CHO:MA(MID,N1)-MA(MID,N2);
MACHO:MA(CHO,M);移植到backtrader中的指标为
class myCHO(Indicator):lines (cho,macho,) # 定义输出线params ((p1,10),(p2,20),(p3,6),)def __init__(self):power1 2*self.data.close - self.data.high - self.data.lowvpower self.data.volume * power1 / (self.data.high self.data.low)mid bt.indicators.CumulativeSum(vpower)ma1 bt.indicators.SMA(mid, periodself.p.p1)ma2 bt.indicators.SMA(mid, periodself.p.p2)cho ma1 - ma2macho bt.indicators.SMA(cho, periodself.p.p3)self.lines.cho choself.lines.macho machosuper(myCHO, self).__init__() QS05_EMV简易波动指标
EMV也是将价格和成交量的变化结合而成的指标这里的成交量会使用平均值除以成交量而价格的趋势采用的(HL)/2的平均价格减去上一个周期的平均价格然后除以平均价格得到了百分比所以EMV对于所有的股票其数量级都是一致的这一点比OBV以及CHO都要通用性强一些。
N:14;
M:9;VOLUME:MA(VOL,N)/VOL;
MID:100*(HIGHLOW-REF(HIGHLOW,1))/(HIGHLOW);
EMV:MA(MID*VOLUME*(HIGH-LOW)/MA(HIGH-LOW,N),N);
MAEMV:MA(EMV,M);
这里我们就直接在backtrader中实现这个指标
class myEMV(Indicator):lines (emv,maemv,) # 定义输出线params ((p1,14),(p2,9),)def __init__(self):vol1 bt.indicators.SMA(self.data.volume,periodself.p.p1)/self.data.volumemid1 self.data.highself.data.lowmid 100* (mid1 - mid1(-1))/ mid1ma2 bt.indicators.SMA(self.data.high-self.data.low, periodself.p.p1)emv1 bt.indicators.SMA(mid*vol1*(self.data.high-self.data.low)/ma2, periodself.p.p1)maemv bt.indicators.SMA(emv1, periodself.p.p2)self.lines.emv emv1self.lines.maemv maemvsuper(myEMV, self).__init__()class EMV_test_1(BaseSt):params ((stra_name,EMV_test_1),(p1,14), (tradeCnt,1),(sucessCnt,0),) def __init__(self): self.order Noneemv myEMV(self.data)self.crs bt.indicators.CrossOver(emv.l.emv, emv.l.maemv)def next(self):if self.order: # 检查是否有指令等待执行returnif not self.position: # 没有持仓 才会进入 if self.crs0: self.order self.buy() # 执行买入else:if self.crs0: self.order self.sell() # 执行卖出 run_main_plot_ex2 (df_list,EMV_test_1,0)----------------------
策略为 EMV_test_1 , 期末总资金 105796.95 盈利为 5796.95 总共交易次数为 36 ,交易成功率为 36.1% QS07_VPT量价曲线指标
跟上面的CHOEMV差不多VPT也是基于量和价关系的技术分析指标价格的关系也很简单就是日涨跌幅涨为正跌为负然后乘上成交量并做N周期的累加这个其实比前面两个更简单因为CHO是计算的多方力量的差值而EMV计算的是平均价格的涨跌。
N:51;
M:6;VPT:SUM(VOL*(CLOSE-REF(CLOSE,1))/REF(CLOSE,1),N);
MAVPT:MA(VPT,M);
这里就直接在backtrader中把它实现。
class myVPT(Indicator):lines (vpt,mavpt,) # 定义输出线params ((p1,51),(p2,6),)def __init__(self):cpct (self.data.close - self.data.close(-1))/self.data.close(-1)power1 cpct*self.data.volumevpt bt.indicators.SumN(power1, periodself.p.p1)mavpt bt.indicators.SMA(vpt, periodself.p.p2)self.lines.vpt vptself.lines.mavpt mavptsuper(myVPT, self).__init__()class VPT_test_1(BaseSt):略run_main_plot_ex2 (df_list,VPT_test_1,0)----------------------------
策略为 VPT_test_1 , 期末总资金 98249.04 盈利为 -1750.96 总共交易次数为 47 ,交易成功率为 34.0% QS08_WVAD威廉变异离散量指标
WVAD也是计算了成交量与价格两个部分在价格方面使用的是收盘价减开盘价除以当天的振幅的计算方式对应的是阳线为正阴线为负再乘上成交量就会将成交量变为有正有负的数值这种思路跟上面的VPT是类似的只是用到的价格计算的因子不一样。
N:24;
M:6;WVAD:SUM((CLOSE-OPEN)/(HIGH-LOW)*VOL,N)/10000;
MAWVAD:MA(WVAD,M);
仍然是直接在backtrader中把它实现出来。
class myWVAD(Indicator):lines (wvad,mawvad,) # 定义输出线params ((p1,24),(p2,6),)def __init__(self):cpct (self.data.close - self.data.open)/(self.data.high-self.data.low)power1 cpct*self.data.volumewvad bt.indicators.SumN(power1, periodself.p.p1)/10000.0mawvad bt.indicators.SMA(wvad, periodself.p.p2)self.lines.wvad wvadself.lines.mawvad mawvadsuper(myWVAD, self).__init__()class WVAD_test_1(BaseSt):params ((stra_name,WVAD_test_1),(p1,24), (tradeCnt,1),(sucessCnt,0),) def __init__(self): self.order Nonewvad myWVAD(self.data)self.crs bt.indicators.CrossOver(wvad.l.wvad, wvad.l.mawvad)def next(self):if self.order: # 检查是否有指令等待执行returnif not self.position: # 没有持仓 才会进入 if self.crs0: self.order self.buy() # 执行买入else:if self.crs0: self.order self.sell() # 执行卖出 run_main_plot_ex2 (df_list,WVAD_test_1,0)----------------------------
策略为 WVAD_test_1 , 期末总资金 106770.76 盈利为 6770.76 总共交易次数为 53 ,交易成功率为 45.3% 04_小结
把股票软件的指标移植到backtrader中来一般采用2种方式。第一种会很简单是直接在backtrader内置指标的基础上进行的小更改那么我们可以直接继承这个指标的类然后再进行简单的计算就可以了。第二种就是通过自定义指标类的方式进行其实上面的几个指标都是采用这种方式进行的。
在自定义指标类中对于指标的计算的逻辑实现也非常的自由一般而言直接可以在__init__()中就把指标创建好如果的确有困难也可以在next中来逐项实现。在__init__()中每一个参与计算的都是一列(line)数据相当于pandas表中的列。
在股票软件中一些常见的函数其实在backtrader中也几乎都有对应的函数
股票软件backtraderIF( condition, s1, s2) bt.IF(condition, S1,S2) bt.And(Max(C,0),1) SUM(C, N) bt.indicators.SumN(self.data, periodN)SUM(C, 0) 从头开始累加bt.indicators.CumulativeSum(self.data)MA(C,5) bt.indicators.SMA(self.data,period5)
只要我们清楚了这些函数就会发现把股票软件中已经存在的指标公式移植过来并不是一件困难的事。
不过backtrader默认接收的数据项只有开、高、低、收和成交量而前面我们基本上也只从akshare或tushare.pro中获取这几个数据其他的数据项被忽略的。但无论如何股票软件本身可以获取的数据项是非常多的比较WINNER函数获取某价格的获利比例等另外比较典型的是涨跌家数比等如果我们想在backtrader中使用这些数据那就需要特殊的操作了这个在后面的章节里再详细讨论。
经过到目前为止的这些实践我们已经了解了许多backtrader内置的指标股票软件内置的指标以及能够把一些股票软件内置的指标移植到backtrader中来进行回测并且相应的根据backtrader的交易系统又在股票软件的指标上添加了更高级的功能比如计算某个策略的收益率比如对于连续的多个买点和卖点给予屏蔽等。在当前的基础上我们对指标的认识有了较大的提高接下来我们就尝试着进行复杂、综合策略的实践了。