0%

一个基于价值的screen(筛选器) -- 大概是最简单的量化投资

本文设计了一个能长期跑赢沪深300指数的screen。并通过回测观察了该screen在不同市场环境下的表现。对基于价值的股票投资有一定的指导意义。

量化的第一步通常是确定投资标的池,面对几千只股票,通过初步的筛选,哪些是符合标准并进入池子中。这步工作不太可能人工完成,因为数量实在太大。那么,接下来一个问题的我该如何设计一个screening?这就涉及到你的投资风格问题了,通常来说,有如下这些风格:

  • 价值股投资;格雷厄姆,巴菲特都是祖师爷。
  • 成长股投资;贵有贵的道理。。。
  • 趋势投资;顺势而为。
  • 逆向投资;人弃我取。
  • 跟投;别人投啥我投啥,比如跟踪外资策略。
  • 。。。

我比较认同价值投资,因此就基于价值投资的理念,设计一个screening。顺便扯一下题外话,买P/E是100倍的股票未必就不是价值投资。价值投资的本质上还是看买入的价格是否低于内在价值,言归正传。既然目标是做一个价值投资的screen,那就来明确一下该目标背后的含义。我希望通过该screen,找到被错误定价的公司,即被低估的公司,价格便宜,但没有明显的负面因素造成这种低估,例如高风险,低增长,或低质量的增长(需要很高的资本投入等)。换句话说,我希望找到一组股票,在低价格乘数(multiples)区间交易(e.g.低P/E),公司风险低,有高的增长,并且增长的质量好。这么写出来是不是觉得在做梦啊?!Too good to be true!这就对了,凭啥赚钱那么容易?!你都觉得不可能了说明市场其实在大部分时间还是有效的!但是,虽然觉得概率低,试着找找看这样的股票也没啥坏处,指不定就真在地上找到了皮夹子。

Screen 维度

接下来我就从下面几个维度开始设计screening:

  • 价格
  • 风险
  • 成长性
  • 成长质量

Screen for price

衡量价格一般用价格乘数,相关指标有很多,最常用的P/E,P/B。需要指出一个大原则,如下图所示:

即分子与分母从财务角度必须一致,如果分子是equity value,那分母也必须是equity value。同样,如果分子是enterprise value,那分母也必须是enterprise value。举例来说,P/E 是满足一致性要求的,P(price)是equity value,E(earning, net income)也是equity value。

由于不同行业有其自身的特征,比如有些行业杠杆率高,为了实现跨行业的可比性,我使用enterprise value 乘数: EV / EBIT。其中:

  • EV = enterprise value = market value of equity + interest-bearing debt - cash
  • EBIT,息税前利润。

有了价格乘数,那么接下来一个问题是:如何算是便宜?可以有两种方式:

  • 绝对,设定一个具体的EV / EBIT 标准,超过了就算贵。
  • 相对,对所有股票EV / EBIT 进行排序,设定一个百分比,比如top 33% 算贵。

我倾向使用相对方式进行screening。因为,格雷厄姆时代的标准放到今天可能就找不到股票了。

Screen for risk

Risk 可以分为:

  • Operating risk,主要是从公司经营角度出发,影响营收或公司正常用作的风险
  • Financial risk, 从财务角度出发,例如债务
  • Liquidity risk,主要从股票持有人变现角度出发,如买卖spread,公司停牌之类

对于设计Screen来说,一般比较容易做的是:

  • 基于价格:如股票的beta,或是standard deviation for returns。
  • 基于会计:根据财务报表上的数据进行分析,例如公司偿债能力。

从债务杠杆出发,选用book debt-to-equity ratio作为风险度量。当然,并非说债务高就一定不好。但是,“这个世界上没有无缘无故的狠”,便宜的股票通常在财务上面临压力,因此,在此前提下,债务越低越安全。之所以没有使用market value来计算debt-to-equity ratio是因为market value of equity其实是price的一种度量,而EV / EBIT已经度量了价格。

除了考虑债务杠杆的绝对值,在加入债务杠杆的变化:

Change in book D/E = Current year’s D/E - prior year’s D/E

收入容易被操控,因此是一个需要小心的地方。如果net income大于cash flow from operatinos,那么就有可能是一个不好的信号。使用以下指标:

ACCRUAL = (NI-CFO)/TA

其中:

  • NI = net income before extraordinary items
  • CFO = cash flow from operations
  • TA = total assets

Screen for growth

对于增长我没有太多好的想法,使用一致性预期应该是一个不错的选择,但是,并非每只股票都有一致性预期,尤其是不受待见的问题股票。因此,我就简单用Revenue growth了:

Revenue growth = Current year’s Revenue / Prior years’s Revenue - 1

这里,我还想加入一个技术指标用来表示增长:Momentum,使用3个月的股票价格的return:

M3M = 3-Month price change

Screen for quality of growth

我希望入围的公司不光有增长,而且增长的质量比较高。比如,不用靠很高的资本支出。对于这个Screen,我使用return on invested capital (ROIC)。

ROIC = EBIT(1-t) / (Book value of equity + book value of debt - cash)

进一步,不光考虑ROIC的绝对水平,还进一步考虑ROIC的变化:

Change in ROIC = Current year’s ROIC - prior year’s ROIC

总结一下,我用了一下这些条件对股票进行过滤:

  • Price
    • EV / EBIT
  • Risk
    • book debt-to-equity ratio
    • Change in book D/E
    • ACCRUAL = (NI-CFO)/TA
  • Growth
    • Revenue growth
    • M3M = 3-Month price change
  • Quality
    • ROIC
    • Change in ROIC

一共8个过滤条件。

两种不同的过滤方式

如何使用这8个条件进行筛选并获得目标股票池?一种方式是顺序过滤。根据重要性将条件排序。比如,我觉得股票是否便宜是最重要的,那么就将EV / EBIT排在第一位,用这个条件将所有股票进行排序,然后选择top 50%,然后接下来再用次重要的条件对入围的股票再进行排序,依次完成所有的条件。但是,这么做有几个问题:

  • 如果某两个或多个条件具有相同的重要性,你必须给定一个顺序,这样造成的结果就是会过滤掉你希望保留的股票。
  • 最后可能没有满足条件的股票,或结果集的数量太少了。

为了解决以上问题,可以使用多条件并行过滤(simultaneous screening)的方法:

  • 首先将每个条件的值进行标准化处理 — Z-Score
  • 将各个条件的z-score值按照一定的权重加总
  • 根据加总后值进行排序,获得过滤结果

这里需要注意的地方是:

  • 对条件值进行标准化处理可以有多种方法,我选用z-score。这步是必须的,否则不同的条件值之间的数量级会有很大的差别。汇总后,那些值很大的条件就会对结果产生更大的影响
  • 不同条件z-score的方向应该有相同的影响。比如,z-score大,代表有正面影响,反之则有负面影响

模型实现

到了这步就要拿出我码农的本色!开始撸数据,写代码。目标就是计算出每只股票在对应条件上的z-score。其实,最大的挑战还是数据,包括获取数据,清理数据以及处理outlier。具体细节没必要放在这里(此处省略240行代码。。。)。我主要做以下工作:

  • 工具方面,基本就是python了,利用numpy,pandas来存储加工数据
  • 数据利用的是ricequant平台,不得不说,他们在数据的二次加工上做的挺完善,基本上你想要的股票基本面数据他们都提供了。这就极大简化了数据处理的工作,至于数据是否准确,还需要观察一段时间
  • 扫描全部A股。确保股票至少上市3年才进入我的筛选范围
  • 对于EBIT < 0的股票不考虑,虽说EBIT < 0不代表这支股票不好,但是,由于我这里的策略还是基于value,而EBIT < 0 的股票有可能是成长型公司,或是公司经营出了问题。另外,如果EBIT < 0,那么EV / EBIT就变得没有意义
  • 对于outlier,我的定义是z-score > 3 或 z-score < -3,发现outlier,我并不是简单做删除处理,而是将outlier设置成3或-3
  • 剔除了所有的金融股票,包括银行和非银,原因很简单,你很难定义什么是他们的debt,就更不用说interesting bearing debt了,那该怎么计算他们的debt to equity ratio呢?

下图所示是数据处理完的结果:

上半部分cleaned_df是所有股票的条件值的计算结果,下半部分则是将其转化成了z-score。至此z-score都已计算完成。

z-score权重

有了各个条件上z-core,那么接下来我需要加总求和。如何求和?也就是每个条件上z-score对应的权重是多少?这个权重该如何确定?

最简单的方法:等权重求和了!虽说是简单粗暴,但是,等权重也是有一个明显的好处,那就避免了overfitting!因此是一个不错的benchmark,以后对z-score权重的优化都可以同这个等权重benchmark做比较。再强调一下,每个条件上的z-score在求和的时候必须保证同方向,因此求和操作应如下:

SUM of Z-Score =

  • minus EV / EBIT
  • minus book debt-to-equity ratio
  • minus Change in book D/E
  • minus (NI-CFO)/TA
  • plus Revenue growth
  • plus M3M
  • plus ROIC
  • plus Change in ROIC

下表显示了排名前50的股票:

label z_score_sum symbol industry_code industry_name
300677.XSHE 10.716210 英科医疗 C29 橡胶和塑料制品业
002869.XSHE 7.475962 金溢科技 C39 计算机、通信和其他电子设备制造业
600421.XSHG 7.123312 ST仰帆 C34 通用设备制造业
002164.XSHE 7.034439 宁波东力 C35 专用设备制造业
601020.XSHG 6.866158 华钰矿业 B09 有色金属矿采选业
002838.XSHE 6.495545 道恩股份 C29 橡胶和塑料制品业
300552.XSHE 6.002087 万集科技 I65 软件和信息技术服务业
002124.XSHE 5.956577 天邦股份 C13 农副食品加工业
603606.XSHG 5.861706 东方电缆 C38 电气机械和器材制造业
002289.XSHE 5.485774 ST宇顺 C39 计算机、通信和其他电子设备制造业
000048.XSHE 5.417372 京基智农 C13 农副食品加工业
002286.XSHE 5.285237 保龄宝 C13 农副食品加工业
300464.XSHE 4.960576 星徽精密 F52 零售业
603129.XSHG 4.847436 春风动力 C37 铁路、船舶、航空航天和其他运输设备制造业
300056.XSHE 4.837722 中创环保 C35 专用设备制造业
600652.XSHG 4.782898 ST游久 I64 互联网和相关服务
002216.XSHE 4.725449 三全食品 C14 食品制造业
300246.XSHE 4.653786 宝莱特 C35 专用设备制造业
002714.XSHE 4.440177 牧原股份 A03 畜牧业
600132.XSHG 4.402648 重庆啤酒 C15 酒、饮料和精制茶制造业
002030.XSHE 4.378498 达安基因 C27 医药制造业
300274.XSHE 4.367707 阳光电源 C38 电气机械和器材制造业
002555.XSHE 4.361072 三七互娱 I64 互联网和相关服务
002677.XSHE 4.335123 浙江美大 C38 电气机械和器材制造业
300103.XSHE 4.327513 达刚控股 N77 生态保护和环境治理业
002312.XSHE 4.320156 三泰控股 C26 化学原料及化学制品制造业
601633.XSHG 4.311244 长城汽车 C36 汽车制造业
300225.XSHE 4.307500 金力泰 C26 化学原料及化学制品制造业
300676.XSHE 4.304628 华大基因 M74 专业技术服务业
603416.XSHG 4.267281 信捷电气 C40 仪器仪表制造业
603338.XSHG 4.231131 浙江鼎力 C35 专用设备制造业
300435.XSHE 4.172462 中泰股份 D45 燃气生产和供应业
603906.XSHG 4.095128 龙蟠科技 C26 化学原料及化学制品制造业
002285.XSHE 4.074116 世联行 K70 房地产业
300411.XSHE 4.064889 金盾股份 C34 通用设备制造业
000913.XSHE 4.055015 钱江摩托 C37 铁路、船舶、航空航天和其他运输设备制造业
002735.XSHE 4.049436 王子新材 C29 橡胶和塑料制品业
603877.XSHG 3.998039 太平鸟 C18 纺织服装、服饰业
601216.XSHG 3.992290 君正集团 C26 化学原料及化学制品制造业
002706.XSHE 3.991975 良信电器 C38 电气机械和器材制造业
603787.XSHG 3.990077 新日股份 C37 铁路、船舶、航空航天和其他运输设备制造业
000897.XSHE 3.988275 津滨发展 K70 房地产业
601012.XSHG 3.951803 隆基股份 C30 非金属矿物制品业
603960.XSHG 3.926110 克来机电 C35 专用设备制造业
002730.XSHE 3.869764 电光科技 C35 专用设备制造业
002569.XSHE 3.842822 ST步森 C18 纺织服装、服饰业
601069.XSHG 3.841323 西部黄金 B09 有色金属矿采选业
002568.XSHE 3.825067 百润股份 C15 酒、饮料和精制茶制造业
300405.XSHE 3.803350 科隆股份 C26 化学原料及化学制品制造业
002833.XSHE 3.783292 弘亚数控 C35 专用设备制造业

新鲜出炉啊~~~这是根据2020年Q3财报产生的结果。

回测

回测其实更为繁琐,借助RQAlpha框架,真的是极大的降低了工作量(此处又省略了296行代码。。。)。在本篇中,我只用最简单粗暴的方式进行回测,不做任何的优化。说一下我在回测过程中做了哪些处理:

  • 在构建portfolio时,直接用aggregate z-score为top 50的股票;也就是说没有太多考虑分散投资,在下一篇,我会根据行业构建一个相对分散投资的portfolio。
  • 使用等权重资金投资到每个股票,也就是说,有50只股票,将资金等分成50份,每份投资一只股票。如果以后做优化的话,可以考虑risk parity方法,或根据每只股票的预期收益,做MVO。我觉得sharpe ratio应该是可以提高不少。
  • 在回测的过程中考虑forward looking bias。由于国内财报的披露时间为:
    • 1季报:每年4月1日——4月30日。
    • 2季报(中报):每年7月1日——8月31日。
    • 3季报: 每年10月1日——10月31日
      因此我在回测的过程中,定义获取财报的时间分别为:
    • 1季报:4月30日以后的第一个交易日。
    • 2季报(中报):8月31日以后的第一个交易日。
    • 3季报: 10月31日以后的第一个交易日。
  • 每年做3次rebalance,分别对应以上三个财报季。rebalance方式也是简单粗暴,第一步全部卖出,第二步再以等权重方式买入新入围的50只股票。因此将来,这步也是有很多的优化可以做。
  • 考虑了survival bias,比如,我从2015年6月12日开始回测,那我会以当时股票市场的全部股票作为我的pool,并以此进行screening(而非今天市场上的全部股票)。而当时的股票到今天可能已经退市了!因此用这种方式进行回测能避免survival bias。

下面上结果,我先用2年时长进行回测(2018-9-25到2020-9-25):

  • 基准是沪深300指数
  • 深红色是screen的净值曲线,蓝色是基准,黄色是超额收益
  • 2年的周期最后是跑赢了基准!但是,有超过1年多的时间是落后基准,这里想表达的意思是,使用这个screen(包括其他策略)需要有一个相对长期的预期,不能期望时时刻刻都能跑赢基准
  • 整体来说和沪深300的相关性很高,beta=0.9。screen的收益是46.66%,基准的收益是33.734%。作为未经任何优化的screen能取得这样的结果还是有点出乎我的意料

然后,我想看看2015年股灾后的表现,用了一个很极端的场景:从2015-6-12开始回测,也就是从股灾的顶点开始,一直到2020-9-25。我想看看,这个策略在股灾巨幅下跌时的表现,以及需要多久能重新回到原点。如下图所示:

  • 首先这段回测超过5年。从刚开始那段暴跌看,似乎screen也没有表现出抗跌的特点,但有意思的是,在其后的反弹过程中大幅跑赢了基准
  • 最终的结果看screen不仅收复了所有的下跌,还获得了7.288%的收益,而沪深300还亏损了13.88%!其实不能觉得5年多才7.288%,要知道这是很极端的情况,也就是你在泡沫的顶点开始投资!因此对screen的表现也觉得挺不错!
  • 还有一点比较有意思,就是沪深300代表的大盘股从2016年2月开始稳步上涨一直到2018年1月,而screen在这段时间一直没有表现,甚至在行情结束时被反超。因此,要坚持策略也需要有个强大的内心!
  • 这里beta只有0.8,我觉得是一个好现象,长期来看同沪深300的相关性变低了

聊一聊下一步打算,我会对这个screen做进一步的优化:

  • 在计算aggregate z-score时,不是简单粗暴的使用等权重
  • 在构建portfolio时,不是简单的选择aggregate z-score为top n的那些股票,而是考虑如何做好分散。因为排在前面的股票可能会集中在某几个行业,这样,你的portfolio就太集中了
  • 对screen做一些风格方面的调整,比如,我就想追求绝对便宜,那么可以试一下先根据 EV / EBIT 剔除掉最贵的50%的股票,然后在余下的50%里面做aggregate z-score
  • rebalance时是否需要将股票全部换掉?可以考虑每次换出aggregate z-score最低的几只股票,并补充新的高分股票等等。。。

这些测试我将放在下一篇。感觉本篇内容足够干货了! 等以上优化完成以后,我打算在雪球上以此建一个portfolio,作为benchmark长期跟踪,并能分享给有兴趣的朋友。

总结

最后,想谈谈使用各种类型的screen是否能击败市场获取超额收益。从经验上看是存在这个可能的,比如本文的screen,还有如Joel Greenblatt的神奇公式(更简单)长期也能跑赢SP500。但是,光是这样的跑赢是否足够,我们是否可以要求的更多?我比较认同Damodaran的观点,他认为作为一个投资者,你必须得有一些独特的东西(核心竞争力)才能在这个市场上获得超额收益。换句话说,就好像我们看公司会看它是不是有护城河,那么作为个人投资者,你需要考虑你的竞争优势在哪里。当格雷厄姆在1951年使用screen时,screen可以看作是一种竞争优势,因为:

  • 首先,要做一个screen在当时并不容易。那个时代,要获取大量公司的财报,并且还得有自动化的工具来分析数据,对于普通投资者来说是一件相当难的事情,只有极少数实力强悍的投资者才具备这样的能力。
  • 其次,是纪律性,投资者必须让自己远离各种诱惑和潮流,并始终如一的关注screen产生的股票。
  • 第三,是耐心,投资者必须长期持有screen产生的股票才能获得理想的收益(这同本文的回测结果是相符的)

今天,人们有各种途径获得股票数据,还有无数的分析工具。因此第一种优势几乎不存在了,(这里我想多说一句,在这一点上我不太认同,个人感觉,第一种优势在当今可能还是发挥着比较关键的作用,因为高质量的数据通常不会免费,所以仅此一点,就挡住了绝大部分的散户。其次是技术的使用,有多少投资人懂得python,甚至能熟练使用excel?)那么就剩下纪律和耐心作为你潜在的竞争优势。可以说,自动化的screening以及投资流程可以做到不需要人工介入,因此,这样一套系统从受到情绪化影响的角度来看,即使同最有纪律性且耐心的人来比,也明显胜出一大截!更直白的说,作为一个主动投资者,如果说所有你能拿得出手的东西仅仅是screen,那你基本不太可能打败机器。除了用screen武装自己外,你是否能赚到钱还取决于你在screen之后所做工作的质量。如果你在分析股票内在价值或定性研究方面有所专长,你也许能获取更高超额收益。

参考文献:

欢迎关注我的其它发布渠道