Investment_2022_fall_Assignment2

Reference

Bradley, Jordan and Ritter (2003)

Background

After completion of an IPO, a firm is still considered to be in registration for an additional period of time. During this time, 25 days during our sample period which is termed the post-IPO "quiet" period, firms may make statements of fact regarding business developments and may respond to inquiries from analysts and shareholders regarding factual matters.

However, U.S. Securities and Exchange Commission (SEC) regulations generally prohibit firms and their underwriters from publishing opinions (known as recommendation) concerning valuation and from making forward-looking statements regarding earnings, revenues, and similar items.

We will using event study to see how the first recommendation for IPO/the end of the "quiet period" make effects to the stock return. Watch out that the two event may not be on the same day.

Summary

  1. Keep only the earliest recommendation for each IPO
  2. Supposed that the event is the first recommendation date for each IPO. Generate a table similar to Table II in the paper per type of recommendation by 3 groups: one for strong buys (IRECCD=1), one for buys (IRECCD=2) and one for the others (IRECCD=3, 4 or 5)
  3. Repeat 2) using the event as the end of the quiet period i.e. OFFER_DATE+25.
  4. Repeat 2) with two groups of IPOs, based on whether there are recommendations in the [-2,+2] window around the end of the quiet period.
  5. Interpret your figure with the results you had for parts 4) and 5)

Data Discription

IPOs_1996_2000: PERMNO (permno of the company), CUSIP (of the company), and OFFER_DATE (the date of the IPO).(p.s. PERMNO and CUSIP are both id of stocks in the U.S.A, but CUSIP would change depending on the company delisting, etc.,while PERMNO would never change)

Recommendation: CUSIP (identification of the company the recommendation refers to), ANNDATS (the date the recommendations was issued), and IRECCD (a code for the type of recommendations, defined as following: 1=STRONG BUY, 2=BUY, 3=HOLD, 4=UNDERPERFORM, and 5=SELL)

AssignmentReturns & Market_returns: return data of the stocks and value-weigthed index as the market portfolio return

Code

1

sort by announce date, and drop the repeating stocks by CUSIP

cd D:\Janis\Desktop\资产定价(投资学)\data
import pandas as pd
import numpy as np
from scipy import stats
from scipy.stats import ttest_1samp
from datetime import timedelta
import matplotlib.pyplot as plt
d_ipo = pd.read_sas('IPOs_1996_2000.sas7bdat')
d_rcm = pd.read_sas('Recommendation.sas7bdat')
#按announce date排序,并去掉重复的CUSIP,就能得到股票首次被anno的日期的列表
d_rcm_by_date = d_rcm.sort_values(by = 'ANNDATS', ascending=True).drop_duplicates(subset=['CUSIP'], keep='first')
#把推荐等级IRECCD从b string转换成int,方便后续筛选
d_rcm_by_date['IRECCD'] = [int(s.decode()) for s in d_rcm_by_date['IRECCD']]
#加入Offer_Date 股票发行日
d_rcm_by_date = pd.merge(pd.DataFrame(d_ipo, columns=['CUSIP', 'Offer_Date']), pd.DataFrame(d_rcm_by_date, columns=['CUSIP', 'IRECCD', 'ANNDATS']), on = 'CUSIP')
#后续分别以第一次anno、Offer_Date+25为event
d_rcm_by_date
CUSIP Offer_Date IRECCD ANNDATS
0 b’78668010′ 1996-01-11 1 1996-02-23
1 b’65539810′ 1996-01-18 2 1996-05-23
2 b’75642210′ 1996-01-22 2 1996-02-21
3 b’02109K10′ 1996-01-23 1 1996-03-19
4 b’30271210′ 1996-01-23 1 1996-02-27
1846 b’84749R10′ 2000-12-07 2 2001-01-02
1847 b’41690610′ 2000-12-07 1 2001-02-26
1848 b’02888410′ 2000-12-07 1 2001-01-03
1849 b’37246C10′ 2000-12-11 2 2001-04-20
1850 b’76122Q10′ 2000-12-14 1 2001-01-09

1851 rows × 4 columns

2

calculate the delta days from the event day to the day to fill the table, like bucket sort

d_ret = pd.read_sas('AssignmentReturns.sas7bdat')
d_mret = pd.read_sas('Market_returns.sas7bdat')
#d_merge_ret中存储全部日期、全部股票的return数据
d_merge_ret = pd.merge(pd.DataFrame(d_ret, columns=['CUSIP', 'PERMNO', 'RET', 'DATE']), pd.DataFrame(d_mret, columns=['date', 'VWRETD']), left_on = "DATE", right_on = "date")
d_merge_ret = d_merge_ret.drop(columns = 'date')
#计算market adjusted returns
d_merge_ret['mar'] = d_merge_ret['RET'] - d_merge_ret['VWRETD']
#d_calc中存储股票编号、mar、IRECCD
d_calc = pd.merge(pd.DataFrame(d_merge_ret, columns = ['CUSIP', 'DATE', 'mar']), pd.DataFrame(d_rcm_by_date, columns = ['CUSIP', 'IRECCD', 'ANNDATS']), left_on = "CUSIP", right_on = "CUSIP")
#计算股票mar日期和第一次anno的相差天数delta,并转换为int
#第一个event
d_calc['delta'] = (d_calc['DATE'] - d_calc['ANNDATS']).dt.days
#因题目要求计算-5~5天内的,去除所有时间范围以外的数据
d_calc = d_calc.drop(d_calc[abs(d_calc['delta']) > 5].index)
#后续按IRECCD分组,按delta打印表格,所以可以去除日期
#相当于中心化操作
d_calc = d_calc.drop(columns = ['DATE', 'ANNDATS'])
d_calc
CUSIP mar IRECCD delta
27 b’78668010′ -0.008855 1 -3
28 b’78668010′ 0.009342 1 -2
29 b’78668010′ -0.024849 1 -1
30 b’78668010′ -0.000660 1 0
31 b’78668010′ -0.019343 1 3
1025184 b’76122Q10′ 0.046886 1 -1
1025185 b’76122Q10′ -0.029103 1 0
1025186 b’76122Q10′ -0.010368 1 1
1025187 b’76122Q10′ 0.034579 1 2
1025188 b’76122Q10′ 0.131998 1 3
def calc(k):
    #储存题目要求的输出表格
    d_ans = pd.DataFrame(columns = ['days', 'amar', 't', 'n'])
    d_ans['days'] = range(-5,6)
    d_ans.loc[:,:] = 0
    #初始化days为-5~5
    d_ans['days'] = range(-5,6)
    #i计数目前处理到第几列,实际上就是row.index
    i = 0
    #对表格进行循环
    for row in d_ans.iterrows():
        #条件k是按推荐等级分组,delta == days是按距离event天数分组,取出符合条件的mar序列
        d_temp = d_calc[(k) & (d_calc['delta'] == row[1]['days'])]['mar']
        #求average mar
        d_ans.loc[i, 'amar'] = d_temp.dropna().mean()
        #求符合条件的个数n
        df_bool = (k) & (d_calc['delta'] == row[1]['days'])
        d_ans.loc[i, 'n'] = df_bool.sum()
        #对取出的mar序列做t检验,原假设μ = 0
        #注意ttest_1samp输出的[t, p],这里取t
        d_ans.loc[i, 't'] = ttest_1samp(d_temp.dropna(), 0)[0]
        i = i+1
    return d_ans
#分别传入对IRECCD的判断,以分成{1},{2},{3,4,5}三组
print(calc(d_calc['IRECCD'] == 1))
print(calc(d_calc['IRECCD'] == 2))
print(calc(d_calc['IRECCD'] > 2))
    days      amar         t    n
0     -5  0.004576  1.792604  504
1     -4  0.010044  3.246565  405
2     -3  0.009570  2.656499  316
3     -2  0.000710  0.229541  309
4     -1  0.010485  3.910741  500
5      0  0.004649  1.626139  690
6      1  0.002323  1.010052  611
7      2 -0.004036 -1.682755  502
8      3  0.001402  0.587696  411
9      4 -0.001336 -0.463270  316
10     5 -0.002402 -0.759221  309
    days      amar         t    n
0     -5  0.009697  3.146405  474
1     -4  0.014181  3.851839  369
2     -3  0.011604  2.456671  268
3     -2  0.000684  0.216472  281
4     -1 -0.000216 -0.071135  473
5      0  0.003992  1.329914  632
6      1 -0.001126 -0.395615  566
7      2 -0.001162 -0.392480  472
8      3  0.000293  0.089909  375
9      4 -0.007425 -1.801691  270
10     5 -0.000873 -0.236744  287
    days      amar         t   n
0     -5  0.030962  1.548338  23
1     -4 -0.020598 -0.935432  19
2     -3  0.012714  0.891346  19
3     -2  0.004483  0.486314  15
4     -1 -0.017472 -1.948986  23
5      0 -0.039365 -2.226424  33
6      1 -0.036419 -3.233579  29
7      2 -0.008507 -0.515271  24
8      3 -0.011686 -0.890740  20
9      4  0.008818  0.724564  19
10     5 -0.004680 -0.297206  17

3

#第二个event
d_calc = pd.merge(pd.DataFrame(d_merge_ret, columns = ['CUSIP', 'DATE', 'mar']), pd.DataFrame(d_rcm_by_date, columns = ['CUSIP', 'IRECCD', 'Offer_Date']), left_on = "CUSIP", right_on = "CUSIP")
#delta是有数据的日期与Offer_Date+25之差
d_calc['delta'] = (d_calc['DATE'] - d_calc['Offer_Date']).dt.days - 25
#去除不在[-5,5]内的数据
d_calc = d_calc.drop(d_calc[abs(d_calc['delta']) > 5].index)
d_calc = d_calc.drop(columns = ['DATE', 'Offer_Date'])
#第5题不要求对IRECCD进行分组,故直接传入k = True
print(calc(1))
    days      amar         t     n
0     -5  0.002639  1.548408  1172
1     -4  0.007390  4.409964  1329
2     -3  0.012567  7.104972  1129
3     -2  0.009144  3.996107   720
4     -1  0.014305  5.747104   584
5      0  0.007941  3.367472   732
6      1  0.004713  1.973140   915
7      2  0.000736  0.369180  1169
8      3  0.001751  1.047002  1315
9      4 -0.000756 -0.463374  1128
10     5 -0.001592 -0.764718   720

5

#加入Offer_Date 股票发行日
d_rcm_by_date = pd.merge(pd.DataFrame(d_ipo, columns=['CUSIP', 'Offer_Date']), pd.DataFrame(d_rcm, columns=['CUSIP', 'IRECCD', 'ANNDATS']), on = 'CUSIP').drop_duplicates(subset=['CUSIP'], keep='first')
#后续分别以第一次anno、Offer_Date+25为event
d_rcm_by_date
CUSIP Offer_Date IRECCD ANNDATS
0 b’78668010′ 1996-01-11 b’1′ 1996-02-23
5 b’65539810′ 1996-01-18 b’2′ 1996-05-23
9 b’75642210′ 1996-01-22 b’2′ 1996-02-21
25 b’02109K10′ 1996-01-23 b’1′ 1996-03-19
44 b’30271210′ 1996-01-23 b’1′ 1996-02-27
29828 b’84749R10′ 2000-12-07 b’2′ 2001-01-02
29836 b’41690610′ 2000-12-07 b’1′ 2001-02-26
29842 b’02888410′ 2000-12-07 b’1′ 2001-01-03
29851 b’37246C10′ 2000-12-11 b’2′ 2001-04-20
29855 b’76122Q10′ 2000-12-14 b’2′ 2001-01-10

be careful with which is the event!

d_calc = pd.merge(pd.DataFrame(d_merge_ret, columns = ['CUSIP', 'DATE', 'mar']), pd.DataFrame(d_rcm_by_date, columns = ['CUSIP', 'IRECCD', 'Offer_Date', 'ANNDATS']), left_on = "CUSIP", right_on = "CUSIP")
#delta还是用来代表和event的相差天数
d_calc['delta'] = (d_calc['DATE'] - d_calc['Offer_Date']).dt.days - 25
#题目要求判断推荐日anndats是否在距离发行日窗口期[-2,2],以此分组
#设置in_window,在窗口期内的为True
d_calc['in_window'] = (((d_calc['ANNDATS'] - d_calc['Offer_Date']).dt.days - 25 <= 2) & ((d_calc['ANNDATS'] - d_calc['Offer_Date']).dt.days - 25 >= -2))
#与按IRECCD分组同理
print(calc(d_calc['in_window'] == True))
print(calc(d_calc['in_window'] == False))
    days      amar         t    n
0     -5  0.007484  2.365433  441
1     -4  0.013627  3.915532  439
2     -3  0.023586  5.659326  348
3     -2  0.016189  2.719038  169
4     -1  0.029160  4.005638  135
5      0  0.006211  1.283090  258
6      1  0.010800  2.563634  383
7      2 -0.002639 -0.668781  444
8      3  0.001917  0.530497  431
9      4 -0.001357 -0.421846  350
10     5 -0.003517 -0.721198  166
    days      amar         t    n
0     -5 -0.000283 -0.145392  731
1     -4  0.004314  2.378810  890
2     -3  0.007657  4.423083  781
3     -2  0.006983  2.954376  551
4     -1  0.009838  4.184526  449
5      0  0.008883  3.528722  474
6      1  0.000331  0.120076  532
7      2  0.002803  1.322881  725
8      3  0.001670  0.950041  884
9      4 -0.000486 -0.259439  778
10     5 -0.001015 -0.445447  554

5

plt.figure(figsize = (16, 5))
plt.title("Figure 1")
cov = calc(d_calc['in_window'] == True)
un_cov = calc(d_calc['in_window'] == False)
plt.plot(cov.iloc[:,0], cov.iloc[:,1], 'r:', label = 'cov')
plt.plot(un_cov.iloc[:,0], un_cov.iloc[:,1], 'y--', label = 'un_cov')
plt.legend()
plt.show()

Visits: 8

发布者:Janis

,,,

加入对话

1条评论

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据