相关文章推荐
开朗的山楂  ·  Android 自定义SeekBar ...·  1 年前    · 
任性的蘑菇  ·  C# ...·  1 年前    · 
气势凌人的可乐  ·  C# ...·  1 年前    · 
打酱油的香槟  ·  SQLite 教程 | 菜鸟教程·  1 年前    · 

杨  冰分析员,SAC执业证书编号:S0080515120002;SFC CE Ref: BOM868

程昱分析员,SAC执业证书编号:S0080517070005;SFC CE Ref:BON965

陈健恒分析员,SAC执业证书编号:S0080511030011;SFC CE Ref: BBM220

或许市场不够重视,但这可能也是隐藏价值的一个角落。配售和定增基金往往会持有一些流通受限的股票,而这些股票估值的方法在2017年经历了一次比较大的变化——根据《证券投资基金投资流通受限股票估值指引(试行)》,限售股适用AAP估值法(亚式看跌期权),取代此前近似摊余成本的方法。目前,主要是定增基金和配售基金涉及比较多这类问题。实事求是地说,即使在以前的简易算法下,虽然公式简单,但数据处理存在一定难度,投资者对于当时(比如2016年)定增基金的净值也存在一定疑虑。而现在公式更为复杂了,对投资者来说黑箱化的程度也自然更高了。在以前的模式下,限售股(比如定增得来的股份)的估值为:V= min(当前价,成本价+ (当前价 - 成本价)* 已持有的时间 / 总锁定期),或者表示如下图。

而新的AAP模型估值法下,估值 = 市场价* (1 -折扣系数),折扣系数为按照亚式看跌期权计算出的期权价值(LoMD)。公式如下:

2bf95d295db6f6ffc1f8e608cb306b3b.png 可以看出,想比之前近似摊余成本法的计算模式,这里复杂程度还是要高出不少的。其中,σ为该股票波动率,q为分红率,T为以年为单位的锁定时间,其他均为字面意思。实际上,上面公式在实现时基本没有难度,更多的难点在于数据的处理。

下面,我们来一步一步拆解这个LoMD。首先自然需要引入一些常见库:

import readSql as rs

import pandas as pd

from scipy import stats

from WindPy import w

import datetime as dt

其中的readSql为我们惯用的自编库,在此前的转债报告中也曾出现过,但为了不产生太多歧义,我们只用其中的几个函数即str2yyyymmdd、yyyymmdd2str:在日期的两种字符串之间互相转换,例如“2019/1/18”转成“20190118”及其逆运算。

下面我们先展示最后的主函数,然后逐个击破涉及到的中间函数。整个主函数如下所示。第一个参数dfHeld为pandas下的DataFrame型,其中保存的是基金所持限售股的信息(参考年报、半年报以及基金临时公告整理),示例格式如下图。而后面的start和end分别为计算期的起止时间,我们最后返回了在这个期间内,一组限售股的折扣系数,形式同样是DataFrame。

defdiscRatioTable(dfHeld, start,end):

'''输入限售股table,输出这些券对应的LoMD

start: 计算首日,yyyy/mm/dd

end: 计算终日, yyyy/mm/dd'''

# 先计算这些股票的vol

lstStocks = list(dfHeld.index)

nDaysBefore, flag = backToTime(start, list(dfHeld[u'可流通日期'].apply(rs.yyyymmdd2str)))

if flag:

dfAdjClose = priceData(lstStocks, nDaysBefore, end)

dfLogPct = logPctData(dfAdjClose)

dfLogPct.to_clipboard()

else:

print u'没有限售股,不必算了'

return None

dfVol = pd.DataFrame(index=dfAdjClose.loc[start:end].index,columns=lstStocks)

for code in lstStocks:

for date in dfVol.index:

days = _deletaTradingDate(date,rs.yyyymmdd2str(dfHeld.loc[code, u'可流通日期']))

dfVol.loc[date, code] =calcVol(dfLogPct, code, date, days) if days > 0 else None

# 然后是q,股息率

dfDiv = dividendYield(lstStocks, start, end)

# 最后计算discRatio表

dfDiscRatio = pd.DataFrame(index=dfVol.index, columns=lstStocks)

for code in lstStocks:

strEnd = rs.yyyymmdd2str(dfHeld.loc[code, u'可流通日期'])

for date in dfDiscRatio.index:

t = deltaT(date, strEnd)

dfDiscRatio.loc[date,code] = aapDisc(dfDiv.loc[date, code], dfVol.loc[date, code], t)

return dfDiscRatio

观察整个函数结构,可以比较清晰地看到,整个过程分为三步

1、计算波动率表;

2、计算股息率表;

3、根据前两步的结果计算折扣系数并最终返回。

下面来计算波动率σ。根据《指引》,其计算方法为先确定计算时点距离股票可流通的到期日数(暂且记作n,但当n不足20时取20)。然后用计算日前n日股价的历史对数收益率计算波动率。因此,这里实际第一步是计算n(下方_deletaTradingDate函数),然后确定n个交易日之前是哪一天(下方backToTime函数)。两个函数中间,用tDaysBefore作为过渡。而由于我们每次对一个基金进行计算时,往往其所持限售股不止一个,因此我们将第二个参数类型预设为列表(list)。具体如下:

defbackToTime(now, lstDays):

‘’’now是计算时点,lstDays是解禁日列表’’’

dtNow = dt.datetime.strptime(now, '%Y/%m/%d')

dtMax = max([dt.datetime.strptime(t, '%Y/%m/%d') for t inlstDays])

strMax = dt.datetime.strftime(dtMax, '%Y/%m/%d')

if dtMax > dtNow:

n = _deletaTradingDate(now, strMax)

return tDaysBefore(now, n), 1

else:

return now, 0

def_deletaTradingDate(start,end):

'''交易日差(以日为单位)'''

obj = w.tdayscount(start, end)

if obj.ErrorCode == 0:

return obj.Data[0][0]

else:

print "Connect ErrorCode",obj.ErrorCode

raise ValueError(u'算交易日出现错误')

deftDaysBefore(now, days):

obj = w.tdaysoffset(-days, now)

return dt.datetime.strftime(obj.Data[0][0],'%Y/%m/%d')

在主函数中,我们用上述方法确定计算时间的起止点,确定好之后,就可以提取股价、计算对数收益率并据此计算波动率了。这里有一定难点是:如果股票在预定的计算区间内无成交(比如停牌,或者新股未上市),需要用对应行业的AMAC指数数据作为替代,详见下方logPctData函数。

def loadData(tickers, field, start,end, *others):

# 用万得API取数据的通用函数

strTickers = ','.join(tickers)

obj = w.wsd(strTickers, field, start, end, others)

arrData = np.array(obj.Data).transpose()

srsDate = [dt2str(d) for d in obj.Times]

return pd.DataFrame(arrData,index = srsDate, columns = tickers)

defpriceData(lstStocks, start,end):

'''取复权价

lstStocks:股票列表

start:yyyy/m/d格式的时间起点

end:时间终点'''

dfAdjClose = loadData(lstStocks, ‘close’, start, end, ‘Priceadj=B’)

return dfAdjClose

deflogPctData(dfAdjClose):

dfLogPct = dfAdjClose.applymap(np.log) - dfAdjClose.shift(1).applymap(np.log)

t = dfAdjClose.apply(lambda x: not(all(pd.notnull(x))))

lstNullCodes =list(set(t[t].index))

# 下面的内容是,如果存在异常数据,则用行业指数替代

if lstNullCodes:

obj = w.wss(','.join(lstNullCodes), 'indexcode_AMAC', 'tradeDate={_end}'.format(_end=rs.str2yyyymmdd(dfAdjClose.index[-1])))

dfStockVsIndex = pd.DataFrame(np.array(obj.Data).transpose(),index=obj.Codes, columns=['AMAC'])

dfIndex = loadData(dfStockVsIndex[‘AMAC’].values.tolist(), ‘close’,dfAdjClose.index[0],dfAdjClose.index[-1])

dfPctIndex = dfIndex.applymap(np.log) - dfIndex.shift(1).applymap(np.log)

for i in xrange(1,len(dfLogPct)):

t = dfLogPct.iloc[i].isnull()

lstNullInThisRow = list(t[t].index)

iflstNullInThisRow:

for c inlstNullInThisRow:

idxID =dfStockVsIndex.loc[c, 'AMAC']

dfLogPct.iloc[i].loc[c] =dfPctIndex.iloc[i].loc[idxID]

return dfLogPct

这一步最简单的反而是最终计算波动率,如下函数,没有太多值得解释的地方。注意,下面的函数是计算单个股票、单个交易日的波动率,实际存在一个效率的问题(显然不如整个DataFrame直接去算,这也是我们将在实际使用时修正的内容,这个问题在最后算折扣率时也存在)。

defcalcVol(dfLogPct, ticker, now,days):

'''dfLogPct:复权收益表

ticker:代码

now:yyyy/mm/dd

days:自然数字,时间窗口'''

loc = dfLogPct.index.get_loc(now)

start = max([0, loc-int(days)])

return dfLogPct.iloc[start:loc].loc[:, ticker].std() *np.sqrt(250)

然后是股息率。类似地,这里有一个问题是如果上市未满一年,股息率需要用行业指数做替代。不过相比于波动率,这里毕竟不涉及日期处理,整体要简单一些。

defdividendYield(lstStocks,start, end):

obj = w.wss(','.join(list(set(lstStocks))), 'ipo_date')

dfIPO_Date = pd.DataFrame(np.array(obj.Data).transpose(),index=lstStocks, columns=['ipo_date'])

dfIPO_Date['OneYearAfter'] = dfIPO_Date['ipo_date'].apply(lambdax: x + dt.timedelta(365))

obj = w.wss(','.join(list(set(lstStocks))), 'indexcode_AMAC','tradeDate={_end}'.format(_end=rs.str2yyyymmdd(end)))

dfStockVsIndex = pd.DataFrame(np.array(obj.Data).transpose(),index=lstStocks, columns=['AMAC'])

dfStockVsIndex.fillna('881001.WI', inplace=True)

lstIndexCodes = list(set(dfStockVsIndex['AMAC'].values))

dfStockDiv = loadData(lstStocks, ‘dividendyield2’, start, end)

dfIndexDiv = loadData(lstIndexCodes, ‘dividendyield2’, start, end)

for code in dfStockDiv.columns:

for idx in dfStockDiv.index:

ifpd.isnull(dfStockDiv.loc[idx, code]) or dt.datetime.strptime(idx,"%Y%m%d") <= dfIPO_Date.loc[code, 'OneYearAfter']:

idIndex =dfStockVsIndex.loc[code, 'AMAC']

dfStockDiv.loc[idx, code] =dfIndexDiv.loc[idx, idIndex]

dfStockDiv.index = [rs.yyyymmdd2str(t) for t indfStockDiv.index]

return dfStockDiv / 100.0

最后,是AAP估值数据。可以看到,这个函数才是最简单的,没有太多值得解释的地方。但也请注意,这里是为表达清楚,因此用了三个参数都是数值的模式——问题随之而来,这样就无可避免在最后的批量运算中调用两层嵌套的循环,显然会有效率问题,因此我们在最后使用时,实际在这里进行了调整,需要用类似矩阵运算的方法处理。

defaapDisc(q, v, t):

'''q:股息率(单位不是%)

v:波动率(单位不是%)

t:剩余时间(单位是年)'''

v2t = v**2*t

d = np.sqrt(v2t + np.log(2 * (np.exp(v2t) - v2t - 1)) - 2 *np.log(np.exp(v2t) - 1))

discRatio = np.exp(-q*t) * (stats.norm.cdf(d/2.0) -stats.norm.cdf(-d/2.0))

return discRatio

完成整个计算过程后的一点感受:

1、AAP估值和以前的摊余成本法真正的区别是什么?简单来说,老算法基本依赖于成本价和锁定时间,新的算法下,估值与成本价已经基本无关,取而代之的是锁定时间、波动率和股息率。这也意味着:1)无论配售和定增,基金净值都有可能在拿到股份的节点,出现跳升(当然定价不合理也)。因此配售\定增发生的预期,对基金价值的影响会比较大;2)以及,定增的定价上,如果要引入公募基金,那么这个定价的折扣就不能低于AAP估值法太多,否则参与即浮亏,在基金这一端是比较难以接受的。

2、现行算法下,什么是重要的?三要素变成了波动率、锁定期和股息率。我们观察,最不重要的是股息率,最重要的是锁定期,波动率居于二者之间。

固收+ 市场跟踪

1、分级A市场方面,分级A指数近10个交易日上涨0.2%,其中此前大涨的R+3.5%指数回落,跌0.13%,而R+3.0%指数涨幅达到0.26%。主流品种整体差异不大,军工A上涨0.49%,而深成指A仍有0.24%的涨幅。对于分级A市场,我们保持近期观点,目前市场平均折价仍较低,不具备鲜明的投资特性,因而对于博弈性投资者而言,分级A价值不大。但毕竟绝对收益率水平不低且股市的波幅实际已经变窄,加大了分级A产品的债券成分,因此存在债券替代价值。品种上,可关注券商A及深成指A。

2、打新基金:近期,由于科创板新股涨幅整体降低,打新基金的无风险收益已经有所降低。但11月新股上市的数量较多,也有部分个股的上市表现较好,因此我们筛选的打新基金样本池11月普遍表现出了正增长。

3、配售基金近10日平均下跌0.88%,与市场预期走势比较吻合。此前市场开始预期银行股的战略配售,配售基金随之上涨。不过近期市场情绪不佳,新股上市出现破发、涨幅偏低等情况,配售基金近期也出现回落。

定增基金分化稍大,平均涨幅1%,其中九泰瑞富、九泰泰富涨幅超过2%。整体上,定增基金的名义、隐含折价率有小幅压缩,但并不明显。

4、转债市场方面,相比股市来讲,转债指数的振幅要小得多,近10日小幅下跌0.32%。背后原因在于,近期调整的主力军消费白马,在转债中分布不多,即便存在,其所占权重也很低。而真正主导转债指数的,仍是银行转债。总体上,我们在8月底认为转债买入窗口关闭,机会减少,近期仍保持这一观点。但近期股市情绪大幅回落,转债新券上市的价位、估值也开始和老券拉开距离,我们建议投资者开始关注转债市场估值调整以及低估值品种数量,尤其12月中旬有可能随着低价新券上市增多,转债市场机会可能重新出现。

5、ABS市场方面,临近年底银行间市场的发行节奏有所加快。在多个资产共同发力的情况下,10-11月的发行规模已经突破2800亿。如果12月的发行节奏依然较快,则全年发行量仍保留了超预期的可能性,12月可能迎来一个投资价格较好的时点。

价格方面,在超预期的供给暂时还未出现之前,价格仍然保持在低位。

6、REITs市场方面,国内共有6支基金主要投资于REITs领域。其中,上投摩根、南方、广发的三只基金别分跟踪了3个较为主流的REITs指数;诺安、嘉实、鹏华的三只基金则为主动投资。

此前美国REITs市场出现较大的调整,近两周价格表现有了一定的恢复,各基金净值均出现了正增长。

本文所引为报告部分内容,报告原文请见2019年11月29日中金固定收益研究发表的研究报告。

免责声明:自媒体综合提供的内容均源自自媒体,版权归原作者所有,转载请联系原作者并获许可。文章观点仅代表作者本人,不代表新浪立场。若内容涉及投资建议,仅供参考勿作为投资依据。投资有风险,入市需谨慎。

代码是基于《财务报表分析与 股票 估值 》的,其中自由现金流的口径与大众认知略有出入,建议使用前先阅读该书第14、15章; 本人非计算机专业,模型代码可能存在部分错误; 银行 暂时无法 估值 ,因为其财报形式和其他种类公司相比略有不同; 数据采用的是邢不行老师整理的 股票 历史日线数据和新浪财务数据; 本人非财务、会计专业,尽管过程中请教了CPA大神,但财务数据口径依然可能存在问题。 DCF介绍 自由现金流贴现法是绝对 估值 法的一种,理论基础是现值原理:任何资产的 价值 都等于其预期未来全部现金流的现值总和,对公司而言就是自由现金流。 由于准确预测未来所有自由现金流是不可能的,而且 股票 并没有固定的生命周期,因此将模型简化为以下四种: $$ \begin{aligned} &零增长模型:V=\frac{FCF}{WACC}\ &不变增长模型:V=\frac{FCF(1+g)}{WACC-g}\ &两阶段模型:V=\sum_{t=1}^n\frac{{FCF}t}{(1+WACC)^t}+\frac{TV}{(1+WACC)^n},\ \ 其中TV=\frac{FCF_n(1+g_2)}{WACC-g_2}\ &三阶段模型:V=\sum{t=1}^n\frac{{FCF}0(1+g_1)}{(1+WACC)^t}+\sum{t=n+1}^m\frac{{FCF}n(1+g_2)}{(1+WACC)^t}+\frac{FCF{n+m}(1+g_3)}{(WACC-g_3)(1+WACC)^{n+m}}\ \end{aligned} $$ 零增长模型适用于成熟稳定、没有增长的公司,每年的自由现金流也保持在一个稳定的金额水平,类似于永续年金;如果该类公司的自由现金流全部用于发放 利现金,那么其得出的结果与 利贴现模型非常接近。 不变增长模型适用于成熟的公司,未来的自由现金流以非常缓慢的速度增长。 在两阶段模型中,投资者的预期回报WACC至少要高于总体的经济增长率;不变增长率g2通常小于WACC,反之,意味着很长时间以后公司的规模将超过总体经济规模。 在三阶段模型中,假设所有的公司经历三个阶段:成长阶段、过渡阶段和稳定阶段。三个阶段的成长率由高到低,稳定阶段保持较低增长率的不变增长。 具体计算步骤: 计算自由现金流并依据相应的 方法 折现($\star\star\star\star\star$) 计算 价值 = 折现后的自由现金流+金融资产+长期 权投资-公司债务 计算少数 东比例 归属于上市公司 东的 价值 = 价值 $\times$(1-少数 东比例) 每 内在 价值 =归属于上市公司 东的 价值 / 本 经营资产自由现金流=公司维持原有生产经营规模前提下的增量现金流入=经营活动现金流量净额-保全性资本支出=经营活动现金流量净额-固定资产折旧-无形资产和长期待摊费用摊销-处置长期资产的损失 $WACC=k_d\times\frac{D}{D+E}\times(1-t)+k_e\times\frac{E}{D+E}$。其中债务资本成本率=债务资本总额/债务资本平均金额$\times$100%=(财务费用+汇兑收益)/(期初债务资本+期末债务资本)/2; 权资本成本率应该高于同期的国债利率,加上 股票 投资的风险溢价,我们普遍设置为9%;t为公司实际所得税税率=1-净利润/税前利润。 公司债务=有息债务 少数 东比例=$\frac{少数 东权益}{ 东权益合计}$ 本=市值/ 价 蒙特卡洛模拟计算看涨期权蒙特卡洛 估值 是一个计算量较高的一个算法,对于简单的问题也需要海量的计算,因此要高效的 实现 蒙特卡洛算法. 下面使用不同的 方法 实现 蒙特卡洛算法 1.Scipy 2.纯 Python 3.Numpy(1) 4.Numpy(2) 方法 一:Scipy欧式看涨期权的定价公式Black-Scholes-Merton(1973): 虽说中国 市太靠谱,都是炒概念、炒预期的,但是有一个事情却确确实实是 民的福利。这就是分红。哪如何计算获取一个 股票 的分红最高呢?本文就讲述一下如何获取 股票 的分红情况,如何编程获取 市中分红最高的 股票 。 这里首先讨论一个问题:是不是每 分红最高的 股票 ,我们就可以认为获取的分红就是最高的呢?例如贵州茅台2019年每10 分红145.39元。万科2019年每10 分红10.45102元。可以看到贵州茅... 截止2019年年底我国 股票 投资者数量为15975.24万户, 如此多的 民热衷于炒 ,首先抛开炒 技术不说, 那么多 股票 数据是不是非常难找, 找到之后是不是看着密密麻麻的数据是不是头都大了? 今天带大家爬取雪球平台的 股票 数据, 并且 实现 数据可视化 先看下效果图 格雷厄姆 股票 估值 模型格雷厄姆 股票 估值 模型具体思路如下:文件结构合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 格雷厄姆 股票 ... 清华编程高手尹成带你基于算法实践 python 量化交易量化交易是指以先进的数学模型替代人为的主观判断,利用计算机技术从庞大的历史数据中海选能带来超额收益的多种“大概率”事件以制定策略,极大地减少了投资者情绪波动的影响,避免在市场极度狂热或悲观的情况下作出非理性的投资决策。定量投资和传统的定性投资本质上来说是相同的,二者都是基于市场非有效或弱有效的理论基础。两者的区别在于定量投资管理是“定性思想的量化...