掘金社区

基于EV/EBITDA倍数估值法的Alpha对冲策略Pinned highlighted

掘金小Q 【 论坛管理员 】 发表在策略研究 2018-03-24 10:58:14

策略研究
1114
0
0

Alpha对冲策略简介

什么是Alpha对冲策略?

​ 投资者在市场交易中面临着系统性风险(即Beta风险)和非系统性风险(即Alpha风险),通过对系统性风险进行度量并将其分离,从而获取超额绝对收益(即阿尔法收益)的策略组合,即为阿尔法策略。从广义上讲,获取阿尔法收益的投资策略有很多种,其中既包括传统的基本面分析选股策略、估值策略、固定收益策略等等,也包括利用衍生工具对冲掉贝塔风险、获取阿尔法收益的可转移阿尔法策略。后者在国内通常被称为阿尔法对冲策略,并在近年A股市场上得到广泛应用。

Alpha对冲策略是如何构建的?

​ 阿尔法策略所涉及的市场领域非常广泛,在股市、债市、商品市场等各类市场都有应用。而目前国内市场上最常见的还是股市阿尔法对冲策略,其通常利用选股、择时等方面优势,寻找具有稳定超额收益的现货组合,通过股指期货等衍生工具来分离贝塔,进而获得与市场相关度较低的阿尔法收益。尤其是在熊市或者盘整期,可以采用“现货多头+期货空头的方法,一方面建立能够获取超额收益的投资组合的多头头寸,另一方面建立股指期货的空头头寸以对冲现货组合的系统风险,从而获取正的绝对收益。此外,还有机构根据获取阿尔法的途径,采取统计套利、事件驱动、高频交易等策略来获取阿尔法收益。而在上述各种策略构建过程中,基于大类资产配置、行业配置、择时与选股体系的量化策略均得到了广泛应用。


Alpha对冲策略示意图.png

Alpha对冲策略成功的关键是什么?

Alpha策略成败的两个关键要素是:

  • 现货组合的超额收益空间有多大;
  • 交易成本的高低。

    ​两者相抵的结果,才是Alpha策略可获得的利润空间。在股市Alpha策略中,最考验策略制定者水平的因素在于选股方法和能力。

EV/EBITDA倍数估值法简介

什么是EV?

EV(EnterpriseValue),表示企业价值,是衡量公司价值的一种方式,经常以直接的公司市值的替代形
式出现。
EV的计算公式为:$EV=公司市值+ 负债+ 少数股东权益+ 优先股- 现金以及现金等价物$

​ 人们经常将EV看作是接管的理论价格。在并购活动中,收购方必须承担被收购方的负债,这提高了并购的成本,但与此同时,被收购公司的现金以及现金等价物在收购之后将直接落入收购者的口袋,这对收购者来说等同于减少了收购成本。EV和直接的公司市值在很多方面都有差别,因此有很多人把它作为公司价值更精确的代表。比如,当收购方收购其它公司时,它要同时承担对方公司的债务,因此EV提供了一个更为准确的收购估值,因为在EV的估值计算当中加入了债务。

什么是EBITDA?

EBITDA(Earnings Before Interest, Taxes, Depreciation and Amortization)即是未计利息、税
项、折旧及摊销前的利润。

EBITDA的计算公式为:$EBITDA=税前净利润+利息+折旧+摊销$

​ EBITDA最早是在20世纪80年代中期使用杠杆收购的投资机构在对那些需要再融资的账面亏损企业进行评估时,被大量使用。他们通过计算EBITDA来快速检查公司是否有能力来偿还这笔融资的利息。

​ 玩杠杆收购的那些投资银行家们推广了EBITDA的使用,他们通过EBITDA来检测某家公司是否有能力偿还短期(1-2年)贷款。至少,从理论上讲,EBITDA利息覆盖率(EBITDA除以财务费用)可以让投资者知道这家公司在再融资之后究竟是否有足够利润支付利息费用。

什么是EV/EBITDA倍数估值法,具有什么样的特点?

EV/EBITDA倍数估值法又称企业价值倍数,是一种被广泛使用的公司估值指标。

公式为:$EV/EBITDA倍数=EV÷EBITDA$

​ 投资应用: EV/EBITDA和市盈率(PE)等相对估值法指标的用法一样,其倍数相对于行业平均水平或历史水平较高通常说明高估,较低说明低估。此外,不同行业或板块有不同的估值(倍数)
水平。

​ 几乎所有的投资者都知道PE,了解EV/EBITDA倍数的人也在增多。但真正能准确理解其背后含义、现
实中的变形并能熟练使用的投资者可能还不多。其实这是一个在估值实践中与PE并驾齐驱的估值方法,由
于能有效的弥补PE的一些不足,它在国外的运用程度甚至比PE更为普遍。EV/EBITDA倍数和PE同属于可比法,在使用的方法和原则上大同小异,只是选取的指标口径有所不同。

应用

​ 在具体运用中, EV/EBITDA倍数法和PE 法的使用前提一样,都要求企业预测的未来收益水平必须能够体现企业未来的收益流量和风险状况的主要特征。这体现于可比公司选择的各项假设和具体要求上, 缺失了这些前提, 该方法同样也就失去了合理估值的功能。相比而言,由于指标选取角度的不同,EV/EBITDA倍数弥补了PE 倍数的一些不足,使用的范围也更为广泛。

优点

​ 相比于将所有因素都综合在一起的净利润指标, EBITDA 剔除了诸如财务杠杆使用状况、折旧政策变化、长期投资水平等非营运因素的影响, 更为纯粹,因而也更为清晰地展现了企业真正的运营绩效。这有利于投资者排除各种干扰, 更为准确地把握企业核心业务的经营状况。同时,从指标对企业价值的反映程度上来说, 由于剔除了上述因素的影响,企业单一年度的EBITDA指标与企业未来收益和风险的相关性更高,换句话说,影响企业单一年度EBITDA水平的因素和影响企业未来所有年度EBITDA水平的因素更为一致,而影响企业单一年度净利润的因素则相对复杂和多变。也正因为如此, 作为一种以可比为基础的估值方法, EV/EBITDA倍数法的合理性相对于PE也就更强。

缺点

​ 首先,和PE 比较起来, EV/EBITDA方法要稍微复杂一些,至少还要对债权的价值以及长期投资的价值进行单独估计。
​ 其次, EBITDA中没有考虑到税收因素,因此,如果两个公司之间的税收政策差异很大,这个指标的估值结果就会失真。最后,和PE一样, EBITDA也是一个单一的年度指标,并没有考虑到企业未来增长率这个对于企业价值判断至关重要的因素, 因而也只有在两个企业具有近似增长前景的条件下才适用。

Alpha对冲策略实现(基于掘金量化平台)

策略思想

  • 每隔1个月,定时提取沪深300成份股的过去的EV/EBITDA数据。
  • 选取EV/EBITDA大于零,并且升序排列前三十的股票后, 等权购买。
  • 用相应的沪深300期货主力合约等额对冲。

策略主要步骤实现

获取当前交易日日期

now = context.now

​ 直接调用context.now函数,返回“datetime.datetime”格式

获取上一交易日日期

last_day = get_previous_trading_date(exchange='SHSE', date=now)

​ 获取上一交易日可调用get_previous_trading_date函数,返回值为字符串格式:

  • exchang需要设置交易市场代码。
  • date需要设置指定日期。

获取当天有交易的股票

	not_suspended_info = get_history_instruments(symbols='SHSE.000300', start_date=now, end_date=now)
    not_suspended_symbols = [item['symbol'] for item in not_suspended_info if not item['is_suspended']]

​ 获取当天有交易的股票,即非停牌的股票,首先需获取停牌信息,这里需调用get_history_instruments函数,返回值类型为list[dict],之后就是将所提取的“字典”转换为”list“:

  • symbols需要设置订阅的标的代码。
  • start_dateend_date需设置获取成分股的开始与结束日期,这里需要调成上一交易日以获取上一交易日的成分股信息。

固定月初调仓

schedule(schedule_func=algo, date_rule='1m', time_rule='09:40:00')

​ 固定时间调仓可使用schedule函数进行定时任务配置:

  • schedule_func为调用的策略函数的名称。
  • date_rule可设为1m(一月)。
  • time_rule为开仓日的开仓时间,这里设为每月第一个交易日的09:40:00

获取沪深300成分股

stock300 = get_history_constituents(index='SHSE.000300', start_date=last_day,end_date=last_day)[0]['constituents'].keys()

​ 获取指数成分股可调用函数get_history_constituents或者get_constituents,返回值类型为list[dict],这里调用get_history_constituents是因为再回测时需要获取上一交易日的成分股,而get_constituents只能获取最新的成分股:

  • index需要设置获取指数的代码。
  • start_dateend_date需设置获取成分股的开始与结束日期,这里需要调成上一交易日以获取上一交易日的成分股信息。

获取“EV/EBITDA”数据

fin = get_fundamentals(table='tq_sk_finindic',
symbols=not_suspended_symbols,start_date=now, end_date=now,
fields='EVEBITDA',filter='EVEBITDA>0', order_by='EVEBITDA', limit=30, df=True)

​ 获取财务数据使用get_fundamentals函数,返回值类型为list[dict]

  • table需填写所需获取指标所在表格的代码。
  • fields设置返回值的种类。
  • orde_by需设置数据的排序方式,默认 NoneTCLOSE 表示按 TCLOSE 升序排序。 -TCLOSE 表示按 TCLOSE 降序排序。TCLOSE, -NEGOTIABLEMV 表示按 TCLOSE 升序, NEGOTIABLEMV 降序综合排序。
  • limit表示返回值的个数。

获取沪深300期货主力合约

symbol_futures = get_continuous_contracts(csymbol='CFFEX.IF', start_date=last_day,
end_date=last_day)[‐1]['symbol']

​ 获取合约信息可以用get_continuous_contracts函数,返回值类型为list[dict]

  • csymbol需填写所需获取合约的代码。
  • start_dateend_date需填写获取合约的开始与结束日期。

获取股指期货的保证金比率

ratio = get_history_instruments(symbols=symbol_futures, start_date=last_day, end_date=last_day)[0]['margin_ratio']

​ 获取期货保证金比率需调用get_history_instruments函数,返回值类型为list[dict]

  • symbols需填写所需获取合约的代码。

  • start_dateend_date需填写获取合约的开始与结束日期。

策略回测分析

回测报告

策略回测分析

分析

​ 我们选取了2017年5月至2017年10月作为回测周期,应用“EV/EBITDA”单因子作为选股基础。可以看出:

  • 胜率(具有盈利的平仓次数与总平仓次数之比)达到了74%,也即策略每十次开仓,有七次是盈利的。
  • 卡玛比率(年化收益率与历史最大回撤之比)是使用最大回撤率来衡量风险。**采用最大回撤率来衡量风险,关注的是最极端的情况。**卡玛比率越高表示策略承受每单位最大损失获得的报酬越高。在这里卡玛比率超过了3。
  • 夏普比率(年化收益率减无风险收益率的差收益波动率之比)超过1,也即承受一单位的风险,会有超过一单位的收益回报
  • 策略收益曲线 总体稳定,说明“EV/EBITDA”因子具有一定有效性。

策略源码

# coding=utf-8
from __future__ import print_function, absolute_import, unicode_literals
from gm.api import *

'''
本策略每隔1个月定时触发计算SHSE.000300成份股的过去的EV/EBITDA并选取EV/EBITDA大于0的股票
随后平掉排名EV/EBITDA不在最小的30的股票持仓并等权购买EV/EBITDA最小排名在前30的股票
并用相应的CFFEX.IF对应的真实合约等额对冲
回测数据为:SHSE.000300和他们的成份股和CFFEX.IF对应的真实合约
回测时间为:2017-07-01 08:00:00到2017-10-01 16:00:00
'''


def init(context):
    # 每月第一个交易日09:40:00的定时执行algo任务
    schedule(schedule_func=algo, date_rule='1m', time_rule='09:40:00')

    # 设置开仓在股票和期货的资金百分比(期货在后面自动进行杠杆相关的调整)
    context.percentage_stock = 0.4
    context.percentage_futures = 0.4


def algo(context):
    # 获取当前时刻
    now = context.now
    print (now)
    # 获取上一个交易日
    last_day = get_previous_trading_date(exchange='SHSE', date=now)
    print (last_day)
    # 获取沪深300成份股
    stock300 = get_history_constituents(index='SHSE.000300', start_date=last_day,
                                                end_date=last_day)[0]['constituents'].keys()
    # 获取上一个工作日的CFFEX.IF对应的合约
    index_futures = get_continuous_contracts(csymbol='CFFEX.IF', start_date=last_day, end_date=last_day)[-1]['symbol']
    # 获取当天有交易的股票+
    not_suspended_info = get_history_instruments(symbols=stock300, start_date=now, end_date=now)
    not_suspended_symbols = [item['symbol'] for item in not_suspended_info if not item['is_suspended']]
    # 获取成份股EV/EBITDA大于0并为最小的30个
    fin = get_fundamentals(table='tq_sk_finindic', symbols=not_suspended_symbols,start_date=now, end_date=now, fields='EVEBITDA',filter='EVEBITDA>0', order_by='EVEBITDA', limit=30, df=True)
    fin.index = fin.symbol
    # 获取当前仓位
    positions = context.account().positions()
    # 平不在标的池或不为当前股指期货主力合约对应真实合约的标的
    for position in positions:
        symbol = position['symbol']
        sec_type = get_instrumentinfos(symbols=symbol)[0]['sec_type']
        # 若类型为期货且不在标的池则平仓
        if sec_type == SEC_TYPE_FUTURE and symbol != index_futures:
            order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market
                                 ,position_side=PositionSide_Short)
            print('市价单平不在标的池的', symbol)
        elif symbol not in fin.index:
            order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market
                                 ,position_side=PositionSide_Long)
            print('市价单平不在标的池的', symbol)

    # 获取股票的权重
    percent = context.percentage_stock / len(fin.index)
    # 买在标的池中的股票
    for symbol in fin.index:
        order_target_percent(symbol=symbol, percent=percent, order_type=OrderType_Market,
                             position_side=PositionSide_Long)
        print(symbol, '以市价单调多仓到仓位', percent)

    # 获取股指期货的保证金比率
    ratio = get_history_instruments(symbols=index_futures, start_date=last_day, end_date=last_day)[0]['margin_ratio']
    # 更新股指期货的权重
    percent = context.percentage_futures * ratio
    # 买入股指期货对冲
    order_target_percent(symbol=index_futures, percent=percent, order_type=OrderType_Market,
                         position_side=PositionSide_Short)
    print(index_futures, '以市价单调空仓到仓位', percent)


if __name__ == '__main__':
    '''
    strategy_id策略ID,由系统生成
    filename文件名,请与本文件名保持一致
    mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST
    token绑定计算机的ID,可在系统设置-密钥管理中生成
    backtest_start_time回测开始时间
    backtest_end_time回测结束时间
    backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
    backtest_initial_cash回测初始资金
    backtest_commission_ratio回测佣金比例
    backtest_slippage_ratio回测滑点比例
    '''
    run(strategy_id='fad1fffe-c8e1-11e7-98de-9cd21ef04ea9',
        filename='main.py',
        mode=MODE_BACKTEST,
        token='your token',
        backtest_start_time='2016-01-01 09:00:00',
        backtest_end_time='2017-01-01 15:00:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=10000000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001)

暂无评论

Looks like your connection to 掘金量化社区 - 量化交易者的策略交流学习社区 was lost, please wait while we try to reconnect.