预测公司危机发生 Logit & Probit

Photo by Lukas Blazek on Unsplash

本文重点概要

  • 文章难度:★★★☆☆
  • 以回归模型加入不同解释变数,讨论台股公司之危机发生机率
  • 阅读建议:本文在模型方面,并不会深入探讨回归之架构组成,仅会分析模型结果及举例,让我们了解的资讯,因此需要读者对统计模型有基本的观念,先阅读【量化分析】预测市场?!;此外,本文提及之标的仅供说明使用,不代表任何金融商品之推荐或建议。

前言

预测未来是大家投资者们想追求的,不论是对于未来的市场,或是对于公司及产业的未来,然而预测未来总存在著不确定及随机性,因此我们只能利用过去的历史资料及现有的公司指标,将其投入统计中进行验证,本文与先前文章【量化分析】预测市场?! 内容类似,但差别在于本文是预测公司未来,并且做出三种统计方式的比较。

使用不同指标,ROA、CR、DR、ATTNVR、CFAT、RSize、Sigma、ExRET,检定验证发现某些指标具有解释能力,并纳入回归模型,利用结果来解释危机发生机率。

此篇文章之回归变数参考两位作者相关论文

  1. Altman z-score model (1968)
  2. Shumway (2001)

编辑环境及模组需求

本文使用 Spyder作为编辑器

###量化三宝、套件工具
import pandas as pd
import numpy as np
import tejapi
import statsmodels.formula.api as smf
import datetime as dt
from dateutil.relativedelta import *
tejapi.ApiConfig.api_key = "YOUR_KEY"
tejapi.ApiConfig.ignoretz = True ###时间栏位忽略时区

资料库使用

公司基本资料详细栏位说明:

标的:上市、上柜、兴柜、公开发行

资料库代码为(TWN/AIND),捞取其栏位「危机发生日、危机事件类别」

df1 = tejapi.get('TWN/AIND', #从TEJ api捞取所需要的资料
                chinese_column_name = True,
                paginate = True,
                opts={'columns':['coid','mdate', 'dflt_d','fail_fg']})

首先将捞取的资料做分类,剔除上市前之公司,由于我们是衡量「危机事件发生机率」,因此我们定义危机事件状况设为1,并开始处理资料,事件发生日要重设,方便之后与财报资料作合并。

df2 = df1[df1['危机事件类别'] != ''] #把有发生违约事件的筛选出来
df2['年/月'] = df2[['危机发生日']].applymap(lambda x: x.strftime('%Y-%m')).astype('datetime64') #把日期都换成月初
df2['月'] = df2['年/月'].dt.month #取月份出来
df2.reset_index(inplace = True)for i in range(len(df2.index)): 
    if df2['月'][i] == 1 or df2['月'][i] == 4 or df2['月'][i] == 7 or df2['月'][i] == 10:
        df2['年/月'][i] = df2['年/月'][i]+ relativedelta(months = +2)
    if df2['月'][i] == 2 or df2['月'][i] == 5 or df2['月'][i] == 8 or df2['月'][i] == 11:
        df2['年/月'][i] = df2['年/月'][i]+ relativedelta(months = +1)
#为了之后对上财报资料,先将日期处理好
df2['Y'] = 1 #把所有危机事件类别设成1
df2.rename(columns= {'公司简称':'公司'}, inplace=True)

接著将全市场会计科目不限期数之资料捞下,并挑选所需要的变数(会计科目)即可,包含:

X1 = working capital(R678)/total asset (0010)

X2 = retained earnings (2341)/ total asset

X3 = EBIT (2402)/total asset

X4 = market value (MV)/ total debt (1000)

X5 = revenue/ total asset (R607)

X6 = ROA (R11V)

X7 = debt ratio (R505)

TEJ会计科目细项及代码

由于一次捞取的资料比数有限(paginate = True,最多每次捞取100万笔),因此,我们需要分段捞取再将其合并成同一个Dataframe。

###每一Dataframe大概有30万笔资料以此类推
a1 = tejapi.get('TWN/AIFIN', #从TEJ api捞取所需要的资料
                chinese_column_name = True,
                paginate = True,
                mdate = {'gt':'2008-01-01', 'lt':'2011-01-01'},
                acc_code = ['R678', '0010','2341','2402','MV','1000', 'R607','R11V', 'R505'])
a2 = tejapi.get('TWN/AIFIN', 
                chinese_column_name = True,
                paginate = True,
                mdate = {'gt':'2011-01-01', 'lt':'2014-01-01'},
                acc_code = ['R678', '0010','2341','2402','MV','1000', 'R607','R11V', 'R505'])
a3 = tejapi.get('TWN/AIFIN', 
                chinese_column_name = True,
                paginate = True,
                mdate = {'gt':'2014-01-01', 'lt':'2017-01-01'},
                acc_code = ['R678', '0010','2341','2402','MV','1000', 'R607','R11V', 'R505'])
a4 = tejapi.get('TWN/AIFIN', 
                chinese_column_name = True,
                paginate = True,
                mdate = {'gt':'2017-01-01', 'lt':'2020-01-01'},
                acc_code = ['R678', '0010','2341','2402','MV','1000', 'R607','R11V', 'R505'])
a5 = tejapi.get('TWN/AIFIN', 
                chinese_column_name = True,
                paginate = True,
                mdate = {'gt':'2020-01-01'},
                acc_code = ['R678', '0010','2341','2402','MV','1000', 'R607','R11V', 'R505'])
###合并资料
acc = pd.concat([a1,a2,a3,a4,a5])

转换会计科目数值将其放到columns,有利于将会计科目计算成所需的变数,计算逻辑来自参考文献,并将合并后没有危机发生之公司设为0,资料尚有一个特殊值须删除。

acc1 = acc.pivot_table(values='数值', index=['公司','年/月'], columns='会计科目')
acc1['X1'] = (acc1['R678']/acc1['0010'])*100
acc1['X2'] = (acc1['2341']/acc1['0010'])*100
acc1['X3'] = (acc1['2402']/acc1['0010'])*100
acc1['X4'] = (acc1['MV']/acc1['1000'])*100
acc1 = acc1.rename(columns = {'R607':'X5', 'R11V':'X6', 'R505':'X7'})
acc2 = acc1[['X1','X2','X3','X4','X5','X6','X7']]
acc2.reset_index(inplace=True)
df3 = pd.merge(acc2, df2[['公司','年/月','Y']], how='outer')
df3['Y'] = df3['Y'].replace(np.nan, 0) #把没有发生危机的设为0
df3 = df3.dropna()
df3['X4'] = df3['X4'].drop([59690,59688]) #把无限大的值去掉
df3 = df3.rename(columns = {'状况':'Y'})

接著带入变数于三种模型检定其解释力,我们可以发现:

变数X1在Logit模型中是不显著的,而X2、5、6在三种模型中皆具有显著性,其预测的结果也与我们直观来说是符合的。以ROA举例,当ROA越高公司利用资产获利的能力越佳,倒闭的可能性越低,系数为负,符合预期。

OLS模型结果
OLS模型结果

接著将个别公司的会计资料带入回归模型中,来取得公司危机发生的机率,我们以上市柜2330台积电、2454联发科,下市1592英瑞KY、2475华映。

结论

可以发现我们的护国神山及发哥的危机事件发生极为低,假设以他为基准来比较可以看到LPM模型(OLS),下市的1592英瑞KY、2475华映的危机发生机率比2330台积电、2454联发科来得多出至少3倍以上,直观而言,权值股之所以是权值股,他的公司体质一定比其他公司来的好,此预测不代表绝对,我们仅使用了相关会计资料所显示的状态去作出回归验证,但还是具有一定参考性及解释力,这也意味著股市本来就存在不确定性,若有看似危机的讯号出现应当时刻关注,并留意自身风险的评估;因此,欢迎持续关注本平台,我们后续将会有更多文章向大家分享,此外,也欢迎读者及投资者们选购 TEJ E Shop中的方案来套用试试看自身手中的持股,危机发生的可能与比较,相信读者拥有完整的资料库后,就能对股市的不确定性有所掌握。

完整程式码

延伸阅读

相关连结

返回总览页