siat 3.6.8__py3-none-any.whl → 3.7.2__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/allin.py +3 -0
- siat/common.py +71 -1
- siat/event_study.py +647 -0
- siat/grafix.py +39 -12
- {siat-3.6.8.dist-info → siat-3.7.2.dist-info}/METADATA +1 -1
- {siat-3.6.8.dist-info → siat-3.7.2.dist-info}/RECORD +9 -8
- {siat-3.6.8.dist-info → siat-3.7.2.dist-info}/LICENSE +0 -0
- {siat-3.6.8.dist-info → siat-3.7.2.dist-info}/WHEEL +0 -0
- {siat-3.6.8.dist-info → siat-3.7.2.dist-info}/top_level.txt +0 -0
siat/allin.py
CHANGED
siat/common.py
CHANGED
@@ -3943,6 +3943,24 @@ def df_swap_columns(df, col1, col2):
|
|
3943
3943
|
cols[i2], cols[i1] = cols[i1], cols[i2]
|
3944
3944
|
|
3945
3945
|
return df[cols]
|
3946
|
+
|
3947
|
+
#==============================================================================
|
3948
|
+
if __name__=='__main__':
|
3949
|
+
adate="2024-6-8"
|
3950
|
+
week_day(adate)
|
3951
|
+
week_day("2024-11-16")
|
3952
|
+
week_day("2024-11-17")
|
3953
|
+
|
3954
|
+
def week_day(adate):
|
3955
|
+
import pandas as pd
|
3956
|
+
try:
|
3957
|
+
datepd=pd.to_datetime(adate)
|
3958
|
+
except:
|
3959
|
+
return 0,False
|
3960
|
+
|
3961
|
+
weekday = datepd.weekday()
|
3962
|
+
return weekday # 周六和周日的索引值分别为5和6
|
3963
|
+
|
3946
3964
|
#==============================================================================
|
3947
3965
|
if __name__=='__main__':
|
3948
3966
|
adate="2024-6-8"
|
@@ -3956,7 +3974,7 @@ def is_weekend(adate):
|
|
3956
3974
|
return False
|
3957
3975
|
|
3958
3976
|
weekday = datepd.weekday()
|
3959
|
-
return weekday == 5 or weekday == 6 #
|
3977
|
+
return weekday == 5 or weekday == 6 # 周六和周日的索引值分别为5和6
|
3960
3978
|
|
3961
3979
|
|
3962
3980
|
#==============================================================================
|
@@ -4410,3 +4428,55 @@ def annual_compound_growth(df,column="Close"):
|
|
4410
4428
|
return
|
4411
4429
|
#==============================================================================
|
4412
4430
|
#==============================================================================
|
4431
|
+
if __name__ == '__main__':
|
4432
|
+
sample1=[1,2,3]
|
4433
|
+
sample2=0
|
4434
|
+
|
4435
|
+
ttest(sample1,sample2)
|
4436
|
+
|
4437
|
+
|
4438
|
+
def ttest(sample1,sample2):
|
4439
|
+
"""
|
4440
|
+
|
4441
|
+
功能:对比sample1与sample2之间是否存在显著差异,配对学生检验
|
4442
|
+
sample1:可为数值型的列表或序列,不可为空
|
4443
|
+
sample2:可为数值型的列表或序列或单个数值,若为列表或序列需与sample1个数相同
|
4444
|
+
"""
|
4445
|
+
import pandas as pd
|
4446
|
+
import numpy as np
|
4447
|
+
from scipy import stats
|
4448
|
+
|
4449
|
+
#检查与预处理
|
4450
|
+
if not (isinstance(sample1,list) or isinstance(sample1,pd.Series)):
|
4451
|
+
print(" #Error(ttest): sample1 must be a list or series",sample1)
|
4452
|
+
return None
|
4453
|
+
|
4454
|
+
if not (isinstance(sample2,list) or isinstance(sample2,pd.Series)):
|
4455
|
+
if isinstance(sample2,int) or isinstance(sample2,float):
|
4456
|
+
sample2=[sample2]
|
4457
|
+
sample2=[item for s in sample2 for item in [s]*len(sample1)]
|
4458
|
+
else:
|
4459
|
+
print(" #Error(ttest): sample2 must be a list or series or a value",sample2)
|
4460
|
+
return None
|
4461
|
+
|
4462
|
+
|
4463
|
+
# 转换样本数据
|
4464
|
+
sample1 = pd.Series(sample1)
|
4465
|
+
sample1=sample1.astype(float)
|
4466
|
+
sample1 = np.array(sample1)
|
4467
|
+
|
4468
|
+
sample2 = pd.Series(sample2)
|
4469
|
+
sample2=sample2.astype(float)
|
4470
|
+
sample2 = np.array(sample2)
|
4471
|
+
|
4472
|
+
# 执行t检验
|
4473
|
+
t_stat, p_value = stats.ttest_ind(sample1, sample2)
|
4474
|
+
|
4475
|
+
return round(p_value,4)
|
4476
|
+
|
4477
|
+
#==============================================================================
|
4478
|
+
#==============================================================================
|
4479
|
+
#==============================================================================
|
4480
|
+
#==============================================================================
|
4481
|
+
#==============================================================================
|
4482
|
+
#==============================================================================
|
siat/event_study.py
ADDED
@@ -0,0 +1,647 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
本模块功能:证券事件分析法
|
4
|
+
所属工具包:证券投资分析工具SIAT
|
5
|
+
SIAT:Security Investment Analysis Tool
|
6
|
+
创建日期:2024年11月14日
|
7
|
+
最新修订日期:
|
8
|
+
作者:王德宏 (WANG Dehong, Peter)
|
9
|
+
作者单位:北京外国语大学国际商学院
|
10
|
+
作者邮件:wdehong2000@163.com
|
11
|
+
版权所有:王德宏
|
12
|
+
用途限制:仅限研究与教学使用!
|
13
|
+
特别声明:作者不对使用本工具进行证券投资导致的任何损益负责!
|
14
|
+
"""
|
15
|
+
|
16
|
+
#==============================================================================
|
17
|
+
#关闭所有警告
|
18
|
+
import warnings; warnings.filterwarnings('ignore')
|
19
|
+
#==============================================================================
|
20
|
+
from siat.common import *
|
21
|
+
from siat.translate import *
|
22
|
+
#from siat.security_trend2 import *
|
23
|
+
|
24
|
+
from siat.stock import *
|
25
|
+
#from siat.security_prices import *
|
26
|
+
#from siat.security_price2 import *
|
27
|
+
#from siat.capm_beta2 import *
|
28
|
+
#from siat.risk_adjusted_return2 import *
|
29
|
+
#from siat.valuation import *
|
30
|
+
|
31
|
+
from siat.grafix import *
|
32
|
+
|
33
|
+
import pandas as pd; import numpy as np
|
34
|
+
|
35
|
+
import datetime as dt; stoday=str(dt.date.today())
|
36
|
+
#==============================================================================
|
37
|
+
#==============================================================================
|
38
|
+
if __name__=='__main__':
|
39
|
+
#测试组1
|
40
|
+
ticker='600519.SS'
|
41
|
+
|
42
|
+
event_date='2024-4-2' #贵州茅台2023年报于2024年4月2日晚披露
|
43
|
+
start='2024-3-1'; end='2024-4-30'
|
44
|
+
event_window=[1,1] #事件发生时股市已经收盘,故检测下一个交易日的股市反应
|
45
|
+
market_index='000001.SS' #贵州茅台在上交所上市,故使用上证综合指数
|
46
|
+
RF=0
|
47
|
+
|
48
|
+
#测试组2
|
49
|
+
ticker=['600519.SS','399997.SZ']
|
50
|
+
|
51
|
+
event_date='2024-3-15' #315晚会
|
52
|
+
start='2024-3-1'; end='2024-3-30'
|
53
|
+
event_window=[1,2]
|
54
|
+
market_index='000300.SS'
|
55
|
+
RF="market model"
|
56
|
+
|
57
|
+
#共同部分
|
58
|
+
post_event_days=7
|
59
|
+
method='CAPM'
|
60
|
+
early_response_days=-2
|
61
|
+
estimation_window_days=-365
|
62
|
+
|
63
|
+
ret_type="Daily Adj Ret%"
|
64
|
+
ticker_type='stock' #贵州茅台为股票
|
65
|
+
facecolor="whitesmoke"
|
66
|
+
show_AR='auto'
|
67
|
+
loc='best'
|
68
|
+
|
69
|
+
es=event_study("600519.SS",event_date="2024-4-2", \
|
70
|
+
start='2024-3-1',end='2024-4-30', \
|
71
|
+
event_window=[0,0],post_event_days=7, \
|
72
|
+
method='CAPM', \
|
73
|
+
market_index='000001.SS',RF=0.0143)
|
74
|
+
|
75
|
+
es=event_study("600519.SS",event_date="2024-4-2", \
|
76
|
+
start='2024-3-15',end='2024-4-20', \
|
77
|
+
event_window=[0,1],post_event_days=7, \
|
78
|
+
method='CAPM', \
|
79
|
+
market_index='000001.SS',RF=0.0143)
|
80
|
+
|
81
|
+
es=event_study("600519.SS",event_date="2024-4-2", \
|
82
|
+
start='2024-3-1',end='2024-4-30', \
|
83
|
+
event_window=[0,0],post_event_days=7, \
|
84
|
+
method='market',market_index='000001.SS')
|
85
|
+
|
86
|
+
es=event_study("600519.SS",event_date="2024-4-2", \
|
87
|
+
start='2024-3-1',end='2024-4-30', \
|
88
|
+
event_window=[0,0],post_event_days=7, \
|
89
|
+
method='random walk')
|
90
|
+
|
91
|
+
|
92
|
+
def event_study(ticker,event_date, \
|
93
|
+
start='MRM',end='today', \
|
94
|
+
event_window=[0,0], \
|
95
|
+
post_event_days=0, \
|
96
|
+
method='CAPM', \
|
97
|
+
early_response_days=-2, \
|
98
|
+
estimation_window_days=-365, \
|
99
|
+
market_index='000300.SS', \
|
100
|
+
RF="market index", \
|
101
|
+
ret_type="Daily Adj Ret%", \
|
102
|
+
ticker_type='auto', \
|
103
|
+
facecolor="whitesmoke",show_AR='auto',loc='best'):
|
104
|
+
"""
|
105
|
+
|
106
|
+
功能:展示事件研究法的累计异常收益率CAR。
|
107
|
+
参数:
|
108
|
+
ticker:证券代码,可为股票、债券、基金、指数、国债收益率等。可为单个或多个。
|
109
|
+
event_date:事件发生日(注意时区的影响),以此日期为基期0,该日期可能在周末或假日。
|
110
|
+
注意:允许标注多个事件日,但仅以第一个事件日计算相关日期。
|
111
|
+
start/end:展示事件影响的起止日期,至少需要将事件日、事件窗口和事件后窗口包括在内,主要用于绘图。
|
112
|
+
注意:如果不绘制AR仅绘制CAR,事件窗口前CAR均为空,start日期在绘图中将不起作用。
|
113
|
+
event_window:事件窗口的起止日期,为相对事件日的相对日期,默认[0,0],即事件当日一天。
|
114
|
+
注意:事件窗口不一定包括事件日(适用于事件日在非交易日的情形,例如周末或假日)
|
115
|
+
如果事件日为非交易日,事件窗口需要后移至事件日后的第一个交易日。
|
116
|
+
如果怀疑市场提前对事件发生反应,可以考虑前移事件窗口的开始日期。
|
117
|
+
post_event_days:用于分析事件窗口后的漂移效应,取事件窗口后多少天,默认取7天。
|
118
|
+
method:估计事件窗口以及事件后窗口收益率预期值的方法,默认为CAPM(主要用于ticker为股票等)
|
119
|
+
如果ticker为股票等,也可以直接使用指数的收益率为其预期收益率,此时method为Market或Index。
|
120
|
+
如果ticker为指数,无法再借助指数,method只能使用Random Walk,即使用前一个收益率为预期收益率。
|
121
|
+
注意:不管多个ticker时里面的不同证券类型,仅按第一个ticker的类型判断,并使用同一种证券类型。
|
122
|
+
early_response_days:默认为-2,即提前2天市场就开始有反应。
|
123
|
+
市场很可能对事件提前发生反应(因为泄密等原因),例如中国市场规定上市公司董事会开完后两天内必须披露。
|
124
|
+
很可能刚开完董事会,市场就得到了消息。为规避这种情况对估计窗口的影响,可以调节此参数。
|
125
|
+
estimation_window_days:当method使用CAPM时,用于估计贝塔系数和截距项,以便计算预期收益率。
|
126
|
+
默认在事件窗口开始日期+提前反应天数前的365个自然日(约250个交易日)。
|
127
|
+
market_index:当method为CAPM时,用于计算市场收益率。默认中国市场采用000300.SS。
|
128
|
+
注意:需要根据不同市场采取不同的市场指数,例如香港市场为恒生指数,美国市场为标普500指数等。
|
129
|
+
RF:默认使用市场模型"market index",无需指定;可直接指定具体数值;
|
130
|
+
也可指定特定指标,例如一年期中国国债收益率"1YCNY.B"或一年期美债收益率"1YUSY.B"等。
|
131
|
+
ticker_type:显式指明ticker的证券类型,当siat误判其类型(中国内地股票/债券/基金)时使用,默认'auto'。
|
132
|
+
facecolor:显式指定绘图背景颜色,默认"whitesmoke"。
|
133
|
+
show_AR:是否绘图时绘制异常收益率AR,默认'auto'(单个ticker时绘制,多个时不绘制),也可指定True/False强行绘制/不绘制。
|
134
|
+
"""
|
135
|
+
|
136
|
+
DEBUG=False
|
137
|
+
DEBUG2=False
|
138
|
+
|
139
|
+
#=====事件研究各个日期的计算与调整===========================================
|
140
|
+
if isinstance(event_date,str):
|
141
|
+
event_date=[date_adjust(event_date,adjust=0)]
|
142
|
+
elif isinstance(event_date,list):
|
143
|
+
event_date=[date_adjust(ed,adjust=0) for ed in event_date]
|
144
|
+
else:
|
145
|
+
print(" #Warning(event_study): invalid date or list of dates {}".format(event_date))
|
146
|
+
return None
|
147
|
+
event_date.sort() #升序排序
|
148
|
+
|
149
|
+
#事件窗口日期:遇到周末需要调整,提前或顺延至最近的工作日
|
150
|
+
event_window_new=event_window.copy() #列表的普通赋值仅为指针,新列表的改动也会影响原列表
|
151
|
+
adjust_start=0
|
152
|
+
event_window_start=date_adjust(event_date[0],adjust=event_window[0])
|
153
|
+
if week_day(event_window_start) == 5: #周六
|
154
|
+
if event_window[0] >= 0:
|
155
|
+
adjust_start=2
|
156
|
+
else:
|
157
|
+
adjust_start=-1
|
158
|
+
elif week_day(event_window_start) == 6: #周日
|
159
|
+
if event_window[0] >= 0:
|
160
|
+
adjust_start=1
|
161
|
+
else:
|
162
|
+
adjust_start=-2
|
163
|
+
event_window_start=date_adjust(event_window_start,adjust=adjust_start)
|
164
|
+
event_window_new[0]=event_window[0]+adjust_start
|
165
|
+
|
166
|
+
adjust_end=0
|
167
|
+
event_window_end=date_adjust(event_window_start,adjust=event_window[1]-event_window[0])
|
168
|
+
if week_day(event_window_end) == 5: #周六
|
169
|
+
if event_window[1] >= 0:
|
170
|
+
adjust_end=2
|
171
|
+
else:
|
172
|
+
adjust_end=-1
|
173
|
+
elif week_day(event_window_end) == 6: #周日
|
174
|
+
if event_window[1] >= 0:
|
175
|
+
adjust_end=1
|
176
|
+
else:
|
177
|
+
adjust_end=-2
|
178
|
+
event_window_end=date_adjust(event_window_end,adjust=adjust_end)
|
179
|
+
event_window_new[1]=event_window[1]+adjust_start+adjust_end
|
180
|
+
|
181
|
+
|
182
|
+
if DEBUG:
|
183
|
+
print(" DEBUG: event window is between {0} to {1}".format(event_window_start,event_window_end))
|
184
|
+
|
185
|
+
if event_window_new != event_window:
|
186
|
+
print(" #Notice: event window adjusted from {0} to {1} because of weekend".format(event_window,event_window_new))
|
187
|
+
|
188
|
+
#事件后窗口日期
|
189
|
+
post_event_start=date_adjust(event_window_end,adjust=0)
|
190
|
+
if week_day(post_event_start) == 5: #周六
|
191
|
+
post_event_start=date_adjust(post_event_start,adjust=2)
|
192
|
+
elif week_day(post_event_start) == 6: #周日
|
193
|
+
post_event_start=date_adjust(post_event_start,adjust=1)
|
194
|
+
|
195
|
+
post_event_end=date_adjust(post_event_start,adjust=post_event_days)
|
196
|
+
if week_day(post_event_end) == 5: #周六
|
197
|
+
post_event_end=date_adjust(post_event_end,adjust=2)
|
198
|
+
elif week_day(post_event_end) == 6: #周日
|
199
|
+
post_event_end=date_adjust(post_event_end,adjust=1)
|
200
|
+
|
201
|
+
if post_event_end > stoday:
|
202
|
+
post_event_end = stoday
|
203
|
+
|
204
|
+
if DEBUG:
|
205
|
+
print(" DEBUG: post event window is between {0} to {1}".format(post_event_start,post_event_end))
|
206
|
+
|
207
|
+
|
208
|
+
#事件窗口前日期
|
209
|
+
event_eve_date=date_adjust(event_window_start,adjust=-1)
|
210
|
+
if week_day(event_eve_date) == 5: #周六
|
211
|
+
event_eve_date=date_adjust(event_eve_date,adjust=-1)
|
212
|
+
elif week_day(event_eve_date) == 6: #周日
|
213
|
+
event_eve_date=date_adjust(event_eve_date,adjust=-2)
|
214
|
+
|
215
|
+
if DEBUG:
|
216
|
+
print(" DEBUG: event eve is on {}".format(event_eve_date))
|
217
|
+
|
218
|
+
#提前反应日期
|
219
|
+
early_response_date=date_adjust(event_date[0],adjust=early_response_days)
|
220
|
+
if week_day(early_response_date) == 5: #周六
|
221
|
+
early_response_date=date_adjust(early_response_date,adjust=-1)
|
222
|
+
elif week_day(early_response_date) == 6: #周日
|
223
|
+
early_response_date=date_adjust(early_response_date,adjust=-2)
|
224
|
+
|
225
|
+
if DEBUG:
|
226
|
+
print(" DEBUG: early response started on {}".format(early_response_date))
|
227
|
+
|
228
|
+
#估计窗口日期的计算
|
229
|
+
est_window_end=date_adjust(early_response_date,adjust=-1)
|
230
|
+
est_window_start=date_adjust(est_window_end,adjust=estimation_window_days)
|
231
|
+
if DEBUG:
|
232
|
+
print(" DEBUG: regression period starts from {0} to {1}".format(est_window_start,est_window_end))
|
233
|
+
|
234
|
+
|
235
|
+
#=====判断ticker是否为指数,调整预期收益率计算方法============================
|
236
|
+
if isinstance(ticker,str):
|
237
|
+
ticker=[ticker]
|
238
|
+
elif isinstance(ticker,list):
|
239
|
+
ticker=ticker
|
240
|
+
else:
|
241
|
+
print(" #Warning(event_study): unexpected type of ticker {}".format(ticker))
|
242
|
+
return None
|
243
|
+
|
244
|
+
if market_index in ticker:
|
245
|
+
print(" #Warning(event_study): market_index {0} duplicated in and removed from ticker {1}".format(market_index,ticker))
|
246
|
+
ticker.remove(market_index)
|
247
|
+
|
248
|
+
#tname=ticker_name(ticker[0],ticker_type)
|
249
|
+
#检查ticker是否为指数或国债收益率
|
250
|
+
"""
|
251
|
+
if ("指数" in tname or "index" in tname.lower()) or ("收益率" in tname or "yield" in tname.lower()):
|
252
|
+
if not ("random" in method.lower() or "walk" in method.lower()):
|
253
|
+
print(" #Notice: check the applicability of ticker {0}, method {1} with market index {2}".format(ticker[0],method,market_index))
|
254
|
+
"""
|
255
|
+
|
256
|
+
#=====获取证券价格和/或相关指数数据==========================================
|
257
|
+
#基于CAPM获取数据
|
258
|
+
if 'capm' in method.lower():
|
259
|
+
method_type="capm"
|
260
|
+
if isinstance(RF,int) or isinstance(RF,float):
|
261
|
+
#RF为具体数值
|
262
|
+
RF_type="value"
|
263
|
+
df_ret=compare_msecurity(tickers=ticker+[market_index],measure=ret_type, \
|
264
|
+
start=est_window_start,end=end, \
|
265
|
+
ticker_type=ticker_type, \
|
266
|
+
graph=False)
|
267
|
+
|
268
|
+
elif "market" in (str(RF)).lower() or "index" in (str(RF)).lower():
|
269
|
+
#RF通过市场模型计算,无需指定
|
270
|
+
RF_type="model"
|
271
|
+
df_ret=compare_msecurity(tickers=ticker+[market_index],measure=ret_type, \
|
272
|
+
start=est_window_start,end=end, \
|
273
|
+
ticker_type=ticker_type, \
|
274
|
+
graph=False)
|
275
|
+
else:
|
276
|
+
#指定RF代码,例如1YCNY.B
|
277
|
+
RF_type="code"
|
278
|
+
df_ret=compare_msecurity(tickers=ticker+[market_index,RF],measure=ret_type, \
|
279
|
+
start=est_window_start,end=end, \
|
280
|
+
ticker_type=ticker_type, \
|
281
|
+
graph=False)
|
282
|
+
|
283
|
+
#基于市场指数获取数据
|
284
|
+
elif 'market' in method.lower() or 'index' in method.lower():
|
285
|
+
method_type="market"
|
286
|
+
df_ret=compare_msecurity(tickers=ticker+[market_index],measure=ret_type, \
|
287
|
+
start=est_window_start,end=end, \
|
288
|
+
ticker_type=ticker_type, \
|
289
|
+
graph=False)
|
290
|
+
|
291
|
+
elif 'random' in method.lower() or 'walk' in method.lower():
|
292
|
+
method_type="random"
|
293
|
+
df_ret=compare_msecurity(tickers=ticker,measure=ret_type, \
|
294
|
+
start=est_window_start,end=end, \
|
295
|
+
ticker_type=ticker_type, \
|
296
|
+
graph=False)
|
297
|
+
for t in ticker_name(ticker,ticker_type):
|
298
|
+
try:
|
299
|
+
df_ret[t+"_predicted"]=df_ret[t].shift(1)
|
300
|
+
except:
|
301
|
+
#print(" #Warning(event_study): info not found for ticker {}".format(t))
|
302
|
+
continue
|
303
|
+
|
304
|
+
else:
|
305
|
+
print(" #Warning(event_study): unexpected type of AR method {}".format(method))
|
306
|
+
return None
|
307
|
+
|
308
|
+
#=====计算异常收益率AR=====
|
309
|
+
df_cols=list(df_ret)
|
310
|
+
if method_type=='market':
|
311
|
+
for t in ticker_name(ticker,ticker_type):
|
312
|
+
try:
|
313
|
+
df_ret[t+'_AR']=df_ret[t] - df_ret[ticker_name(market_index)]
|
314
|
+
except: continue
|
315
|
+
|
316
|
+
elif method_type=='random':
|
317
|
+
for t in ticker_name(ticker,ticker_type):
|
318
|
+
try:
|
319
|
+
df_ret[t+'_AR']=df_ret[t] - df_ret[t+"_predicted"]
|
320
|
+
except: continue
|
321
|
+
|
322
|
+
else: #按CAPM计算
|
323
|
+
#CAPM回归期间数据
|
324
|
+
est_window_startpd=pd.to_datetime(est_window_start)
|
325
|
+
est_window_endpd =pd.to_datetime(est_window_end)
|
326
|
+
df_reg=df_ret[(df_ret.index >=est_window_startpd) & (df_ret.index <=est_window_endpd)].copy()
|
327
|
+
|
328
|
+
#删除空缺值,否则回归会出错
|
329
|
+
df_reg=df_reg.replace([np.nan, None], np.nan).dropna()
|
330
|
+
|
331
|
+
import statsmodels.api as sm
|
332
|
+
if RF_type=="value":
|
333
|
+
if not ("%" in ret_type): #注意:RF不是百分比
|
334
|
+
X=df_reg[ticker_name(market_index)] - RF #无截距项回归,指定RF具体数值
|
335
|
+
else:
|
336
|
+
X=df_reg[ticker_name(market_index)] - RF*100.0
|
337
|
+
|
338
|
+
elif RF_type=="model":
|
339
|
+
X=df_reg[ticker_name(market_index)]
|
340
|
+
X=sm.add_constant(X) #有截距项回归,基于市场模型
|
341
|
+
else: #RF_type=="code"
|
342
|
+
if not ("%" in ret_type): #注意:1YCNY.B数值是百分比%
|
343
|
+
X=df_reg[ticker_name(market_index)] - df_reg[ticker_name(RF)]/100.0
|
344
|
+
else:
|
345
|
+
X=df_reg[ticker_name(market_index)] - df_reg[ticker_name(RF)]
|
346
|
+
|
347
|
+
if DEBUG:
|
348
|
+
print(" DEBUG: method_type={0}, RF_type={1}".format(method_type,RF_type))
|
349
|
+
|
350
|
+
#CAPM回归
|
351
|
+
beta_dict={}; intercept_dict={}; pvalue_dict={}
|
352
|
+
for t in ticker_name(ticker,ticker_type):
|
353
|
+
try:
|
354
|
+
if RF_type=="value":
|
355
|
+
if not ("%" in ret_type): #注意:RF不是百分比
|
356
|
+
y=df_reg[t] - RF
|
357
|
+
else:
|
358
|
+
y=df_reg[t] - RF*100.0
|
359
|
+
|
360
|
+
elif RF_type=="model":
|
361
|
+
y=df_reg[t]
|
362
|
+
|
363
|
+
else: #RF_type=="code"
|
364
|
+
if not ("%" in ret_type): #注意:1YCNY.B数值是百分比%
|
365
|
+
y=df_reg[t] - df_reg[ticker_name(RF)]/100.0
|
366
|
+
else:
|
367
|
+
y=df_reg[t] - df_reg[ticker_name(RF)]
|
368
|
+
except: continue
|
369
|
+
|
370
|
+
model = sm.OLS(y,X) #定义回归模型y=X
|
371
|
+
results = model.fit() #进行OLS回归
|
372
|
+
|
373
|
+
#提取回归系数,详细信息见results.summary()
|
374
|
+
intercept=results.params[0]
|
375
|
+
beta=results.params[1]; pvalue=results.pvalues[1]
|
376
|
+
|
377
|
+
beta_dict[t] = beta; intercept_dict[t] = intercept; pvalue_dict[t] = pvalue;
|
378
|
+
if DEBUG:
|
379
|
+
print(" DEBUG: {0}, intercept={1}, beta={2}, pvalue={3}".format(t,round(intercept,4),round(beta,4),round(pvalue,4)))
|
380
|
+
|
381
|
+
#计算收益率预期和AR
|
382
|
+
for t in ticker_name(ticker,ticker_type):
|
383
|
+
try:
|
384
|
+
if RF_type=="value":
|
385
|
+
RF_text=str(round(RF*100.0,4))[:6]+'%'
|
386
|
+
if not ("%" in ret_type): #注意:RF不是百分比
|
387
|
+
df_ret[t+"_predicted"]=(df_ret[ticker_name(market_index)] - RF)*beta_dict[t] + RF
|
388
|
+
else:
|
389
|
+
df_ret[t+"_predicted"]=(df_ret[ticker_name(market_index)] - RF*100.0)*beta_dict[t] + RF*100.0
|
390
|
+
|
391
|
+
elif RF_type=="model":
|
392
|
+
RF_text="基于市场模型回归"
|
393
|
+
df_ret[t+"_predicted"]=df_ret[ticker_name(market_index)]*beta_dict[t] + intercept_dict[t]
|
394
|
+
|
395
|
+
else: #RF_type=="code"
|
396
|
+
RF_text=ticker_name(RF)
|
397
|
+
df_ret[t+"_predicted"]=(df_ret[ticker_name(market_index)] - df_ret[ticker_name(RF)])*beta_dict[t] + df_ret[ticker_name(RF)]
|
398
|
+
|
399
|
+
df_ret[t+"_AR"]=df_ret[t] - df_ret[t+"_predicted"]
|
400
|
+
except: continue
|
401
|
+
|
402
|
+
if DEBUG2:
|
403
|
+
print(" DEBUG: RF_type={0}, RF_text={1}".format(RF_type,RF_text))
|
404
|
+
|
405
|
+
#=====计算CAR==============================================================
|
406
|
+
for t in ticker_name(ticker,ticker_type):
|
407
|
+
try:
|
408
|
+
df_ret[t+"_CAR"]=0
|
409
|
+
except: continue
|
410
|
+
|
411
|
+
event_window_startpd=pd.to_datetime(event_window_start)
|
412
|
+
event_window_endpd=pd.to_datetime(event_window_end)
|
413
|
+
post_event_endpd=pd.to_datetime(post_event_end)
|
414
|
+
startpd=pd.to_datetime(start); endpd=pd.to_datetime(end)
|
415
|
+
|
416
|
+
#计算CAR
|
417
|
+
df_ret_event=df_ret[(df_ret.index >=event_window_startpd) & (df_ret.index <=endpd)]
|
418
|
+
for t in ticker_name(ticker,ticker_type):
|
419
|
+
try:
|
420
|
+
df_ret_event[t+'_CAR'] = df_ret_event[t+'_AR'].cumsum()
|
421
|
+
except: continue
|
422
|
+
|
423
|
+
#合成事件前期间
|
424
|
+
df_ret_before_event=df_ret[(df_ret.index >=startpd) & (df_ret.index < event_window_startpd)]
|
425
|
+
for t in ticker_name(ticker,ticker_type):
|
426
|
+
try:
|
427
|
+
df_ret_before_event[t+'_CAR']=np.nan
|
428
|
+
except: continue
|
429
|
+
|
430
|
+
df_show=pd.concat([df_ret_before_event,df_ret_event])
|
431
|
+
|
432
|
+
#是否显示AR:默认单证券显示,多证券不显示
|
433
|
+
df_show_cols=[]
|
434
|
+
for c in list(df_show):
|
435
|
+
if show_AR=='auto':
|
436
|
+
if len(ticker)==1:
|
437
|
+
if 'AR' in c or 'CAR' in c:
|
438
|
+
df_show_cols=df_show_cols+[c]
|
439
|
+
show_AR=True
|
440
|
+
else:
|
441
|
+
if 'CAR' in c:
|
442
|
+
df_show_cols=df_show_cols+[c]
|
443
|
+
show_AR=False
|
444
|
+
elif show_AR==True:
|
445
|
+
if 'AR' in c or 'CAR' in c:
|
446
|
+
df_show_cols=df_show_cols+[c]
|
447
|
+
else: #show_AR==False
|
448
|
+
if 'CAR' in c:
|
449
|
+
df_show_cols=df_show_cols+[c]
|
450
|
+
|
451
|
+
df_show2=df_show[df_show_cols]
|
452
|
+
|
453
|
+
#=====绘图=================================================================
|
454
|
+
#设置标签
|
455
|
+
df0=df_show2
|
456
|
+
|
457
|
+
y_label="收益率%"
|
458
|
+
|
459
|
+
#横轴注释
|
460
|
+
footnote1="首事件日{0},事件窗口{1},事件后窗口天数{2},市场提前反应天数{3}".format(event_date[0],event_window_new,post_event_days,early_response_days)
|
461
|
+
footnote2="收益率类型:"+ectranslate(ret_type)
|
462
|
+
|
463
|
+
if method_type == "market":
|
464
|
+
method_name="市场指数基准"
|
465
|
+
elif method_type == "random":
|
466
|
+
method_name="随机漫步模型"
|
467
|
+
else:
|
468
|
+
method_name="CAPM模型"
|
469
|
+
|
470
|
+
footnote3=",收益率预期方法:"+method_name
|
471
|
+
if not method_type == "random":
|
472
|
+
footnote4=',市场指数:'+ticker_name(market_index)
|
473
|
+
else:
|
474
|
+
footnote4=''
|
475
|
+
|
476
|
+
#显著性检验:异于零的t检验,事件窗口
|
477
|
+
df_event_window=df0[(df0.index >=event_window_start) & (df0.index <=event_window_end)]
|
478
|
+
footnote5="事件窗口CAR的p值:"
|
479
|
+
for c in list(df_event_window):
|
480
|
+
if 'CAR' in c.upper():
|
481
|
+
if len(df_event_window[c])==1:
|
482
|
+
if abs(df_event_window[c].values[0]) > 0.01:
|
483
|
+
p_value=0.0
|
484
|
+
else:
|
485
|
+
p_value=1.0
|
486
|
+
else:
|
487
|
+
p_value=ttest(df_event_window[c],0)
|
488
|
+
footnote5=footnote5+c[:-4]+str(p_value)[:6]+","
|
489
|
+
footnote5=footnote5.strip(",")
|
490
|
+
|
491
|
+
#显著性检验:异于零的t检验,事件后窗口
|
492
|
+
if post_event_days == 0:
|
493
|
+
footnote6=''
|
494
|
+
else:
|
495
|
+
df_post_event_window=df0[(df0.index >event_window_end) & (df0.index <=post_event_end)]
|
496
|
+
footnote6="事件后窗口CAR的p值:"
|
497
|
+
for c in list(df_post_event_window):
|
498
|
+
if 'CAR' in c.upper():
|
499
|
+
if len(df_post_event_window[c])==1:
|
500
|
+
if abs(df_post_event_window[c].values[0]) > 0.01:
|
501
|
+
p_value=0.0
|
502
|
+
else:
|
503
|
+
p_value=1.0
|
504
|
+
else:
|
505
|
+
p_value=ttest(df_post_event_window[c],0)
|
506
|
+
footnote6=footnote6+c[:-4]+str(p_value)[:6]+","
|
507
|
+
footnote6=footnote6.strip(",")
|
508
|
+
|
509
|
+
footnote7="数据来源:Sina/EM/Yahoo/Stooq/SWHY,"+stoday
|
510
|
+
|
511
|
+
#x_label=footnote1+'\n'+footnote2+footnote3+footnote4+'\n'+footnote5+'\n'+footnote6+'\n'+footnote7
|
512
|
+
x_label=footnote1+'\n'+footnote2+footnote3+footnote4+'\n'+footnote7
|
513
|
+
|
514
|
+
|
515
|
+
axhline_value=0
|
516
|
+
axhline_label="零线"
|
517
|
+
title_txt="事件影响分析:"
|
518
|
+
for t in ticker_name(ticker,ticker_type):
|
519
|
+
title_txt=title_txt+t+','
|
520
|
+
title_txt=title_txt.strip(",")
|
521
|
+
|
522
|
+
#判断最新可获得日期
|
523
|
+
last_date=df0.index[-1].strftime("%Y-%m-%d")
|
524
|
+
if DEBUG:
|
525
|
+
print(" DEBUG: last_date={}".format(last_date))
|
526
|
+
if post_event_end > last_date:
|
527
|
+
post_event_end = last_date
|
528
|
+
|
529
|
+
if event_window_new[0] != event_window_new[1]:
|
530
|
+
attention_point_area=[event_window_start,event_window_end]
|
531
|
+
else:
|
532
|
+
attention_point_area=[event_window_start,post_event_end]
|
533
|
+
|
534
|
+
#去掉重复日期项标注且不改变顺序
|
535
|
+
event_date_new=[]
|
536
|
+
for d in event_date:
|
537
|
+
d_new=date_adjust(d,adjust=0)
|
538
|
+
event_date_new=event_date_new+[d_new]
|
539
|
+
|
540
|
+
attention_point=[event_eve_date,event_window_start,event_window_end,post_event_end]+event_date_new
|
541
|
+
if not show_AR:
|
542
|
+
period_days=calculate_days(event_eve_date,post_event_end)
|
543
|
+
if DEBUG:
|
544
|
+
print(" DEBUG: period_days={}".format(period_days))
|
545
|
+
|
546
|
+
if period_days< 6:
|
547
|
+
#绘图时横轴若少于6天会出现时间刻度,易误导需避免
|
548
|
+
draw_start_date=date_adjust(event_eve_date,adjust=period_days-6)
|
549
|
+
attention_point=[draw_start_date,event_window_start,event_window_end,post_event_end]+event_date_new
|
550
|
+
"""
|
551
|
+
if show_AR:
|
552
|
+
attention_point=[event_eve_date,event_window_start,event_window_end,post_event_end]+event_date_new
|
553
|
+
else:
|
554
|
+
attention_point=[event_eve_date,event_window_start,event_window_end,post_event_end]+event_date_new
|
555
|
+
df0=df0[(df0.index >= start) & (df0.index <=post_event_end)]
|
556
|
+
"""
|
557
|
+
attention_point.sort(reverse=False)
|
558
|
+
attention_point=list({}.fromkeys(attention_point).keys())
|
559
|
+
|
560
|
+
#绘图
|
561
|
+
draw_lines(df0,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
562
|
+
data_label=False, \
|
563
|
+
loc=loc,resample_freq='D',smooth=False, \
|
564
|
+
annotate=True,annotate_value=False, \
|
565
|
+
attention_point=attention_point, \
|
566
|
+
attention_point_area=attention_point_area, \
|
567
|
+
ticker_type=ticker_type,facecolor=facecolor)
|
568
|
+
|
569
|
+
#=====输出AR和/或CAR表格====================================================
|
570
|
+
df1=df0.copy()
|
571
|
+
#df1=df1.replace([np.nan, None], np.nan).dropna()
|
572
|
+
df1=df1.replace([np.nan, None],'-')
|
573
|
+
df1["日期"]=df1.index
|
574
|
+
df1["日期"]=df1["日期"].apply(lambda x: x.strftime("%Y-%m-%d"))
|
575
|
+
|
576
|
+
df1=df1[(df1["日期"] >= event_date[0]) & (df1["日期"] <= post_event_end)]
|
577
|
+
df1["星期"]=df1["日期"].apply(lambda x: week_day(x)+1)
|
578
|
+
|
579
|
+
df1["事件标记"]=''
|
580
|
+
for d in event_date_new:
|
581
|
+
if len(event_date_new)==1:
|
582
|
+
event_text="事件日"
|
583
|
+
else:
|
584
|
+
pos=event_date_new.index(d)
|
585
|
+
if pos==1:
|
586
|
+
event_text="首事件日"
|
587
|
+
else:
|
588
|
+
event_text="事件日"+str(pos+1)
|
589
|
+
df1["事件标记"]=df1.apply(lambda x: event_text if x["日期"]==d else x["事件标记"],axis=1)
|
590
|
+
|
591
|
+
#event_text=",事件窗口开始日"
|
592
|
+
event_text="\n事件窗开始"
|
593
|
+
df1["事件标记"]=df1.apply(lambda x: x["事件标记"]+event_text if x["日期"]==event_window_start else x["事件标记"],axis=1)
|
594
|
+
#event_text=",事件窗口结束日"
|
595
|
+
event_text="\n事件窗结束"
|
596
|
+
df1["事件标记"]=df1.apply(lambda x: x["事件标记"]+event_text if x["日期"]==event_window_end else x["事件标记"],axis=1)
|
597
|
+
|
598
|
+
#event_text=",事件后窗口结束日"
|
599
|
+
if post_event_end > event_window_end:
|
600
|
+
event_text="\n事件后窗结束"
|
601
|
+
df1["事件标记"]=df1.apply(lambda x: x["事件标记"]+event_text if x["日期"]==post_event_end else x["事件标记"],axis=1)
|
602
|
+
|
603
|
+
event_text="\n事件窗"
|
604
|
+
df1["事件标记"]=df1.apply(lambda x: x["事件标记"]+event_text if (x["日期"] > event_window_start) and (x["日期"] < event_window_end) else x["事件标记"],axis=1)
|
605
|
+
|
606
|
+
event_text="\n事件后窗"
|
607
|
+
df1["事件标记"]=df1.apply(lambda x: x["事件标记"]+event_text if (x["日期"] > event_window_end) and (x["日期"] < post_event_end) else x["事件标记"],axis=1)
|
608
|
+
|
609
|
+
df1["事件标记"]=df1["事件标记"].apply(lambda x: x.strip('\n'))
|
610
|
+
|
611
|
+
#显示表格
|
612
|
+
df0_list=list(df0)
|
613
|
+
df1_list=["事件标记","日期","星期"]+df0_list
|
614
|
+
df1=df1[df1_list]
|
615
|
+
#title_txt=title_txt+",窗口收益率"
|
616
|
+
|
617
|
+
if "CAPM" in method.upper():
|
618
|
+
footnotex="CAPM回归期间:{0}至{1},无风险收益率RF:{2}".format(est_window_start,est_window_end,RF_text)
|
619
|
+
footnotey="CAPM贝塔系数:"
|
620
|
+
for k in beta_dict:
|
621
|
+
footnotey=footnotey+k+str(round(beta_dict[k],4))[:6]+","
|
622
|
+
footnotey=footnotey.strip(",")
|
623
|
+
|
624
|
+
footnote=footnote2+footnote3+footnote4+'\n'+footnotex+'\n'+footnotey+'\n'+footnote5+'\n'+footnote6
|
625
|
+
else:
|
626
|
+
footnote=footnote2+footnote3+footnote4+'\n'+footnote5+'\n'+footnote6
|
627
|
+
|
628
|
+
|
629
|
+
df_display_CSS(df1,titletxt=title_txt,footnote=footnote,facecolor='papayawhip',decimals=4, \
|
630
|
+
first_col_align='left',second_col_align='left', \
|
631
|
+
last_col_align='center',other_col_align='center')
|
632
|
+
|
633
|
+
|
634
|
+
return df_show2
|
635
|
+
|
636
|
+
|
637
|
+
|
638
|
+
|
639
|
+
|
640
|
+
|
641
|
+
|
642
|
+
|
643
|
+
|
644
|
+
|
645
|
+
|
646
|
+
|
647
|
+
|
siat/grafix.py
CHANGED
@@ -291,6 +291,9 @@ def plot_line(df0,colname,collabel,ylabeltxt,titletxt,footnote,datatag=False, \
|
|
291
291
|
atp_list=attention_point
|
292
292
|
else:
|
293
293
|
atp_list=[]
|
294
|
+
#去重,不打乱原来的顺序
|
295
|
+
atp_list=list(dict.fromkeys(atp_list))
|
296
|
+
|
294
297
|
if not atp_list==[] and not atp_list==['']:
|
295
298
|
|
296
299
|
for at in atp_list:
|
@@ -348,7 +351,7 @@ def plot_line(df0,colname,collabel,ylabeltxt,titletxt,footnote,datatag=False, \
|
|
348
351
|
if haveLegend:
|
349
352
|
plt.legend(loc=loc,fontsize=legend_txt_size)
|
350
353
|
|
351
|
-
plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
354
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
352
355
|
try:
|
353
356
|
plt.gca().set_facecolor(facecolor) #设置画布背景颜色
|
354
357
|
except:
|
@@ -614,6 +617,10 @@ def plot_line2_coaxial(df01,ticker1,colname1,label1, \
|
|
614
617
|
atp_list=attention_point
|
615
618
|
else:
|
616
619
|
atp_list=[]
|
620
|
+
|
621
|
+
#去重,不打乱原来的顺序
|
622
|
+
atp_list=list(dict.fromkeys(atp_list))
|
623
|
+
|
617
624
|
if not atp_list==[] and not atp_list==['']:
|
618
625
|
|
619
626
|
for at in atp_list:
|
@@ -710,7 +717,7 @@ def plot_line2_coaxial(df01,ticker1,colname1,label1, \
|
|
710
717
|
|
711
718
|
# 同轴绘图时,loc1/loc2未用上!
|
712
719
|
plt.legend(loc=loc1,fontsize=legend_txt_size)
|
713
|
-
plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
720
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
714
721
|
try:
|
715
722
|
plt.gca().set_facecolor(facecolor)
|
716
723
|
except:
|
@@ -865,6 +872,9 @@ def plot_line2_coaxial2(df01,ticker1,colname1,label1, \
|
|
865
872
|
atp_list=attention_point
|
866
873
|
else:
|
867
874
|
atp_list=[]
|
875
|
+
#去重,不打乱原来的顺序
|
876
|
+
atp_list=list(dict.fromkeys(atp_list))
|
877
|
+
|
868
878
|
if not atp_list==[] and not atp_list==['']:
|
869
879
|
|
870
880
|
for at in atp_list:
|
@@ -973,7 +983,7 @@ def plot_line2_coaxial2(df01,ticker1,colname1,label1, \
|
|
973
983
|
|
974
984
|
# 同轴绘图时,loc1/loc2未用上!
|
975
985
|
plt.legend(loc=loc1,fontsize=legend_txt_size)
|
976
|
-
plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
986
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
977
987
|
try:
|
978
988
|
plt.gca().set_facecolor(facecolor)
|
979
989
|
except:
|
@@ -1067,6 +1077,8 @@ def plot_line2_twinx(df01,ticker1,colname1,label1, \
|
|
1067
1077
|
atp_list=attention_point
|
1068
1078
|
else:
|
1069
1079
|
atp_list=[]
|
1080
|
+
#去重,不打乱原来的顺序
|
1081
|
+
atp_list=list(dict.fromkeys(atp_list))
|
1070
1082
|
|
1071
1083
|
if DEBUG:
|
1072
1084
|
print("In plot_line2_twinx")
|
@@ -1182,7 +1194,7 @@ def plot_line2_twinx(df01,ticker1,colname1,label1, \
|
|
1182
1194
|
ax2.legend(loc=loc2,fontsize=legend_txt_size)
|
1183
1195
|
|
1184
1196
|
#自动优化x轴标签
|
1185
|
-
plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
1197
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
1186
1198
|
|
1187
1199
|
plt.title(titletxt,fontweight='bold',fontsize=title_txt_size)
|
1188
1200
|
plt.show()
|
@@ -1295,6 +1307,8 @@ def plot_line2_twinx2(df01,ticker1,colname1,label1, \
|
|
1295
1307
|
atp_list=attention_point
|
1296
1308
|
else:
|
1297
1309
|
atp_list=[]
|
1310
|
+
#去重,不打乱原来的顺序
|
1311
|
+
atp_list=list(dict.fromkeys(atp_list))
|
1298
1312
|
|
1299
1313
|
if DEBUG:
|
1300
1314
|
print("In plot_line2_twinx")
|
@@ -1426,7 +1440,7 @@ def plot_line2_twinx2(df01,ticker1,colname1,label1, \
|
|
1426
1440
|
#自动优化x轴标签
|
1427
1441
|
#格式化时间轴标注
|
1428
1442
|
#plt.gca().xaxis.set_major_formatter(mdate.DateFormatter('%y-%m-%d'))
|
1429
|
-
plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
1443
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
1430
1444
|
|
1431
1445
|
plt.title(titletxt,fontweight='bold',fontsize=title_txt_size)
|
1432
1446
|
plt.show()
|
@@ -1634,9 +1648,9 @@ def draw_lines(df0,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
|
1634
1648
|
color=last_line_color)
|
1635
1649
|
|
1636
1650
|
#用于关注值的颜色列表
|
1637
|
-
atv_color_list=["lightgray","paleturquoise","wheat","khaki","lightsage"]
|
1651
|
+
atv_color_list=["lightgray","paleturquoise","wheat","khaki","lightsage","hotpink","mediumslateblue"]
|
1638
1652
|
#用于关注点的颜色列表
|
1639
|
-
atp_color_list=["crimson","dodgerblue","magenta","lightseagreen","chocolate"]
|
1653
|
+
atp_color_list=["crimson","dodgerblue","magenta","lightseagreen","chocolate","hotpink","mediumslateblue"]
|
1640
1654
|
|
1641
1655
|
if not attention_value=='':
|
1642
1656
|
if isinstance(attention_value,int) or isinstance(attention_value,float):
|
@@ -1663,11 +1677,21 @@ def draw_lines(df0,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
|
1663
1677
|
atp_list=attention_point
|
1664
1678
|
else:
|
1665
1679
|
atp_list=[]
|
1680
|
+
#去重,不打乱原来的顺序
|
1681
|
+
atp_list=list(dict.fromkeys(atp_list))
|
1682
|
+
|
1666
1683
|
if not atp_list==[] and not atp_list==['']:
|
1667
1684
|
|
1668
1685
|
for at in atp_list:
|
1669
|
-
|
1670
|
-
|
1686
|
+
try:
|
1687
|
+
pos=atp_list.index(at)
|
1688
|
+
color=atp_color_list[pos]
|
1689
|
+
except:
|
1690
|
+
print("*** in draw_lines:")
|
1691
|
+
print("atp_list={0},at={1},pos={2}".format(atp_list,at,pos))
|
1692
|
+
print("atp_color_list={0}".format(atp_color_list))
|
1693
|
+
|
1694
|
+
color=atp_color_list[-1]
|
1671
1695
|
|
1672
1696
|
#判断是否日期字符串
|
1673
1697
|
try:
|
@@ -1725,7 +1749,7 @@ def draw_lines(df0,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
|
1725
1749
|
#图示标题
|
1726
1750
|
plt.title(title_txt,fontweight='bold',fontsize=title_txt_size)
|
1727
1751
|
|
1728
|
-
plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
1752
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
1729
1753
|
try:
|
1730
1754
|
plt.gca().set_facecolor(facecolor)
|
1731
1755
|
except:
|
@@ -2006,6 +2030,9 @@ def draw_lines2(df0,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
|
2006
2030
|
atp_list=attention_point
|
2007
2031
|
else:
|
2008
2032
|
atp_list=[]
|
2033
|
+
#去重,不打乱原来的顺序
|
2034
|
+
atp_list=list(dict.fromkeys(atp_list))
|
2035
|
+
|
2009
2036
|
if not atp_list==[] and not atp_list==['']:
|
2010
2037
|
for at in atp_list:
|
2011
2038
|
pos=atp_list.index(at)
|
@@ -2037,7 +2064,7 @@ def draw_lines2(df0,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
|
2037
2064
|
plt.xlabel(x_label,fontweight='bold',fontsize=xlabel_txt_size,ha='center')
|
2038
2065
|
#图示标题
|
2039
2066
|
plt.title(title_txt,fontweight='bold',fontsize=title_txt_size)
|
2040
|
-
plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
2067
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
2041
2068
|
try:
|
2042
2069
|
plt.gca().set_facecolor(facecolor)
|
2043
2070
|
except:
|
@@ -2280,7 +2307,7 @@ def plot_2lines(df01,colname1,label1, \
|
|
2280
2307
|
plt.xlabel(footnote,fontsize=xlabel_txt_size,ha='center')
|
2281
2308
|
plt.legend(loc='best',fontsize=legend_txt_size)
|
2282
2309
|
|
2283
|
-
plt.gcf().autofmt_xdate() # 优化标注(自动倾斜)
|
2310
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
2284
2311
|
try:
|
2285
2312
|
plt.gca().set_facecolor(facecolor)
|
2286
2313
|
except:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
siat/__init__ -20240701.py,sha256=gP5uajXnJesnH5SL0ZPwq_Qhv59AG1bs4qwZv26Fo2Y,2894
|
2
2
|
siat/__init__.py,sha256=sJP_LlLfNAssg5ZCPxxkVMi2v6h5x3WcSco3KBN5CsE,2040
|
3
|
-
siat/allin.py,sha256=
|
3
|
+
siat/allin.py,sha256=JLuxVmxtlOTDelnfulK6rPoFTIhzTNe5_GShXCiKGZY,2904
|
4
4
|
siat/alpha_vantage_test.py,sha256=tKr-vmuFH3CZAqwmISz6jzjPHzV1JJl3sPfZdz8aTfM,747
|
5
5
|
siat/assets_liquidity.py,sha256=o_UZdLs693uNWPEQB2OzxDH0mdWimOmq4qe_vx1pue0,28987
|
6
6
|
siat/assets_liquidity_test.py,sha256=UWk6HIUlizU7LQZ890fGx8LwU1jMMrIZswg8cFUJWZ8,1285
|
@@ -18,7 +18,7 @@ siat/capm_beta.py,sha256=cxXdRVBQBllhbfz1LeTJAIWvyRYhW54nhtNUXv4HwS0,29063
|
|
18
18
|
siat/capm_beta2.py,sha256=-ZYYp1HK7SkfTR3vBKZ0QVC4Q_tbST2O4MGbX_V77J0,32031
|
19
19
|
siat/capm_beta_test.py,sha256=ImR0c5mc4hIl714XmHztdl7qg8v1E2lycKyiqnFj6qs,1745
|
20
20
|
siat/cmat_commons.py,sha256=Nj9Kf0alywaztVoMVeVVL_EZk5jRERJy8R8kBw88_Tg,38116
|
21
|
-
siat/common.py,sha256=
|
21
|
+
siat/common.py,sha256=Ed89feCX9c7i_hbd_pcDk8h7Ie-VHOVKITSH4cKA02M,162842
|
22
22
|
siat/compare_cross.py,sha256=3iP9TH2h3w27F2ARZc7FjKcErYCzWRc-TPiymOyoVtw,24171
|
23
23
|
siat/compare_cross_test.py,sha256=xra5XYmQGEtfIZL2h-GssdH2hLdFIhG3eoCrkDrL3gY,3473
|
24
24
|
siat/concepts_iwencai.py,sha256=m1YEDtECRT6FqtzlKm91pt2I9d3Z_XoP59BtWdRdu8I,3061
|
@@ -32,6 +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=HLfgXyr2a5GJZaHiQOlydkgx7DVdlbzThrL3bw8cEQs,29061
|
35
36
|
siat/exchange_bond_china.pickle,sha256=zDqdPrFacQ0nqjP_SuF6Yy87EgijIRsFvFroW7FAYYY,1265092
|
36
37
|
siat/fama_french.py,sha256=aUTC-67t_CEPbLk4u79woW_zfZ7OCP6Fo4z5EdWCSkQ,48051
|
37
38
|
siat/fama_french_test.py,sha256=M4O23lBKsJxhWHRluwCb3l7HSEn3OFTjzGMpehcevRg,4678
|
@@ -60,7 +61,7 @@ siat/future_china.py,sha256=F-HsIf2Op8Z22RzTjet1g8COzldgnMjFNSXsAkeGyWo,17595
|
|
60
61
|
siat/future_china_test.py,sha256=BrSzmDVaOHki6rntOtosmRn-6dkfOBuLulJNqh7MOpc,1163
|
61
62
|
siat/global_index_test.py,sha256=hnFp3wqqzzL-kAP8mgxDZ54Bd5Ijf6ENi5YJlGBgcXw,2402
|
62
63
|
siat/google_authenticator.py,sha256=ZUbZR8OW0IAKDbcYtlqGqIpZdERpFor9NccFELxg9yI,1637
|
63
|
-
siat/grafix.py,sha256=
|
64
|
+
siat/grafix.py,sha256=EYgRBCfUTgJ9B5rTCzhO9UoABM1amTPUVXkOeKWpgGg,108806
|
64
65
|
siat/grafix_test.py,sha256=kXvcpLgQNO7wd30g_bWljLj5UH7bIVI0_dUtXbfiKR0,3150
|
65
66
|
siat/holding_risk.py,sha256=G3wpaewAKF9CwEqRpr4khyuDu9SU2EGyQUHdk7cmHOA,30693
|
66
67
|
siat/holding_risk_test.py,sha256=FRlw_9wFG98BYcg_cSj95HX5WZ1TvkGaOUdXD7-V86s,474
|
@@ -140,8 +141,8 @@ siat/valuation_china.py,sha256=CVp1IwIsF3Om0J29RGkyxZLt4n9Ug-ua_RKhLwL9fUQ,69624
|
|
140
141
|
siat/valuation_market_china_test.py,sha256=gbJ0ioauuo4koTPH6WKUkqcXiQPafnbhU5eKJ6lpdLA,1571
|
141
142
|
siat/var_model_validation.py,sha256=R0caWnuZarrRg9939hxh3vJIIpIyPfvelYmzFNZtPbo,14910
|
142
143
|
siat/yf_name.py,sha256=r0Q67cSMMlfebEkI9h9pdGlJCooEq7hw_3M5IUs4cSI,20081
|
143
|
-
siat-3.
|
144
|
-
siat-3.
|
145
|
-
siat-3.
|
146
|
-
siat-3.
|
147
|
-
siat-3.
|
144
|
+
siat-3.7.2.dist-info/LICENSE,sha256=NTEMMROY9_4U1szoKC3N2BLHcDd_o5uTgqdVH8tbApw,1071
|
145
|
+
siat-3.7.2.dist-info/METADATA,sha256=oZuRuVwB5c3XwvB-P0hRuRo1vWlf549Va29ZO0sE230,8009
|
146
|
+
siat-3.7.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
147
|
+
siat-3.7.2.dist-info/top_level.txt,sha256=r1cVyL7AIKqeAmEJjNR8FMT20OmEzufDstC2gv3NvEY,5
|
148
|
+
siat-3.7.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|