基于EV/EBITDA的量化策略(基于python,附代码)

一、估值方法概括

估值方法共分为两类:绝对估值法和相对估值法。

绝对估值法以“资产的价值是未来收入的折现”为基准,将未来现金流按照一定贴现率贴现到当前时间点得到资产的价值;这种估值方法有一个难点:如何合理预测公司未来收入?

相对估值法通常利用各种比率进行估值,这种方法常见的用法是:预估整个行业或相似企业各比率的合理水平,当目标公司超过该合理水平时,则为高估或低估。

常用的绝对估值法主要包括:股利贴现模型、自由现金流贴现模型等。常用的相对估值法主要包括:市盈率估值法、市净率估值法、市销率估值法、PEG估值法、企业价值倍数法(EV/EBITDA)等。

二、PE与EV/EBITDA对比

市盈率以其方便的特点成为不少投资者进行估值时首要选择的指标。市盈率和EV/EBITDA之间有什么区别呢?

1.P/E市盈率

市盈率分为动态市盈率和静态市盈率。静态市盈率是用上一个报告期的每股收益作为分母计算得到,动态市盈率是用当期或以后的每股收益预测值计算得到。在行情软件中,多用动态市盈率。

市盈率的含义可以理解为股价收益比率。假设一个股票股价为100,每股收益为10,这时该只股票的市盈率为10。假设在理想情况下,意味着只需要10年该股票就能挣到100,即能回本。因此,常被视为一种估值手段。

市盈率的使用这么简单吗?显然不是。许多公司为了让数据好看,会做多收益,加入一次性利润。比如上一个例子,每股收益的10中只有8是正常经营活动得到的,剩余的2是由于本期一次性收益得到的,但计算市盈率时,这部分仍然被算在里面,这就会导致市盈率偏低。

2.EV/EBITDA

含义:EV/EBITDA又称企业价值倍数,公式为EV/EBITDA。

EV:公司价值,EV=市值+总负债-总现金

EBITDA: 息、税、折旧、摊销前盈余,EBITDA = 营业利润 + 折旧费用 + 摊销费用

和PE相比,从分子来看,EV/EBITDA用企业价值代替股票市值,既考虑了股东的情况,也考虑了债权人的情况,弥补了PE指标只考虑股东没有考虑债权人的不足。从分母来看,EV/EBITDA的分母用息、税、折旧、摊销前盈余代替了每股净收益,不包含财务费用,不受企业资本结构的影响;剔除了折旧和摊销,不受折旧政策的影响。另外,EBITDA不包含投资收益、营业外收入等其他收益项目,使得企业间的比较更加纯粹。

与PE相比,EVEBITDA还有一些缺陷。EBITDA没有考虑到税收因素,如果税收差异大,指标的估值就会失真。

三、基于EV/EBITDA的套利策略

策略思路:以过去20个周期的EV/EBITDA数据为基准,计算均值和方差。设定EV/EBITDA的上下界分别为:均值±标准差。当EV/EBITDA超过上界时,说明当前股价被高估,需平仓。当EV/EBITDA低于下界时,说明当前股价被低估,买入1000股。

回测期:2015.11.01至2020.11.10
回测期初始资金:1000万
回测手续费:0.01%
回测滑点:0.01%

回测结果如下:
在这里插入图片描述

回测期累计收益为60.45%,年化收益率为12.02%,最大回撤为11.29%,胜率为96%。

四、 策略代码

注:此策略基于掘金量化平台,数据来自掘金量化平台。网址为:掘金量化平台

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


# 策略中必须有init方法
def init(context):
    # 获取成分股
    context.symbol = 'SHSE.600519'
    # 每周计算一次上下界
    schedule(schedule_func=algo1,date_rule='1w',time_rule='09:00:00')
    # 每日调仓一次
    schedule(schedule_func=algo2,date_rule='1d',time_rule='09:50:00')


def algo1(context):
    # 获取EVEBITDA数据
    data = get_fundamentals_n(table='trading_derivative_indicator', symbols=context.symbol, count=20,
                            end_date=context.now, fields='EVEBITDA', df=True)
    # 计算出合理范围
    data_mean = data['EVEBITDA'].mean()
    data_std = data['EVEBITDA'].std()
    context.upper = data_mean + data_std
    context.lower = data_mean - data_std



def algo2(context):
    # 获取最新EVEBITDA数据
    data = get_fundamentals_n(table='trading_derivative_indicator', symbols=context.symbol, count=1,
                              end_date=context.now, fields='EVEBITDA', df=True)
    # 低于下界的买入
    upper = context.upper
    lower = context.lower
    new_EVEBITDA = data['EVEBITDA'].iloc[0]

    if new_EVEBITDA < lower:
        order_volume(symbol=context.symbol, volume=1000, side=1, order_type=OrderType_Market, position_effect=PositionEffect_Open)
        print('以市价单买入1000股')

    if new_EVEBITDA > upper:
        position = context.account().position(symbol=context.symbol, side=PositionSide_Long)
        if position:
            order_target_volume(symbol=context.symbol, volume=0, position_side=PositionSide_Long, order_type=OrderType_Market)
            print('以市价单平仓')



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='strategy_id',
        filename='main.py',
        mode=MODE_BACKTEST,
        token='token',
        backtest_start_time='2015-11-01 08:00:00',
        backtest_end_time='2020-11-10 16:00:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=10000000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001)

猜你喜欢

转载自blog.csdn.net/weixin_42219751/article/details/115325057
今日推荐