siat 3.7.1__py3-none-any.whl → 3.7.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- siat/event_study.py +287 -94
- {siat-3.7.1.dist-info → siat-3.7.3.dist-info}/METADATA +1 -1
- {siat-3.7.1.dist-info → siat-3.7.3.dist-info}/RECORD +6 -6
- {siat-3.7.1.dist-info → siat-3.7.3.dist-info}/WHEEL +1 -1
- {siat-3.7.1.dist-info → siat-3.7.3.dist-info}/LICENSE +0 -0
- {siat-3.7.1.dist-info → siat-3.7.3.dist-info}/top_level.txt +0 -0
siat/event_study.py
CHANGED
@@ -43,6 +43,7 @@ if __name__=='__main__':
|
|
43
43
|
start='2024-3-1'; end='2024-4-30'
|
44
44
|
event_window=[1,1] #事件发生时股市已经收盘,故检测下一个交易日的股市反应
|
45
45
|
market_index='000001.SS' #贵州茅台在上交所上市,故使用上证综合指数
|
46
|
+
RF=0
|
46
47
|
|
47
48
|
#测试组2
|
48
49
|
ticker=['600519.SS','399997.SZ']
|
@@ -51,6 +52,17 @@ if __name__=='__main__':
|
|
51
52
|
start='2024-3-1'; end='2024-3-30'
|
52
53
|
event_window=[1,2]
|
53
54
|
market_index='000300.SS'
|
55
|
+
RF="market model"
|
56
|
+
|
57
|
+
#测试组3
|
58
|
+
ticker=['600519.SS','399997.SZ']
|
59
|
+
|
60
|
+
event_date='2024-4-2' #贵州茅台2023年报披露日
|
61
|
+
start='auto'; end='auto'
|
62
|
+
event_window=[0,1]
|
63
|
+
method='CAPM'
|
64
|
+
market_index='000001.SS'
|
65
|
+
RF="1YCNY.B"
|
54
66
|
|
55
67
|
#共同部分
|
56
68
|
post_event_days=7
|
@@ -58,11 +70,11 @@ if __name__=='__main__':
|
|
58
70
|
early_response_days=-2
|
59
71
|
estimation_window_days=-365
|
60
72
|
|
61
|
-
RF=0
|
62
73
|
ret_type="Daily Adj Ret%"
|
63
74
|
ticker_type='stock' #贵州茅台为股票
|
64
75
|
facecolor="whitesmoke"
|
65
|
-
show_AR=
|
76
|
+
show_AR=True
|
77
|
+
show_RF=True
|
66
78
|
loc='best'
|
67
79
|
|
68
80
|
es=event_study("600519.SS",event_date="2024-4-2", \
|
@@ -89,17 +101,18 @@ if __name__=='__main__':
|
|
89
101
|
|
90
102
|
|
91
103
|
def event_study(ticker,event_date, \
|
92
|
-
start='
|
93
|
-
event_window=[
|
94
|
-
post_event_days=
|
104
|
+
start='auto',end='auto', \
|
105
|
+
event_window=[1,3], \
|
106
|
+
post_event_days=0, \
|
95
107
|
method='CAPM', \
|
96
108
|
early_response_days=-2, \
|
97
109
|
estimation_window_days=-365, \
|
98
110
|
market_index='000300.SS', \
|
99
|
-
RF=
|
111
|
+
RF="market index", \
|
100
112
|
ret_type="Daily Adj Ret%", \
|
101
113
|
ticker_type='auto', \
|
102
|
-
facecolor="whitesmoke",show_AR='auto',
|
114
|
+
facecolor="whitesmoke",show_AR='auto',show_RF=False, \
|
115
|
+
loc='best'):
|
103
116
|
"""
|
104
117
|
|
105
118
|
功能:展示事件研究法的累计异常收益率CAR。
|
@@ -113,7 +126,7 @@ def event_study(ticker,event_date, \
|
|
113
126
|
注意:事件窗口不一定包括事件日(适用于事件日在非交易日的情形,例如周末或假日)
|
114
127
|
如果事件日为非交易日,事件窗口需要后移至事件日后的第一个交易日。
|
115
128
|
如果怀疑市场提前对事件发生反应,可以考虑前移事件窗口的开始日期。
|
116
|
-
post_event_days
|
129
|
+
post_event_days:用于分析事件窗口后的漂移效应,取事件窗口后多少天。默认不分析,取0天。
|
117
130
|
method:估计事件窗口以及事件后窗口收益率预期值的方法,默认为CAPM(主要用于ticker为股票等)
|
118
131
|
如果ticker为股票等,也可以直接使用指数的收益率为其预期收益率,此时method为Market或Index。
|
119
132
|
如果ticker为指数,无法再借助指数,method只能使用Random Walk,即使用前一个收益率为预期收益率。
|
@@ -125,12 +138,17 @@ def event_study(ticker,event_date, \
|
|
125
138
|
默认在事件窗口开始日期+提前反应天数前的365个自然日(约250个交易日)。
|
126
139
|
market_index:当method为CAPM时,用于计算市场收益率。默认中国市场采用000300.SS。
|
127
140
|
注意:需要根据不同市场采取不同的市场指数,例如香港市场为恒生指数,美国市场为标普500指数等。
|
141
|
+
RF:年化无风险收益率,默认使用市场模型"market index",无需指定;可直接指定具体数值;
|
142
|
+
也可指定特定指标,例如一年期中国国债收益率"1YCNY.B"或一年期美债收益率"1YUSY.B"等。
|
143
|
+
show_RF:在使用市场模型或指定指标时是否显示计算出的RF均值,默认为False。
|
128
144
|
ticker_type:显式指明ticker的证券类型,当siat误判其类型(中国内地股票/债券/基金)时使用,默认'auto'。
|
129
145
|
facecolor:显式指定绘图背景颜色,默认"whitesmoke"。
|
130
|
-
show_AR:是否绘图时绘制异常收益率AR,默认'auto'(单个ticker
|
146
|
+
show_AR:是否绘图时绘制异常收益率AR,默认'auto'(单个ticker时绘制,多个时不绘制)。
|
147
|
+
也可指定True/False强行绘制/不绘制。
|
131
148
|
"""
|
132
149
|
|
133
150
|
DEBUG=False
|
151
|
+
DEBUG2=False
|
134
152
|
|
135
153
|
#=====事件研究各个日期的计算与调整===========================================
|
136
154
|
if isinstance(event_date,str):
|
@@ -142,34 +160,45 @@ def event_study(ticker,event_date, \
|
|
142
160
|
return None
|
143
161
|
event_date.sort() #升序排序
|
144
162
|
|
145
|
-
|
163
|
+
#事件窗口日期:遇到周末需要调整,提前或顺延至最近的工作日
|
146
164
|
event_window_new=event_window.copy() #列表的普通赋值仅为指针,新列表的改动也会影响原列表
|
147
165
|
adjust_start=0
|
148
166
|
event_window_start=date_adjust(event_date[0],adjust=event_window[0])
|
149
167
|
if week_day(event_window_start) == 5: #周六
|
150
|
-
|
168
|
+
if event_window[0] >= 0:
|
169
|
+
adjust_start=2
|
170
|
+
else:
|
171
|
+
adjust_start=-1
|
151
172
|
elif week_day(event_window_start) == 6: #周日
|
152
|
-
|
173
|
+
if event_window[0] >= 0:
|
174
|
+
adjust_start=1
|
175
|
+
else:
|
176
|
+
adjust_start=-2
|
153
177
|
event_window_start=date_adjust(event_window_start,adjust=adjust_start)
|
154
178
|
event_window_new[0]=event_window[0]+adjust_start
|
155
179
|
|
156
180
|
adjust_end=0
|
157
181
|
event_window_end=date_adjust(event_window_start,adjust=event_window[1]-event_window[0])
|
158
182
|
if week_day(event_window_end) == 5: #周六
|
159
|
-
|
183
|
+
if event_window[1] >= 0:
|
184
|
+
adjust_end=2
|
185
|
+
else:
|
186
|
+
adjust_end=-1
|
160
187
|
elif week_day(event_window_end) == 6: #周日
|
161
|
-
|
188
|
+
if event_window[1] >= 0:
|
189
|
+
adjust_end=1
|
190
|
+
else:
|
191
|
+
adjust_end=-2
|
162
192
|
event_window_end=date_adjust(event_window_end,adjust=adjust_end)
|
163
193
|
event_window_new[1]=event_window[1]+adjust_start+adjust_end
|
164
|
-
|
165
194
|
|
166
195
|
if DEBUG:
|
167
196
|
print(" DEBUG: event window is between {0} to {1}".format(event_window_start,event_window_end))
|
168
197
|
|
169
198
|
if event_window_new != event_window:
|
170
|
-
print(" #Notice: event window adjusted to {} because of
|
199
|
+
print(" #Notice: event window adjusted from {0} to {1} because of weekend".format(event_window,event_window_new))
|
171
200
|
|
172
|
-
|
201
|
+
#事件后窗口日期
|
173
202
|
post_event_start=date_adjust(event_window_end,adjust=0)
|
174
203
|
if week_day(post_event_start) == 5: #周六
|
175
204
|
post_event_start=date_adjust(post_event_start,adjust=2)
|
@@ -189,7 +218,7 @@ def event_study(ticker,event_date, \
|
|
189
218
|
print(" DEBUG: post event window is between {0} to {1}".format(post_event_start,post_event_end))
|
190
219
|
|
191
220
|
|
192
|
-
|
221
|
+
#事件窗口前日期
|
193
222
|
event_eve_date=date_adjust(event_window_start,adjust=-1)
|
194
223
|
if week_day(event_eve_date) == 5: #周六
|
195
224
|
event_eve_date=date_adjust(event_eve_date,adjust=-1)
|
@@ -199,7 +228,7 @@ def event_study(ticker,event_date, \
|
|
199
228
|
if DEBUG:
|
200
229
|
print(" DEBUG: event eve is on {}".format(event_eve_date))
|
201
230
|
|
202
|
-
|
231
|
+
#提前反应日期
|
203
232
|
early_response_date=date_adjust(event_date[0],adjust=early_response_days)
|
204
233
|
if week_day(early_response_date) == 5: #周六
|
205
234
|
early_response_date=date_adjust(early_response_date,adjust=-1)
|
@@ -211,7 +240,18 @@ def event_study(ticker,event_date, \
|
|
211
240
|
|
212
241
|
#估计窗口日期的计算
|
213
242
|
est_window_end=date_adjust(early_response_date,adjust=-1)
|
214
|
-
est_window_start=date_adjust(est_window_end,adjust=estimation_window_days)
|
243
|
+
est_window_start=date_adjust(est_window_end,adjust=estimation_window_days)
|
244
|
+
if DEBUG:
|
245
|
+
print(" DEBUG: regression period starts from {0} to {1}".format(est_window_start,est_window_end))
|
246
|
+
|
247
|
+
#处理绘图时显示的日期范围
|
248
|
+
if start=='auto':
|
249
|
+
start=date_adjust(early_response_date,adjust=-7)
|
250
|
+
if end=='auto':
|
251
|
+
if len(ticker) == 1 or show_AR:
|
252
|
+
end=date_adjust(post_event_end,adjust=7)
|
253
|
+
else:
|
254
|
+
end=date_adjust(post_event_end,adjust=2)
|
215
255
|
|
216
256
|
#=====判断ticker是否为指数,调整预期收益率计算方法============================
|
217
257
|
if isinstance(ticker,str):
|
@@ -223,45 +263,84 @@ def event_study(ticker,event_date, \
|
|
223
263
|
return None
|
224
264
|
|
225
265
|
if market_index in ticker:
|
226
|
-
print(" #Warning(event_study): market_index {0} in
|
266
|
+
print(" #Warning(event_study): market_index {0} duplicated in and removed from ticker {1}".format(market_index,ticker))
|
227
267
|
ticker.remove(market_index)
|
228
268
|
|
229
|
-
tname=ticker_name(ticker[0],ticker_type)
|
269
|
+
#tname=ticker_name(ticker[0],ticker_type)
|
230
270
|
#检查ticker是否为指数或国债收益率
|
231
271
|
"""
|
232
272
|
if ("指数" in tname or "index" in tname.lower()) or ("收益率" in tname or "yield" in tname.lower()):
|
233
273
|
if not ("random" in method.lower() or "walk" in method.lower()):
|
234
274
|
print(" #Notice: check the applicability of ticker {0}, method {1} with market index {2}".format(ticker[0],method,market_index))
|
235
275
|
"""
|
276
|
+
|
236
277
|
#=====获取证券价格和/或相关指数数据==========================================
|
237
|
-
|
278
|
+
#基于CAPM获取数据
|
279
|
+
if 'capm' in method.lower():
|
280
|
+
method_type="capm"
|
281
|
+
df_ret=compare_msecurity(tickers=ticker+[market_index],measure=ret_type, \
|
282
|
+
start=est_window_start,end=end, \
|
283
|
+
ticker_type=ticker_type, \
|
284
|
+
graph=False)
|
285
|
+
|
286
|
+
if isinstance(RF,int) or isinstance(RF,float):
|
287
|
+
#RF为具体数值
|
288
|
+
RF_type="value"
|
289
|
+
|
290
|
+
elif "market" in (str(RF)).lower() or "index" in (str(RF)).lower():
|
291
|
+
#RF通过市场模型计算,无需指定
|
292
|
+
RF_type="model"
|
293
|
+
else:
|
294
|
+
#指定RF代码,例如1YCNY.B,注意1:得到的是年化收益率%,注意2:中国的只有近一年的数据
|
295
|
+
RF_type="code"
|
296
|
+
|
297
|
+
if RF_type=="code":
|
298
|
+
df_rf=compare_msecurity(tickers=RF,measure='Close', \
|
299
|
+
start=est_window_start,end=end, \
|
300
|
+
graph=False)
|
301
|
+
RF=df_rf[list(df_rf)[0]].mean() / 100.0
|
302
|
+
|
303
|
+
#基于市场指数获取数据
|
304
|
+
elif 'market' in method.lower() or 'index' in method.lower():
|
305
|
+
method_type="market"
|
238
306
|
df_ret=compare_msecurity(tickers=ticker+[market_index],measure=ret_type, \
|
239
307
|
start=est_window_start,end=end, \
|
240
308
|
ticker_type=ticker_type, \
|
241
309
|
graph=False)
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
"""
|
246
|
-
if 'random' in method.lower() or 'walk' in method.lower():
|
310
|
+
|
311
|
+
elif 'random' in method.lower() or 'walk' in method.lower():
|
312
|
+
method_type="random"
|
247
313
|
df_ret=compare_msecurity(tickers=ticker,measure=ret_type, \
|
248
314
|
start=est_window_start,end=end, \
|
249
315
|
ticker_type=ticker_type, \
|
250
316
|
graph=False)
|
251
317
|
for t in ticker_name(ticker,ticker_type):
|
252
|
-
|
253
|
-
|
318
|
+
try:
|
319
|
+
df_ret[t+"_predicted"]=df_ret[t].shift(1)
|
320
|
+
except:
|
321
|
+
#print(" #Warning(event_study): info not found for ticker {}".format(t))
|
322
|
+
continue
|
323
|
+
|
324
|
+
else:
|
325
|
+
print(" #Warning(event_study): unexpected type of AR method {}".format(method))
|
326
|
+
return None
|
254
327
|
|
255
328
|
#=====计算异常收益率AR=====
|
256
329
|
df_cols=list(df_ret)
|
257
|
-
if 'market'
|
330
|
+
if method_type=='market':
|
258
331
|
for t in ticker_name(ticker,ticker_type):
|
259
|
-
|
260
|
-
|
332
|
+
try:
|
333
|
+
df_ret[t+'_AR']=df_ret[t] - df_ret[ticker_name(market_index)]
|
334
|
+
except: continue
|
335
|
+
|
336
|
+
elif method_type=='random':
|
261
337
|
for t in ticker_name(ticker,ticker_type):
|
262
|
-
|
338
|
+
try:
|
339
|
+
df_ret[t+'_AR']=df_ret[t] - df_ret[t+"_predicted"]
|
340
|
+
except: continue
|
341
|
+
|
263
342
|
else: #按CAPM计算
|
264
|
-
|
343
|
+
#CAPM回归期间数据
|
265
344
|
est_window_startpd=pd.to_datetime(est_window_start)
|
266
345
|
est_window_endpd =pd.to_datetime(est_window_end)
|
267
346
|
df_reg=df_ret[(df_ret.index >=est_window_startpd) & (df_ret.index <=est_window_endpd)].copy()
|
@@ -270,49 +349,111 @@ def event_study(ticker,event_date, \
|
|
270
349
|
df_reg=df_reg.replace([np.nan, None], np.nan).dropna()
|
271
350
|
|
272
351
|
import statsmodels.api as sm
|
273
|
-
|
274
|
-
|
352
|
+
if RF_type in ["value","code"]:
|
353
|
+
if not ("%" in ret_type): #注意:RF是年化收益率(需要转化为日收益率),这里不是百分比
|
354
|
+
X=df_reg[ticker_name(market_index)] - RF/365.0 #无截距项回归,指定RF具体数值
|
355
|
+
else:
|
356
|
+
X=df_reg[ticker_name(market_index)] - RF/365.0 * 100.0 #这里需要转化为日收益率百分比%
|
357
|
+
|
358
|
+
else: #RF_type=="model"
|
359
|
+
X=df_reg[ticker_name(market_index)]
|
360
|
+
X=sm.add_constant(X) #有截距项回归,基于市场模型
|
361
|
+
|
362
|
+
if DEBUG:
|
363
|
+
print(" DEBUG: method_type={0}, RF_type={1}, RF={2}".format(method_type,RF_type,RF))
|
364
|
+
|
365
|
+
#CAPM回归
|
366
|
+
beta_dict={}; intercept_dict={}; pvalue_dict={}; rf_dict={}
|
275
367
|
for t in ticker_name(ticker,ticker_type):
|
276
|
-
|
277
|
-
|
368
|
+
try:
|
369
|
+
if RF_type in ["value","code"]:
|
370
|
+
if not ("%" in ret_type): #注意:RF是年化收益率(需要转化为日收益率),不是百分比
|
371
|
+
y=df_reg[t] - RF/365.0
|
372
|
+
else:
|
373
|
+
y=df_reg[t] - RF/365.0 * 100.0
|
374
|
+
|
375
|
+
else: #RF_type=="model"
|
376
|
+
y=df_reg[t]
|
377
|
+
except: continue
|
378
|
+
|
379
|
+
model = sm.OLS(y,X) #定义回归模型y=X
|
278
380
|
results = model.fit() #进行OLS回归
|
279
|
-
beta=results.params[0] #提取回归系数
|
280
381
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
382
|
+
if DEBUG2:
|
383
|
+
print(" DEBUG: RF_type={0}, results.params={1},results.pvalues={2}".format(RF_type,results.params,results.pvalues))
|
384
|
+
|
385
|
+
#提取回归系数,详细信息见results.summary()
|
386
|
+
if RF_type=="model":
|
387
|
+
intercept=results.params[0]
|
388
|
+
beta=results.params[1]; pvalue=results.pvalues[1]
|
389
|
+
try:
|
390
|
+
#此处回归得到的rf应该为日收益率,转为年化收益率。
|
391
|
+
#注意:不同证券回归出的结果可能差异较大,原因可能是混入了回归残差!
|
392
|
+
if not ("%" in ret_type):
|
393
|
+
rf=intercept / (1-beta) * 365.0
|
394
|
+
else:
|
395
|
+
rf=intercept / (1-beta) / 100.0 * 365.0
|
396
|
+
except: rf=0
|
397
|
+
|
398
|
+
else: #RF_type in ["value","code"]
|
399
|
+
intercept=0
|
400
|
+
beta=results.params[0]; pvalue=results.pvalues[0]
|
401
|
+
rf=RF
|
402
|
+
|
403
|
+
beta_dict[t] = beta; intercept_dict[t] = intercept; pvalue_dict[t] = pvalue; rf_dict[t]=rf
|
404
|
+
if DEBUG2:
|
405
|
+
print(" DEBUG: t={0}, intercept={1}, beta={2}, pvalue={3}, annualized rf={4}".format(t,round(intercept,4),round(beta,4),round(pvalue,4),round(rf,4)))
|
289
406
|
|
407
|
+
#计算收益率预期和AR
|
290
408
|
for t in ticker_name(ticker,ticker_type):
|
291
|
-
|
292
|
-
|
409
|
+
try:
|
410
|
+
if RF_type in ["value","code"]:
|
411
|
+
#CAPM模型:E(R) = RF + (Rm-RF)*beta
|
412
|
+
RF_text=str(round(RF*100.0,4))[:6]+'%'
|
413
|
+
if not ("%" in ret_type): #注意:RF是年化收益率,此处不是百分比
|
414
|
+
df_ret[t+"_predicted"]=(df_ret[ticker_name(market_index)] - RF/365.0)*beta_dict[t] + RF/365.0
|
415
|
+
else:
|
416
|
+
df_ret[t+"_predicted"]=(df_ret[ticker_name(market_index)] - RF*100.0/365.0)*beta_dict[t] + RF*100.0/365.0
|
417
|
+
|
418
|
+
else: #RF_type=="model"
|
419
|
+
#市场模型:E(R) = intercept + Rm*beta
|
420
|
+
RF_text="基于市场模型回归"
|
421
|
+
df_ret[t+"_predicted"]=df_ret[ticker_name(market_index)]*beta_dict[t] + intercept_dict[t]
|
422
|
+
|
423
|
+
df_ret[t+"_AR"]=df_ret[t] - df_ret[t+"_predicted"]
|
424
|
+
except: continue
|
293
425
|
|
294
|
-
|
426
|
+
if DEBUG2:
|
427
|
+
print(" DEBUG: RF_type={0}, RF_text={1}, rf_dict={2}".format(RF_type,RF_text, rf_dict))
|
428
|
+
|
429
|
+
#=====计算CAR==============================================================
|
295
430
|
for t in ticker_name(ticker,ticker_type):
|
296
|
-
|
431
|
+
try:
|
432
|
+
df_ret[t+"_CAR"]=0
|
433
|
+
except: continue
|
434
|
+
|
297
435
|
event_window_startpd=pd.to_datetime(event_window_start)
|
298
436
|
event_window_endpd=pd.to_datetime(event_window_end)
|
299
437
|
post_event_endpd=pd.to_datetime(post_event_end)
|
300
|
-
|
301
438
|
startpd=pd.to_datetime(start); endpd=pd.to_datetime(end)
|
302
|
-
|
439
|
+
|
440
|
+
#计算CAR
|
303
441
|
df_ret_event=df_ret[(df_ret.index >=event_window_startpd) & (df_ret.index <=endpd)]
|
304
442
|
for t in ticker_name(ticker,ticker_type):
|
305
|
-
|
443
|
+
try:
|
444
|
+
df_ret_event[t+'_CAR'] = df_ret_event[t+'_AR'].cumsum()
|
445
|
+
except: continue
|
306
446
|
|
447
|
+
#合成事件前期间
|
307
448
|
df_ret_before_event=df_ret[(df_ret.index >=startpd) & (df_ret.index < event_window_startpd)]
|
308
449
|
for t in ticker_name(ticker,ticker_type):
|
309
|
-
|
450
|
+
try:
|
451
|
+
df_ret_before_event[t+'_CAR']=np.nan
|
452
|
+
except: continue
|
310
453
|
|
311
|
-
#df_ret_after_event=df_ret[(df_ret.index >post_event_endpd)]
|
312
|
-
#df_ret_after_event['CAR']=np.nan
|
313
|
-
|
314
|
-
#df_show=pd.concat([df_ret_before_event,df_ret_event,df_ret_after_event])
|
315
454
|
df_show=pd.concat([df_ret_before_event,df_ret_event])
|
455
|
+
|
456
|
+
#是否显示AR:默认单证券显示,多证券不显示
|
316
457
|
df_show_cols=[]
|
317
458
|
for c in list(df_show):
|
318
459
|
if show_AR=='auto':
|
@@ -339,28 +480,38 @@ def event_study(ticker,event_date, \
|
|
339
480
|
|
340
481
|
y_label="收益率%"
|
341
482
|
|
342
|
-
|
483
|
+
#横轴注释
|
343
484
|
footnote1="首事件日{0},事件窗口{1},事件后窗口天数{2},市场提前反应天数{3}".format(event_date[0],event_window_new,post_event_days,early_response_days)
|
344
485
|
footnote2="收益率类型:"+ectranslate(ret_type)
|
345
486
|
|
346
|
-
if
|
347
|
-
method_name="
|
348
|
-
elif
|
349
|
-
method_name="
|
487
|
+
if method_type == "market":
|
488
|
+
method_name="市场指数基准"
|
489
|
+
elif method_type == "random":
|
490
|
+
method_name="随机漫步模型"
|
350
491
|
else:
|
351
|
-
method_name="CAPM"
|
492
|
+
method_name="CAPM模型"
|
352
493
|
|
353
494
|
footnote3=",收益率预期方法:"+method_name
|
354
|
-
if not
|
495
|
+
if not method_type == "random":
|
355
496
|
footnote4=',市场指数:'+ticker_name(market_index)
|
356
497
|
else:
|
357
498
|
footnote4=''
|
358
499
|
|
359
|
-
#显著性检验:异于零的t
|
500
|
+
#显著性检验:异于零的t检验,事件窗口
|
360
501
|
df_event_window=df0[(df0.index >=event_window_start) & (df0.index <=event_window_end)]
|
361
|
-
footnote5="事件窗口CAR
|
502
|
+
footnote5="事件窗口CAR(终值,p值):"
|
362
503
|
for c in list(df_event_window):
|
363
504
|
if 'CAR' in c.upper():
|
505
|
+
c_name=c[:-4]
|
506
|
+
|
507
|
+
event_window_endpd=pd.to_datetime(event_window_end)
|
508
|
+
#car_value=df_event_window[df_event_window.index == event_window_endpd][c].values[0]
|
509
|
+
car_value=df_event_window[c][-1]
|
510
|
+
if car_value > 0:
|
511
|
+
car_value_str=str(round(car_value,4))[:6]
|
512
|
+
else:
|
513
|
+
car_value_str=str(round(car_value,4))[:7]
|
514
|
+
|
364
515
|
if len(df_event_window[c])==1:
|
365
516
|
if abs(df_event_window[c].values[0]) > 0.01:
|
366
517
|
p_value=0.0
|
@@ -368,22 +519,51 @@ def event_study(ticker,event_date, \
|
|
368
519
|
p_value=1.0
|
369
520
|
else:
|
370
521
|
p_value=ttest(df_event_window[c],0)
|
371
|
-
|
372
|
-
|
522
|
+
if p_value > 0:
|
523
|
+
p_value_str=str(round(p_value,4))[:6]
|
524
|
+
else:
|
525
|
+
p_value_str=str(round(p_value,4))[:7]
|
526
|
+
#footnote5=footnote5+c_name+p_value_str+","
|
527
|
+
footnote5=footnote5+"{0}({1}, {2}), ".format(c_name,car_value_str,p_value_str)
|
528
|
+
footnote5=footnote5.strip(", ")
|
373
529
|
|
530
|
+
#显著性检验:异于零的t检验,事件后窗口
|
374
531
|
df_post_event_window=df0[(df0.index >event_window_end) & (df0.index <=post_event_end)]
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
532
|
+
if len(df_post_event_window) == 0:
|
533
|
+
footnote6=''
|
534
|
+
|
535
|
+
elif len(df_post_event_window) == 0:
|
536
|
+
footnote6=''
|
537
|
+
else:
|
538
|
+
footnote6="事件后窗口CAR(终值,p值):"
|
539
|
+
for c in list(df_post_event_window):
|
540
|
+
if 'CAR' in c.upper():
|
541
|
+
c_name=c[:-4]
|
542
|
+
post_event_endpd=pd.to_datetime(post_event_end)
|
543
|
+
if DEBUG2:
|
544
|
+
print(" DEBUG: c={0},post_event_end={1},df_post_event_window={2}".format(c,post_event_end,df_post_event_window))
|
545
|
+
#car_value=df_post_event_window[df_post_event_window.index == post_event_endpd][c].values[0]
|
546
|
+
car_value=df_post_event_window[c][-1]
|
547
|
+
if car_value > 0:
|
548
|
+
car_value_str=str(round(car_value,4))[:6]
|
381
549
|
else:
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
550
|
+
car_value_str=str(round(car_value,4))[:7]
|
551
|
+
|
552
|
+
if len(df_post_event_window[c])==1:
|
553
|
+
if abs(df_post_event_window[c].values[0]) > 0.01:
|
554
|
+
p_value=0.0
|
555
|
+
else:
|
556
|
+
p_value=1.0
|
557
|
+
else:
|
558
|
+
p_value=ttest(df_post_event_window[c],0)
|
559
|
+
if p_value > 0:
|
560
|
+
p_value_str=str(round(p_value,4))[:6]
|
561
|
+
else:
|
562
|
+
p_value_str=str(round(p_value,4))[:7]
|
563
|
+
|
564
|
+
#footnote6=footnote6+c[:-4]+str(p_value)[:6]+","
|
565
|
+
footnote6=footnote6+"{0}({1}, {2}), ".format(c_name,car_value_str,p_value_str)
|
566
|
+
footnote6=footnote6.strip(", ")
|
387
567
|
|
388
568
|
footnote7="数据来源:Sina/EM/Yahoo/Stooq/SWHY,"+stoday
|
389
569
|
|
@@ -467,20 +647,25 @@ def event_study(ticker,event_date, \
|
|
467
647
|
event_text="事件日"+str(pos+1)
|
468
648
|
df1["事件标记"]=df1.apply(lambda x: event_text if x["日期"]==d else x["事件标记"],axis=1)
|
469
649
|
|
470
|
-
event_text=",事件窗口开始日"
|
650
|
+
#event_text=",事件窗口开始日"
|
651
|
+
event_text="\n事件窗开始"
|
471
652
|
df1["事件标记"]=df1.apply(lambda x: x["事件标记"]+event_text if x["日期"]==event_window_start else x["事件标记"],axis=1)
|
472
|
-
event_text=",事件窗口结束日"
|
653
|
+
#event_text=",事件窗口结束日"
|
654
|
+
event_text="\n事件窗结束"
|
473
655
|
df1["事件标记"]=df1.apply(lambda x: x["事件标记"]+event_text if x["日期"]==event_window_end else x["事件标记"],axis=1)
|
474
|
-
|
475
|
-
|
656
|
+
|
657
|
+
#event_text=",事件后窗口结束日"
|
658
|
+
if post_event_end > event_window_end:
|
659
|
+
event_text="\n事件后窗结束"
|
660
|
+
df1["事件标记"]=df1.apply(lambda x: x["事件标记"]+event_text if x["日期"]==post_event_end else x["事件标记"],axis=1)
|
476
661
|
|
477
|
-
event_text="
|
662
|
+
event_text="\n事件窗"
|
478
663
|
df1["事件标记"]=df1.apply(lambda x: x["事件标记"]+event_text if (x["日期"] > event_window_start) and (x["日期"] < event_window_end) else x["事件标记"],axis=1)
|
479
664
|
|
480
|
-
event_text="
|
665
|
+
event_text="\n事件后窗"
|
481
666
|
df1["事件标记"]=df1.apply(lambda x: x["事件标记"]+event_text if (x["日期"] > event_window_end) and (x["日期"] < post_event_end) else x["事件标记"],axis=1)
|
482
667
|
|
483
|
-
df1["事件标记"]=df1["事件标记"].apply(lambda x: x.strip('
|
668
|
+
df1["事件标记"]=df1["事件标记"].apply(lambda x: x.strip('\n'))
|
484
669
|
|
485
670
|
#显示表格
|
486
671
|
df0_list=list(df0)
|
@@ -489,18 +674,26 @@ def event_study(ticker,event_date, \
|
|
489
674
|
#title_txt=title_txt+",窗口收益率"
|
490
675
|
|
491
676
|
if "CAPM" in method.upper():
|
492
|
-
footnotex="CAPM回归期间:{0}至{1}".format(est_window_start,est_window_end)
|
677
|
+
footnotex="CAPM回归期间:{0}至{1},无风险收益率{2}".format(est_window_start,est_window_end,RF_text)
|
493
678
|
footnotey="CAPM贝塔系数:"
|
494
679
|
for k in beta_dict:
|
495
680
|
footnotey=footnotey+k+str(round(beta_dict[k],4))[:6]+","
|
496
681
|
footnotey=footnotey.strip(",")
|
497
|
-
|
498
|
-
|
682
|
+
|
683
|
+
if show_RF:
|
684
|
+
footnotez="无风险收益率均值:"
|
685
|
+
for r in rf_dict:
|
686
|
+
footnotez=footnotez+r+str(round(rf_dict[r]*100.0,4))[:6]+"%, "
|
687
|
+
|
688
|
+
footnotez=footnotez.strip(", ")
|
689
|
+
footnote=footnote2+footnote3+footnote4+'\n'+footnotex+'\n'+footnotey+'\n'+footnotez+'\n'+footnote5+'\n'+footnote6
|
690
|
+
else:
|
691
|
+
footnote=footnote2+footnote3+footnote4+'\n'+footnotex+'\n'+footnotey+'\n'+footnote5+'\n'+footnote6
|
499
692
|
else:
|
500
693
|
footnote=footnote2+footnote3+footnote4+'\n'+footnote5+'\n'+footnote6
|
501
694
|
|
502
|
-
|
503
|
-
df_display_CSS(df1,titletxt=title_txt,footnote=footnote,facecolor=
|
695
|
+
#显示结果表格
|
696
|
+
df_display_CSS(df1,titletxt=title_txt,footnote=footnote,facecolor=facecolor,decimals=4, \
|
504
697
|
first_col_align='left',second_col_align='left', \
|
505
698
|
last_col_align='center',other_col_align='center')
|
506
699
|
|
@@ -32,7 +32,7 @@ siat/economy.py,sha256=ijMAVA5ydghbQDgNDDdz8fz9NPd2eq90RzpJSRGWz5c,78638
|
|
32
32
|
siat/economy_test.py,sha256=6vjNlPz7W125pJb7simCddobSEp3jmLIMvVkLRZ7zW8,13339
|
33
33
|
siat/esg.py,sha256=GMhaonIKtvOK83rhpQUH5aJt2OL3HQBSVfD__Yw-0oo,19040
|
34
34
|
siat/esg_test.py,sha256=Z9m6GUt8O7oHZSEG9aDYpGdvvrv2AiRJdHTiU6jqmZ0,2944
|
35
|
-
siat/event_study.py,sha256=
|
35
|
+
siat/event_study.py,sha256=Q_AdnJzxxL6udCjn5LP6rdhOngsDRWPu5udFkK1CJZw,32223
|
36
36
|
siat/exchange_bond_china.pickle,sha256=zDqdPrFacQ0nqjP_SuF6Yy87EgijIRsFvFroW7FAYYY,1265092
|
37
37
|
siat/fama_french.py,sha256=aUTC-67t_CEPbLk4u79woW_zfZ7OCP6Fo4z5EdWCSkQ,48051
|
38
38
|
siat/fama_french_test.py,sha256=M4O23lBKsJxhWHRluwCb3l7HSEn3OFTjzGMpehcevRg,4678
|
@@ -141,8 +141,8 @@ siat/valuation_china.py,sha256=CVp1IwIsF3Om0J29RGkyxZLt4n9Ug-ua_RKhLwL9fUQ,69624
|
|
141
141
|
siat/valuation_market_china_test.py,sha256=gbJ0ioauuo4koTPH6WKUkqcXiQPafnbhU5eKJ6lpdLA,1571
|
142
142
|
siat/var_model_validation.py,sha256=R0caWnuZarrRg9939hxh3vJIIpIyPfvelYmzFNZtPbo,14910
|
143
143
|
siat/yf_name.py,sha256=r0Q67cSMMlfebEkI9h9pdGlJCooEq7hw_3M5IUs4cSI,20081
|
144
|
-
siat-3.7.
|
145
|
-
siat-3.7.
|
146
|
-
siat-3.7.
|
147
|
-
siat-3.7.
|
148
|
-
siat-3.7.
|
144
|
+
siat-3.7.3.dist-info/LICENSE,sha256=NTEMMROY9_4U1szoKC3N2BLHcDd_o5uTgqdVH8tbApw,1071
|
145
|
+
siat-3.7.3.dist-info/METADATA,sha256=CTwHox9UQtS4g3RRebTz5dH5GgC62g4WOxJr8mr1tEo,8009
|
146
|
+
siat-3.7.3.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
147
|
+
siat-3.7.3.dist-info/top_level.txt,sha256=r1cVyL7AIKqeAmEJjNR8FMT20OmEzufDstC2gv3NvEY,5
|
148
|
+
siat-3.7.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|