siat 2.13.43__py3-none-any.whl → 2.14.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 +4 -1
- siat/bond_zh_sina.py +143 -0
- siat/capm_beta2.py +615 -0
- siat/common.py +758 -31
- siat/grafix.py +211 -89
- siat/risk_adjusted_return.py +5 -5
- siat/risk_adjusted_return2.py +1339 -0
- siat/security_prices.py +103 -26
- siat/security_trend.py +20 -15
- siat/security_trend2.py +486 -0
- siat/stock.py +34 -16
- siat/stock_technical.py +21 -49
- siat/translate.py +30 -4
- {siat-2.13.43.dist-info → siat-2.14.2.dist-info}/METADATA +1 -1
- {siat-2.13.43.dist-info → siat-2.14.2.dist-info}/RECORD +17 -13
- {siat-2.13.43.dist-info → siat-2.14.2.dist-info}/WHEEL +0 -0
- {siat-2.13.43.dist-info → siat-2.14.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1339 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
本模块功能:股票的风险调整收益
|
4
|
+
所属工具包:证券投资分析工具SIAT
|
5
|
+
SIAT:Security Investment Analysis Tool
|
6
|
+
创建日期:2024年3月16日
|
7
|
+
最新修订日期:2024年3月19日
|
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_prices import *
|
23
|
+
from siat.capm_beta2 import *
|
24
|
+
#from siat.fama_french import *
|
25
|
+
from siat.risk_adjusted_return import *
|
26
|
+
from siat.grafix import *
|
27
|
+
|
28
|
+
import pandas as pd
|
29
|
+
import numpy as np
|
30
|
+
#==============================================================================
|
31
|
+
#==============================================================================
|
32
|
+
#==============================================================================
|
33
|
+
if __name__=='__main__':
|
34
|
+
ticker="600519.SS"
|
35
|
+
ticker={'Market':('US','^SPX','中概教培组合'),'EDU':0.7,'TAL':0.3}
|
36
|
+
|
37
|
+
start="2024-1-1"
|
38
|
+
end="2024-3-15"
|
39
|
+
rar_name="sharpe"
|
40
|
+
ret_type="Monthly Ret%"
|
41
|
+
RF=0
|
42
|
+
source='auto'
|
43
|
+
|
44
|
+
sharpe1m0=get_rolling_sharpe_sortino(ticker,start,end,rar_name="sharpe",ret_type="Monthly Ret%",RF=0)
|
45
|
+
sharpe2w=get_rolling_sharpe_sortino(ticker,start,end,rar_name="sharpe",ret_type="Weekly Ret%",RF=0.01759)
|
46
|
+
sharpe2m=get_rolling_sharpe_sortino(ticker,start,end,rar_name="sharpe",ret_type="Monthly Ret%",RF=0.01759)
|
47
|
+
sharpe2q=get_rolling_sharpe_sortino(ticker,start,end,rar_name="sharpe",ret_type="Quarterly Ret%",RF=0.01759)
|
48
|
+
sharpe2y=get_rolling_sharpe_sortino(ticker,start,end,rar_name="sharpe",ret_type="Annual Ret%",RF=0.01759)
|
49
|
+
|
50
|
+
sortino1=get_rolling_sharpe_sortino(ticker,start,end,rar_name="sortino",ret_type="Monthly Ret%",RF=0)
|
51
|
+
sortino2=get_rolling_sharpe_sortino(ticker,start,end,rar_name="sortino",ret_type="Monthly Ret%",RF=0.01759)
|
52
|
+
|
53
|
+
def get_rolling_sharpe_sortino(ticker,start,end,rar_name="sharpe",ret_type="Monthly Ret%",RF=0,source='auto'):
|
54
|
+
"""
|
55
|
+
功能:获取一只股票的夏普比率或索替诺比率,基于给定的滚动收益率类型,在指定期间内
|
56
|
+
支持股票和投资组合
|
57
|
+
RF: 年化利率,不带百分数
|
58
|
+
"""
|
59
|
+
|
60
|
+
#估计滚动窗口日期的提前量
|
61
|
+
ret_type_lower=ret_type.lower()
|
62
|
+
if 'weekly' in ret_type_lower:
|
63
|
+
dateahead=7*2+7 #考虑收益率标准差和节假日
|
64
|
+
ret_period='Weekly'
|
65
|
+
period_days=5
|
66
|
+
elif 'monthly' in ret_type_lower:
|
67
|
+
dateahead=31*2+7 #考虑收益率标准差和节假日
|
68
|
+
ret_period='Monthly'
|
69
|
+
period_days=21
|
70
|
+
elif 'quarterly' in ret_type_lower:
|
71
|
+
dateahead=(31*3+7)*2 #考虑收益率标准差和节假日
|
72
|
+
ret_period='Quarterly'
|
73
|
+
period_days=63
|
74
|
+
else:
|
75
|
+
dateahead=(366+7*3)*2 #考虑收益率标准差和节假日
|
76
|
+
ret_period='Annual'
|
77
|
+
period_days=252
|
78
|
+
|
79
|
+
start1=date_adjust(start,adjust=-dateahead)
|
80
|
+
|
81
|
+
#抓取股价
|
82
|
+
#pricedf=get_price(ticker,start1,end,source=source)
|
83
|
+
pricedf=get_price_security(ticker,start1,end,source=source)
|
84
|
+
|
85
|
+
#计算收益率和收益率标准差
|
86
|
+
rardf1=calc_daily_return(pricedf)
|
87
|
+
rardf2=calc_rolling_return(rardf1,period=ret_period)
|
88
|
+
|
89
|
+
if '%' in ret_type:
|
90
|
+
RF=RF*100
|
91
|
+
if ret_period=='Weekly':
|
92
|
+
RF_period=RF/52
|
93
|
+
elif ret_period=='Monthly':
|
94
|
+
RF_period=RF/12
|
95
|
+
elif ret_period=='Quarterly':
|
96
|
+
RF_period=RF/4
|
97
|
+
else:
|
98
|
+
RF_period=RF
|
99
|
+
|
100
|
+
#收益率减去一个常数其实不影响其标准差的数值,即std(ret-RF)=std(ret)
|
101
|
+
rardf2[ret_type]=rardf2[ret_type] - RF_period
|
102
|
+
rardf3=rolling_ret_volatility(rardf2, period=ret_period)
|
103
|
+
#收益率减去一个常数应该影响其下偏标准差的数值,即lpsd(ret-RF)!=lpsd(ret),实际上却未影响,有点奇怪
|
104
|
+
rardf4=rolling_ret_lpsd(rardf3, period=ret_period)
|
105
|
+
|
106
|
+
#开始日期富余一段时间,有助于绘图时显示出期望的开始日期
|
107
|
+
startpd=pd.to_datetime(date_adjust(start,adjust=-7))
|
108
|
+
endpd=pd.to_datetime(end)
|
109
|
+
rardf5=rardf4[(rardf4.index >=startpd) & (rardf4.index <=endpd)]
|
110
|
+
|
111
|
+
#确定风险字段名
|
112
|
+
pct_flag=False
|
113
|
+
if '%' in ret_type:
|
114
|
+
pct_flag=True
|
115
|
+
|
116
|
+
rar_name_lower=rar_name.lower()
|
117
|
+
ret_type_nopct=ret_type.replace('%','')
|
118
|
+
if 'sharpe' in rar_name_lower:
|
119
|
+
risk_type=ret_type_nopct+' Volatility'
|
120
|
+
if pct_flag:
|
121
|
+
risk_type=risk_type+'%'
|
122
|
+
rardf5[rar_name]=rardf5.apply(lambda x: x[ret_type]/x[risk_type],axis=1)
|
123
|
+
elif 'sortino' in rar_name_lower:
|
124
|
+
risk_type=ret_type_nopct+' LPSD'
|
125
|
+
if pct_flag:
|
126
|
+
risk_type=risk_type+'%'
|
127
|
+
rardf5[rar_name]=rardf5.apply(lambda x: x[ret_type]/x[risk_type],axis=1)
|
128
|
+
|
129
|
+
#选择返回字段
|
130
|
+
#rardf6=rardf5[['date','source','ticker','footnote',ret_type,risk_type,rar_name]]
|
131
|
+
rardf6=rardf5[[rar_name]]
|
132
|
+
|
133
|
+
return rardf6
|
134
|
+
#==============================================================================
|
135
|
+
#==============================================================================
|
136
|
+
if __name__=='__main__':
|
137
|
+
ticker="600519.SS"
|
138
|
+
ticker={'Market':('US','^SPX','中概教培组合'),'EDU':0.7,'TAL':0.3}
|
139
|
+
|
140
|
+
start="2023-1-1"
|
141
|
+
end="2024-3-15"
|
142
|
+
rar_name="sharpe"
|
143
|
+
ret_type="Exp Ret%"
|
144
|
+
RF=0.01759
|
145
|
+
source='auto'
|
146
|
+
|
147
|
+
sharpe1=get_expanding_sharpe_sortino(ticker,start,end,rar_name="sharpe",RF=0)
|
148
|
+
sharpe2=get_expanding_sharpe_sortino(ticker,start,end,rar_name="sharpe",RF=0.01759)
|
149
|
+
sortino2=get_expanding_sharpe_sortino(ticker,start,end,rar_name="sortino",RF=0.01759)
|
150
|
+
|
151
|
+
def get_expanding_sharpe_sortino(ticker,start,end,rar_name="sharpe",ret_type="Exp Ret%",RF=0,source='auto'):
|
152
|
+
"""
|
153
|
+
功能:获取一只股票的夏普比率或索替诺比率,基于扩展收益率,在指定期间内
|
154
|
+
支持股票和投资组合
|
155
|
+
RF: 年化利率,不带百分数
|
156
|
+
"""
|
157
|
+
|
158
|
+
#估计扩展窗口日期的提前量
|
159
|
+
dateahead=7
|
160
|
+
start1=date_adjust(start,adjust=-dateahead)
|
161
|
+
|
162
|
+
#抓取股价
|
163
|
+
#pricedf=get_price(ticker,start1,end,source=source)
|
164
|
+
pricedf=get_price_security(ticker,start1,end,source=source)
|
165
|
+
|
166
|
+
#计算收益率和收益率标准差
|
167
|
+
rardf2=calc_expanding_return(pricedf,start)
|
168
|
+
|
169
|
+
if '%' in ret_type:
|
170
|
+
RF=RF*100
|
171
|
+
RF_daily=RF/365
|
172
|
+
|
173
|
+
#增加距离开始日期的天数
|
174
|
+
date0=pd.to_datetime(rardf2.index[0])
|
175
|
+
if 'date' not in list(rardf2):
|
176
|
+
if 'Date' not in list(rardf2):
|
177
|
+
rardf2['date']=rardf2['Date']
|
178
|
+
else:
|
179
|
+
rardf2['date']=rardf2.index
|
180
|
+
|
181
|
+
rardf2['days']=rardf2['date'].apply(lambda x: days_between_dates(date0,pd.to_datetime(x)))
|
182
|
+
|
183
|
+
rardf2[ret_type]=rardf2.apply(lambda x: x[ret_type] - RF_daily*x['days'],axis=1)
|
184
|
+
|
185
|
+
#确定风险字段名,计算风险
|
186
|
+
rar_name_lower=rar_name.lower()
|
187
|
+
pct_flag=False
|
188
|
+
if '%' in ret_type:
|
189
|
+
pct_flag=True
|
190
|
+
ret_type_nopct=ret_type.replace('%','')
|
191
|
+
|
192
|
+
if 'sharpe' in rar_name_lower:
|
193
|
+
risk_type=ret_type_nopct+' Volatility'
|
194
|
+
if pct_flag:
|
195
|
+
risk_type=risk_type+'%'
|
196
|
+
|
197
|
+
#rardf2[risk_type]=rardf2[ret_type].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1)*np.sqrt(len(x)-1))
|
198
|
+
rardf2[risk_type]=rardf2[ret_type].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1))
|
199
|
+
elif 'sortino' in rar_name_lower:
|
200
|
+
risk_type=ret_type_nopct+' LPSD'
|
201
|
+
if pct_flag:
|
202
|
+
risk_type=risk_type+'%'
|
203
|
+
|
204
|
+
#rardf2[risk_type]=rardf2[ret_type].expanding(min_periods=1).apply(lambda x: lpsd(x)*np.sqrt(len(x)-1))
|
205
|
+
rardf2[risk_type]=rardf2[ret_type].expanding(min_periods=1).apply(lambda x: lpsd(x))
|
206
|
+
|
207
|
+
|
208
|
+
#计算RAR
|
209
|
+
rardf2[rar_name]=rardf2.apply(lambda x: x[ret_type]/x[risk_type],axis=1)
|
210
|
+
rardf3=rardf2.replace(np.nan,0)
|
211
|
+
|
212
|
+
#选择返回字段
|
213
|
+
#rardf4=rardf3[['date','source','ticker','footnote',ret_type,risk_type,rar_name]]
|
214
|
+
rardf4=rardf3[[rar_name]]
|
215
|
+
|
216
|
+
return rardf4
|
217
|
+
|
218
|
+
#==============================================================================
|
219
|
+
if __name__=='__main__':
|
220
|
+
ticker="600519.SS"
|
221
|
+
ticker={'Market':('US','^SPX','中概教培组合'),'EDU':0.7,'TAL':0.3}
|
222
|
+
|
223
|
+
start="2024-1-1"
|
224
|
+
end="2024-3-15"
|
225
|
+
rar_name="alpha"
|
226
|
+
ret_type="Monthly Ret%"
|
227
|
+
RF=0.01759
|
228
|
+
regression_period=365
|
229
|
+
mktidx='auto'; source='auto'
|
230
|
+
|
231
|
+
alpha1m0=get_rolling_treynor_alpha(ticker,start,end,rar_name="alpha",ret_type="Monthly Ret%",RF=0)
|
232
|
+
alpha2w=get_rolling_treynor_alpha(ticker,start,end,rar_name="alpha",ret_type="Weekly Ret%",RF=0.01759)
|
233
|
+
alpha2m=get_rolling_treynor_alpha(ticker,start,end,rar_name="alpha",ret_type="Monthly Ret%",RF=0.01759)
|
234
|
+
alpha2q=get_rolling_treynor_alpha(ticker,start,end,rar_name="alpha",ret_type="Quarterly Ret%",RF=0.01759)
|
235
|
+
alpha2y=get_rolling_treynor_alpha(ticker,start,end,rar_name="alpha",ret_type="Annual Ret%",RF=0.01759)
|
236
|
+
|
237
|
+
def get_rolling_treynor_alpha(ticker,start,end,rar_name="alpha", \
|
238
|
+
ret_type="Monthly Ret%",RF=0, \
|
239
|
+
regression_period=365,mktidx='auto',source='auto'):
|
240
|
+
"""
|
241
|
+
功能:获取一只股票的特雷诺比率或阿尔法指数,基于给定的滚动收益率类型,在指定期间内
|
242
|
+
支持股票和投资组合
|
243
|
+
RF: 年化利率,不带百分数
|
244
|
+
计算CAPM的期间:默认一年,252个交易日
|
245
|
+
"""
|
246
|
+
|
247
|
+
#估计需要的日期提前量
|
248
|
+
ret_type_lower=ret_type.lower()
|
249
|
+
if 'weekly' in ret_type_lower:
|
250
|
+
dateahead=7*2+7 #考虑收益率标准差和节假日
|
251
|
+
ret_period='Weekly'
|
252
|
+
period_days=5
|
253
|
+
elif 'monthly' in ret_type_lower:
|
254
|
+
dateahead=31*2+7 #考虑收益率标准差和节假日
|
255
|
+
ret_period='Monthly'
|
256
|
+
period_days=21
|
257
|
+
elif 'quarterly' in ret_type_lower:
|
258
|
+
dateahead=(31*3+7)*2 #考虑收益率标准差和节假日
|
259
|
+
ret_period='Quarterly'
|
260
|
+
period_days=63
|
261
|
+
else:
|
262
|
+
dateahead=(366+7*3)*2 #考虑收益率标准差和节假日
|
263
|
+
ret_period='Annual'
|
264
|
+
period_days=252
|
265
|
+
|
266
|
+
#计算日历日regression_period对应的交易日数
|
267
|
+
regtrddays=int(252 / 365 * regression_period)
|
268
|
+
|
269
|
+
#计算滚动查看需要的日期提前量
|
270
|
+
start1=date_adjust(start,adjust=-dateahead)
|
271
|
+
#计算CAPM需要的日期提前量
|
272
|
+
start2=date_adjust(start1,adjust=-regression_period-7*2)
|
273
|
+
|
274
|
+
#CAPM回归,计算贝塔系数
|
275
|
+
reg_result,dretdf3=regression_capm(ticker,start2,end,ret_type=ret_type,RF=RF, \
|
276
|
+
regtrddays=regtrddays,mktidx=mktidx,source=source)
|
277
|
+
|
278
|
+
#计算股票和指数的滚动收益率
|
279
|
+
varx=ret_type+'_x'
|
280
|
+
vary=ret_type+'_y'
|
281
|
+
|
282
|
+
pretdf=dretdf3.copy()
|
283
|
+
pretdfcols=list(pretdf)
|
284
|
+
lndretx='ln_'+pretdfcols[0]
|
285
|
+
lndrety='ln_'+pretdfcols[1]
|
286
|
+
|
287
|
+
#对数法计算滚动收益率
|
288
|
+
RF_period=RF/365 * period_days
|
289
|
+
if '%' in ret_type_lower:
|
290
|
+
pretdf[lndretx]=pretdf[pretdfcols[0]].apply(lambda x: np.log(1+x/100))
|
291
|
+
pretdf[lndrety]=pretdf[pretdfcols[1]].apply(lambda x: np.log(1+x/100))
|
292
|
+
|
293
|
+
pretdf[varx]=pretdf[lndretx].rolling(window=period_days).apply(lambda x: (np.exp(sum(x))-1)*100)
|
294
|
+
pretdf[vary]=pretdf[lndrety].rolling(window=period_days).apply(lambda x: (np.exp(sum(x))-1)*100)
|
295
|
+
|
296
|
+
RF_period=(RF/365 * period_days)*100
|
297
|
+
else:
|
298
|
+
pretdf[lndretx]=pretdf[pretdfcols[0]].apply(lambda x: np.log(1+x))
|
299
|
+
pretdf[lndrety]=pretdf[pretdfcols[1]].apply(lambda x: np.log(1+x))
|
300
|
+
|
301
|
+
pretdf[varx]=pretdf[lndretx].rolling(window=period_days).apply(lambda x: (np.exp(sum(x))-1))
|
302
|
+
pretdf[vary]=pretdf[lndrety].rolling(window=period_days).apply(lambda x: (np.exp(sum(x))-1))
|
303
|
+
|
304
|
+
#合成滚动收益率与贝塔系数
|
305
|
+
pretdf1=pd.merge(pretdf[[varx,vary]],reg_result,how='inner',left_index=True,right_index=True)
|
306
|
+
|
307
|
+
#计算特雷诺比率和阿尔法指标
|
308
|
+
if 'treynor' in rar_name.lower():
|
309
|
+
pretdf1[rar_name]=pretdf1.apply(lambda x: (x[vary]-RF_period)/x['beta'],axis=1)
|
310
|
+
elif 'alpha' in rar_name.lower():
|
311
|
+
vary_pred=vary+'_pred'
|
312
|
+
pretdf1[vary_pred]=pretdf1.apply(lambda x: RF_period+x['beta']*(x[varx]-RF_period),axis=1)
|
313
|
+
pretdf1[rar_name]=pretdf1.apply(lambda x: x[vary]-x[vary_pred],axis=1)
|
314
|
+
|
315
|
+
#开始日期富余一段时间,有助于绘图时显示出期望的开始日期
|
316
|
+
startpd=pd.to_datetime(date_adjust(start,adjust=-7))
|
317
|
+
endpd=pd.to_datetime(end)
|
318
|
+
pretdf2=pretdf1[(pretdf1.index >=startpd) & (pretdf1.index <=endpd)]
|
319
|
+
|
320
|
+
pretdf3=pretdf2[[rar_name,'beta']]
|
321
|
+
|
322
|
+
return pretdf3
|
323
|
+
|
324
|
+
#==============================================================================
|
325
|
+
if __name__=='__main__':
|
326
|
+
ticker="600519.SS"
|
327
|
+
ticker={'Market':('US','^SPX','中概教培组合'),'EDU':0.7,'TAL':0.3}
|
328
|
+
|
329
|
+
start="2024-1-1"
|
330
|
+
end="2024-3-15"
|
331
|
+
rar_name="alpha"
|
332
|
+
ret_type="Exp Ret%"
|
333
|
+
RF=0.01759
|
334
|
+
regression_period=365
|
335
|
+
mktidx='auto'; source='auto'
|
336
|
+
|
337
|
+
alpha1=get_expanding_treynor_alpha(ticker,start,end,rar_name="alpha",ret_type="Exp Ret%",RF=0)
|
338
|
+
alpha2=get_expanding_treynor_alpha(ticker,start,end,rar_name="alpha",ret_type="Exp Ret%",RF=0.01759)
|
339
|
+
|
340
|
+
|
341
|
+
def get_expanding_treynor_alpha(ticker,start,end,rar_name="alpha", \
|
342
|
+
ret_type="Exp Ret%",RF=0, \
|
343
|
+
regression_period=365,mktidx='auto',source='auto'):
|
344
|
+
"""
|
345
|
+
功能:获取一只股票的特雷诺比率或阿尔法指数,基于扩展收益率类型,在指定期间内
|
346
|
+
支持股票和投资组合
|
347
|
+
RF: 年化利率,不带百分数
|
348
|
+
计算CAPM的期间:默认一年,252个交易日=365个日历日
|
349
|
+
"""
|
350
|
+
ret_type_lower=ret_type.lower()
|
351
|
+
#计算日历日regression_period对应的交易日数
|
352
|
+
regtrddays=int(252 / 365 * regression_period)
|
353
|
+
|
354
|
+
#计算滚动查看需要的日期提前量:无滚动
|
355
|
+
start1=date_adjust(start,adjust=0)
|
356
|
+
#计算CAPM需要的日期提前量
|
357
|
+
start2=date_adjust(start1,adjust=-regression_period-7*2)
|
358
|
+
|
359
|
+
#CAPM回归,计算贝塔系数
|
360
|
+
reg_result,dretdf3=regression_capm(ticker,start2,end,ret_type=ret_type,RF=RF, \
|
361
|
+
regtrddays=regtrddays,mktidx=mktidx,source=source)
|
362
|
+
|
363
|
+
#计算股票和指数的扩展收益率
|
364
|
+
varx=ret_type+'_x'
|
365
|
+
vary=ret_type+'_y'
|
366
|
+
|
367
|
+
startpd=pd.to_datetime(start)
|
368
|
+
endpd=pd.to_datetime(end)
|
369
|
+
pretdf=dretdf3[(dretdf3.index >= startpd) & (dretdf3.index <= endpd)].copy()
|
370
|
+
date0=pd.to_datetime(pretdf.index[0])
|
371
|
+
|
372
|
+
pretdfcols=list(pretdf)
|
373
|
+
#日期首日累计收益率应该为零,先用nan代替,最后再替换为零
|
374
|
+
dretx=pretdfcols[0]
|
375
|
+
drety=pretdfcols[1]
|
376
|
+
lagdretx='lag_'+dretx
|
377
|
+
lagdrety='lag_'+drety
|
378
|
+
pretdf[lagdretx]=pretdf[dretx].shift(1)
|
379
|
+
pretdf[lagdrety]=pretdf[drety].shift(1)
|
380
|
+
|
381
|
+
pretdf=pretdf.replace(np.nan,0)
|
382
|
+
|
383
|
+
lndretx='ln_'+dretx
|
384
|
+
lndrety='ln_'+drety
|
385
|
+
|
386
|
+
#对数法计算扩展收益率
|
387
|
+
RF_daily=RF/365
|
388
|
+
if '%' in ret_type_lower:
|
389
|
+
RF_daily=RF/365 * 100
|
390
|
+
pretdf[lndretx]=pretdf[lagdretx].apply(lambda x: np.log(1+x/100))
|
391
|
+
pretdf[lndrety]=pretdf[lagdrety].apply(lambda x: np.log(1+x/100))
|
392
|
+
|
393
|
+
pretdf[varx]=pretdf[lndretx].expanding(min_periods=1).apply(lambda x: (np.exp(sum(x))-1)*100)
|
394
|
+
pretdf[vary]=pretdf[lndrety].expanding(min_periods=1).apply(lambda x: (np.exp(sum(x))-1)*100)
|
395
|
+
|
396
|
+
else:
|
397
|
+
pretdf[lndretx]=pretdf[pretdfcols[0]].apply(lambda x: np.log(1+x))
|
398
|
+
pretdf[lndrety]=pretdf[pretdfcols[1]].apply(lambda x: np.log(1+x))
|
399
|
+
|
400
|
+
pretdf[varx]=pretdf[lndretx].expanding(min_periods=1).apply(lambda x: (np.exp(sum(x))-1))
|
401
|
+
pretdf[vary]=pretdf[lndrety].expanding(min_periods=1).apply(lambda x: (np.exp(sum(x))-1))
|
402
|
+
|
403
|
+
|
404
|
+
pretdf['Date']=pretdf.index
|
405
|
+
pretdf['days']=pretdf['Date'].apply(lambda x: days_between_dates(date0,pd.to_datetime(x)))
|
406
|
+
|
407
|
+
#合成扩展收益率与贝塔系数
|
408
|
+
pretdf1=pd.merge(pretdf[[varx,vary,'days']],reg_result,how='inner',left_index=True,right_index=True)
|
409
|
+
|
410
|
+
#计算特雷诺比率和阿尔法指标
|
411
|
+
if 'treynor' in rar_name.lower():
|
412
|
+
pretdf1[rar_name]=pretdf1.apply(lambda x: (x[vary]-RF_daily*x['days'])/x['beta'],axis=1)
|
413
|
+
elif 'alpha' in rar_name.lower():
|
414
|
+
vary_pred=vary+'_pred'
|
415
|
+
pretdf1[vary_pred]=pretdf1.apply(lambda x: RF_daily*x['days']+x['beta']*(x[varx]-RF_daily*x['days']),axis=1)
|
416
|
+
pretdf1[rar_name]=pretdf1.apply(lambda x: x[vary]-x[vary_pred],axis=1)
|
417
|
+
|
418
|
+
|
419
|
+
pretdf3=pretdf1[[rar_name,'beta']]
|
420
|
+
|
421
|
+
return pretdf3
|
422
|
+
|
423
|
+
|
424
|
+
#==============================================================================
|
425
|
+
if __name__=='__main__':
|
426
|
+
ticker="600519.SS"
|
427
|
+
ticker={'Market':('US','^SPX','中概教培组合'),'EDU':0.7,'TAL':0.3}
|
428
|
+
|
429
|
+
rar_name="sharpe"
|
430
|
+
rar_name="alpha"
|
431
|
+
|
432
|
+
ret_type="Monthly Ret%"
|
433
|
+
ret_type="Exp Ret%"
|
434
|
+
|
435
|
+
start="2024-1-1"
|
436
|
+
end="2024-3-15"
|
437
|
+
RF=0.01759
|
438
|
+
regression_period=365
|
439
|
+
mktidx='auto'; source='auto'
|
440
|
+
|
441
|
+
alpha1=get_rar(ticker,start,end,rar_name="alpha",ret_type="Exp Ret%",RF=0)
|
442
|
+
alpha2=get_rar(ticker,start,end,rar_name="alpha",ret_type="Exp Ret%",RF=0.01759)
|
443
|
+
|
444
|
+
|
445
|
+
def get_rar(ticker,start,end,rar_name="sharpe",ret_type="Monthly Ret%", \
|
446
|
+
RF=0,regression_period=365,mktidx='auto',source='auto'):
|
447
|
+
"""
|
448
|
+
功能:获取一只股票的收益-风险性价比指标,在指定期间内,支持股票和投资组合
|
449
|
+
支持滚动收益率和扩展收益率
|
450
|
+
滚动收益率支持周、月、季度和年度,默认为年度
|
451
|
+
支持特雷诺比率、夏普比率、所提诺比率和阿尔法指标
|
452
|
+
|
453
|
+
RF: 年化利率,不带百分数
|
454
|
+
计算CAPM的期间:默认一年,252个交易日=365个日历日
|
455
|
+
"""
|
456
|
+
|
457
|
+
ret_type_lower=ret_type.lower()
|
458
|
+
ret_type_title=ret_type.title() #字符串每个单词首字母大写
|
459
|
+
rar_name_lower=rar_name.lower()
|
460
|
+
|
461
|
+
#判断是否扩展收益率
|
462
|
+
if 'exp' not in ret_type_lower:
|
463
|
+
if ('sharpe' in rar_name_lower) or ('sortino' in rar_name_lower):
|
464
|
+
rardf=get_rolling_sharpe_sortino(ticker=ticker,start=start,end=end, \
|
465
|
+
rar_name=rar_name_lower, \
|
466
|
+
ret_type=ret_type_title,RF=RF,source=source)
|
467
|
+
elif ('alpha' in rar_name_lower) or ('treynor' in rar_name_lower):
|
468
|
+
rardf=get_rolling_treynor_alpha(ticker=ticker,start=start,end=end, \
|
469
|
+
rar_name=rar_name_lower, \
|
470
|
+
ret_type=ret_type_title,RF=RF, \
|
471
|
+
regression_period=regression_period, \
|
472
|
+
mktidx=mktidx,source=source)
|
473
|
+
|
474
|
+
else:
|
475
|
+
if ('sharpe' in rar_name_lower) or ('sortino' in rar_name_lower):
|
476
|
+
rardf=get_expanding_sharpe_sortino(ticker=ticker,start=start,end=end, \
|
477
|
+
rar_name=rar_name_lower, \
|
478
|
+
ret_type=ret_type_title,RF=RF,source=source)
|
479
|
+
elif ('alpha' in rar_name_lower) or ('treynor' in rar_name_lower):
|
480
|
+
rardf=get_expanding_treynor_alpha(ticker=ticker,start=start,end=end, \
|
481
|
+
rar_name=rar_name_lower, \
|
482
|
+
ret_type=ret_type_title,RF=RF, \
|
483
|
+
regression_period=regression_period, \
|
484
|
+
mktidx=mktidx,source=source)
|
485
|
+
|
486
|
+
return rardf
|
487
|
+
|
488
|
+
#==============================================================================
|
489
|
+
if __name__=='__main__':
|
490
|
+
ticker="600519.SS"
|
491
|
+
ticker={'Market':('US','^SPX','中概教培组合'),'EDU':0.7,'TAL':0.3}
|
492
|
+
|
493
|
+
start="2024-1-1"
|
494
|
+
end="2024-3-15"
|
495
|
+
rar=['sharpe','sortino','treynor','alpha']
|
496
|
+
ret_type="Monthly Ret%"
|
497
|
+
RF=0.01759
|
498
|
+
regression_period=365
|
499
|
+
|
500
|
+
graph=True; axhline_value=0; axhline_label=''
|
501
|
+
printout=False; sortby='tpw_mean'; trailing=20; trend_threshhold=0.001
|
502
|
+
annotate=False
|
503
|
+
mktidx='auto'; source='auto'
|
504
|
+
|
505
|
+
rars=compare_1ticker_mrar(ticker="600519.SS",start="2024-1-1",end="2024-3-19",rar=['sharpe','alpha'],printout=True)
|
506
|
+
|
507
|
+
def compare_1ticker_mrar(ticker,start,end,rar=['sharpe','sortino','treynor','alpha'], \
|
508
|
+
ret_type="Annual Ret%",RF=0,regression_period=365, \
|
509
|
+
graph=True,axhline_value=0,axhline_label='', \
|
510
|
+
printout=False,sortby='tpw_mean',trailing=7,trend_threshhold=0.01, \
|
511
|
+
annotate=False,mktidx='auto',source='auto'):
|
512
|
+
"""
|
513
|
+
功能:一只股票,对比其多个rar,支持股票和投资组合
|
514
|
+
"""
|
515
|
+
|
516
|
+
import os,sys
|
517
|
+
class HiddenPrints:
|
518
|
+
def __enter__(self):
|
519
|
+
self._original_stdout = sys.stdout
|
520
|
+
sys.stdout = open(os.devnull, 'w')
|
521
|
+
|
522
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
523
|
+
sys.stdout.close()
|
524
|
+
sys.stdout = self._original_stdout
|
525
|
+
|
526
|
+
if isinstance(ticker,list):
|
527
|
+
ticker=ticker[0] #将列表转换为字符串
|
528
|
+
if isinstance(rar,str):
|
529
|
+
rar=[rar] #将字符串转换为列表,避免下面的循环出错
|
530
|
+
if isinstance(ret_type,list):
|
531
|
+
ret_type=ret_type[0]
|
532
|
+
if isinstance(RF,list):
|
533
|
+
RF=RF[0]
|
534
|
+
if isinstance(regression_period,list):
|
535
|
+
regression_period=regression_period[0]
|
536
|
+
|
537
|
+
tname=ticker_name(ticker)
|
538
|
+
print(" Starting to retrive and calculate different rar for",tname,"\b, please wait ......")
|
539
|
+
|
540
|
+
df=pd.DataFrame()
|
541
|
+
for t in rar:
|
542
|
+
#关闭print输出
|
543
|
+
with HiddenPrints():
|
544
|
+
df_tmp=get_rar(ticker,start,end,t,ret_type=ret_type, \
|
545
|
+
RF=RF,regression_period=regression_period, \
|
546
|
+
mktidx=mktidx,source=source)
|
547
|
+
|
548
|
+
if df_tmp is None:
|
549
|
+
break
|
550
|
+
else:
|
551
|
+
dft=df_tmp[[t]]
|
552
|
+
|
553
|
+
if len(df)==0:
|
554
|
+
df=dft #第一个
|
555
|
+
else:
|
556
|
+
df=pd.merge(df,dft,how='outer',left_index=True,right_index=True)
|
557
|
+
|
558
|
+
if len(df)==0:
|
559
|
+
print(" #Error(compare_1ticker_mrar): rar data inaccessible for",tname,"between",start,end)
|
560
|
+
return None
|
561
|
+
|
562
|
+
#以下仅用于绘图或制表
|
563
|
+
df1=df.copy()
|
564
|
+
for c in list(df1):
|
565
|
+
if df1[c].max() > axhline_value and df1[c].min() < axhline_value:
|
566
|
+
axhline_label='零线'
|
567
|
+
|
568
|
+
df1.rename(columns={c:ectranslate(c)},inplace=True)
|
569
|
+
|
570
|
+
footnote1="注:基于"+ectranslate(ret_type)+","
|
571
|
+
if RF !=0:
|
572
|
+
footnote2="年化无风险利率为"+str(round(RF*100,4))+'%。'
|
573
|
+
else:
|
574
|
+
footnote2="不考虑年化无风险利率时。"
|
575
|
+
|
576
|
+
footnote3=''
|
577
|
+
if 'treynor' in rar or 'alpha' in rar:
|
578
|
+
footnote3="贝塔系数基于日收益率,回归期间"+str(regression_period)+"个自然日"
|
579
|
+
|
580
|
+
import datetime; todaydt = datetime.date.today()
|
581
|
+
footnote4="数据来源: 综合新浪/stooq/Yahoo,"+str(todaydt)+"统计"
|
582
|
+
if footnote3 !='':
|
583
|
+
footnotex=footnote1+footnote2+footnote3+'\n'+footnote4
|
584
|
+
else:
|
585
|
+
footnotex=footnote1+footnote2+footnote3+'\n'+footnote4
|
586
|
+
|
587
|
+
#绘图
|
588
|
+
if graph:
|
589
|
+
|
590
|
+
y_label=''
|
591
|
+
import datetime; todaydt = datetime.date.today()
|
592
|
+
x_label="数据来源: 综合新浪/stooq/Yahoo,"+str(todaydt)
|
593
|
+
title_txt="风险调整收益:"+tname
|
594
|
+
|
595
|
+
draw_lines(df1,y_label,x_label=footnotex, \
|
596
|
+
axhline_value=axhline_value,axhline_label=axhline_label, \
|
597
|
+
title_txt=title_txt,data_label=False,annotate=annotate)
|
598
|
+
|
599
|
+
#制表
|
600
|
+
recommenddf=pd.DataFrame()
|
601
|
+
if printout:
|
602
|
+
#删除含有Nan的行
|
603
|
+
df1.dropna(inplace=True)
|
604
|
+
|
605
|
+
if sortby=='tpw_mean':
|
606
|
+
sortby_txt='按推荐标记+近期优先加权平均值降序排列'
|
607
|
+
elif sortby=='min':
|
608
|
+
sortby_txt='按推荐标记+最小值降序排列'
|
609
|
+
elif sortby=='mean':
|
610
|
+
sortby_txt='按推荐标记+平均值降序排列'
|
611
|
+
elif sortby=='median':
|
612
|
+
sortby_txt='按推荐标记+中位数值降序排列'
|
613
|
+
elif sortby=='trailing':
|
614
|
+
sortby_txt='按推荐标记+短期均值走势降序排列'
|
615
|
+
|
616
|
+
title_txt='***** 风险调整收益评估:'+tname+','+sortby_txt+' *****'
|
617
|
+
|
618
|
+
footnote6='期间范围:'+str(start)+'至'+str(end)+";近期范围:近"+str(trailing)+"个交易日。趋势变化率阈值:"+str(trend_threshhold)+"。"
|
619
|
+
footnote7="近期趋势和星号为多项因素综合研判,最多五颗星星"
|
620
|
+
footnotey=footnote6+footnote7+'\n'+footnotex
|
621
|
+
|
622
|
+
recommenddf=descriptive_statistics2(df1,title_txt,footnotey,decimals=4, \
|
623
|
+
sortby=sortby,recommend_only=True,trailing=trailing, \
|
624
|
+
trend_threshhold=trend_threshhold)
|
625
|
+
|
626
|
+
return df,recommenddf
|
627
|
+
|
628
|
+
#==============================================================================
|
629
|
+
if __name__=='__main__':
|
630
|
+
ticker=["600519.SS","000858.SZ"]
|
631
|
+
ticker={'Market':('US','^SPX','中概教培组合'),'EDU':0.7,'TAL':0.3}
|
632
|
+
|
633
|
+
start="2024-1-1"
|
634
|
+
end="2024-3-15"
|
635
|
+
rar='sharpe'
|
636
|
+
ret_type="Monthly Ret%"
|
637
|
+
RF=0.01759
|
638
|
+
regression_period=365
|
639
|
+
|
640
|
+
graph=True; axhline_value=0; axhline_label=''
|
641
|
+
printout=False; sortby='tpw_mean'; trailing=5; trend_threshhold=0.01
|
642
|
+
annotate=False
|
643
|
+
mktidx='auto'; source='auto'
|
644
|
+
|
645
|
+
rars=compare_mticker_1rar(ticker=["600519.SS","000858.SZ"],start="2024-1-1",end="2024-3-19",rar='sharpe',printout=True)
|
646
|
+
|
647
|
+
def compare_mticker_1rar(ticker,start,end,rar='sharpe', \
|
648
|
+
ret_type="Annual Ret%",RF=0,regression_period=365, \
|
649
|
+
graph=True,axhline_value=0,axhline_label='', \
|
650
|
+
printout=False,sortby='tpw_mean',trailing=7,trend_threshhold=0.01, \
|
651
|
+
annotate=False,mktidx='auto',source='auto', \
|
652
|
+
style_print=True):
|
653
|
+
"""
|
654
|
+
功能:多只股票,对比其同一个rar,支持股票和投资组合
|
655
|
+
"""
|
656
|
+
|
657
|
+
import os,sys
|
658
|
+
class HiddenPrints:
|
659
|
+
def __enter__(self):
|
660
|
+
self._original_stdout = sys.stdout
|
661
|
+
sys.stdout = open(os.devnull, 'w')
|
662
|
+
|
663
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
664
|
+
sys.stdout.close()
|
665
|
+
sys.stdout = self._original_stdout
|
666
|
+
|
667
|
+
#转换字符串和列表,避免下面的循环出错
|
668
|
+
if not isinstance(ticker,list):
|
669
|
+
ticker=[ticker]
|
670
|
+
if isinstance(rar,list):
|
671
|
+
rar=rar[0]
|
672
|
+
if isinstance(ret_type,list):
|
673
|
+
ret_type=ret_type[0]
|
674
|
+
if isinstance(RF,list):
|
675
|
+
RF=RF[0]
|
676
|
+
if isinstance(regression_period,list):
|
677
|
+
regression_period=regression_period[0]
|
678
|
+
print(" Starting to retrive and calculate",rar,"\b, please wait ......")
|
679
|
+
|
680
|
+
df=pd.DataFrame()
|
681
|
+
for t in ticker:
|
682
|
+
#关闭print输出
|
683
|
+
with HiddenPrints():
|
684
|
+
df_tmp=get_rar(t,start,end,rar_name=rar,ret_type=ret_type, \
|
685
|
+
RF=RF,regression_period=regression_period, \
|
686
|
+
mktidx=mktidx,source=source)
|
687
|
+
|
688
|
+
if df_tmp is None:
|
689
|
+
break
|
690
|
+
else:
|
691
|
+
dft=df_tmp[[rar]]
|
692
|
+
dft.rename(columns={rar:ticker_name(t)},inplace=True)
|
693
|
+
|
694
|
+
if len(df)==0: #第一个
|
695
|
+
df=dft
|
696
|
+
else:
|
697
|
+
df=pd.merge(df,dft,how='outer',left_index=True,right_index=True)
|
698
|
+
|
699
|
+
if len(df)==0:
|
700
|
+
print(" #Error(compare_mticker_1rar): rar data not available for",ticker_name(t),"between",start,end)
|
701
|
+
return None
|
702
|
+
|
703
|
+
#仅用于绘图和制表
|
704
|
+
df1=df.copy()
|
705
|
+
for c in list(df1):
|
706
|
+
if df1[c].max() > axhline_value and df1[c].min() < axhline_value:
|
707
|
+
axhline_label='零线' #显示零线,但不标注图例
|
708
|
+
#df1.rename(columns={c:codetranslate(c)},inplace=True)
|
709
|
+
|
710
|
+
#共同脚注
|
711
|
+
footnote1=ectranslate(rar)+"基于"+ectranslate(ret_type)+"。"
|
712
|
+
if RF !=0:
|
713
|
+
footnote2="年化无风险利率"+str(round(RF*100,4))+'%。'
|
714
|
+
else:
|
715
|
+
footnote2="假设年化无风险利率为零。"
|
716
|
+
|
717
|
+
footnote3=''
|
718
|
+
if rar.lower() in ['treynor','alpha']:
|
719
|
+
footnote3="贝塔系数基于日收益率,回归期间"+str(regression_period)+"个自然日"
|
720
|
+
|
721
|
+
import datetime; todaydt = datetime.date.today()
|
722
|
+
footnote4="数据来源: 综合新浪/stooq/Yahoo,"+str(todaydt)+"统计"
|
723
|
+
if footnote3 !='':
|
724
|
+
footnotex=footnote1+footnote2+footnote3+'\n'+footnote4
|
725
|
+
else:
|
726
|
+
footnotex=footnote1+footnote2+footnote3+'\n'+footnote4
|
727
|
+
|
728
|
+
#绘图
|
729
|
+
if graph:
|
730
|
+
title_txt="风险调整收益:"+ectranslate(rar)
|
731
|
+
y_label=ectranslate(rar)
|
732
|
+
|
733
|
+
draw_lines(df1,y_label,x_label=footnotex, \
|
734
|
+
axhline_value=axhline_value,axhline_label=axhline_label, \
|
735
|
+
title_txt=title_txt,data_label=False,annotate=annotate)
|
736
|
+
|
737
|
+
#制表
|
738
|
+
recommenddf=pd.DataFrame()
|
739
|
+
if printout:
|
740
|
+
if sortby=='tpw_mean':
|
741
|
+
sortby_txt='按推荐标记+近期优先加权平均值降序排列'
|
742
|
+
elif sortby=='min':
|
743
|
+
sortby_txt='按推荐标记+最小值降序排列'
|
744
|
+
elif sortby=='mean':
|
745
|
+
sortby_txt='按推荐标记+平均值降序排列'
|
746
|
+
elif sortby=='median':
|
747
|
+
sortby_txt='按推荐标记+中位数值降序排列'
|
748
|
+
elif sortby=='trailing':
|
749
|
+
sortby_txt='按推荐标记+短期均值走势降序排列'
|
750
|
+
|
751
|
+
title_txt='***** 风险调整收益评估:基于'+ectranslate(rar)+','+sortby_txt+' *****'
|
752
|
+
|
753
|
+
footnote6='期间范围:'+str(start)+'至'+str(end)+";近期范围:近"+str(trailing)+"个交易日。趋势变化率阈值:"+str(trend_threshhold)
|
754
|
+
footnote7="近期趋势和星号为风险调整收益指标数值加趋势等多项因素综合研判,最多五颗星星"
|
755
|
+
footnotey=footnote6+'\n'+footnote7+'\n'+footnotex
|
756
|
+
|
757
|
+
#删除含有Nan的行,否则可能引起近期优先加权平均计算结果市场出现Nan
|
758
|
+
df1.dropna(inplace=True)
|
759
|
+
recommenddf=descriptive_statistics2(df1,title_txt,footnotey,decimals=4, \
|
760
|
+
sortby=sortby,recommend_only=True,trailing=trailing, \
|
761
|
+
trend_threshhold=trend_threshhold,style_print=style_print)
|
762
|
+
|
763
|
+
return df,recommenddf
|
764
|
+
|
765
|
+
#==============================================================================
|
766
|
+
if __name__=='__main__':
|
767
|
+
ticker=["600519.SS","000858.SZ"]
|
768
|
+
ticker={'Market':('US','^SPX','中概教培组合'),'EDU':0.7,'TAL':0.3}
|
769
|
+
|
770
|
+
start="2024-1-1"
|
771
|
+
end="2024-3-15"
|
772
|
+
rar=['sharpe','alpha']
|
773
|
+
ret_type="Monthly Ret%"
|
774
|
+
RF=0.01759
|
775
|
+
regression_period=365
|
776
|
+
|
777
|
+
graph=False; axhline_value=0; axhline_label=''
|
778
|
+
printout=True; sortby='tpw_mean'; trailing=5; trend_threshhold=0.01
|
779
|
+
annotate=False
|
780
|
+
mktidx='auto'; source='auto'
|
781
|
+
|
782
|
+
rars=compare_mticker_mrar(ticker,start,end,rar,graph=False,printout=True)
|
783
|
+
|
784
|
+
def compare_mticker_mrar(ticker,start,end,rar=['sharpe','alpha','sortino','treynor'], \
|
785
|
+
ret_type="Annual Ret%",RF=0,regression_period=365, \
|
786
|
+
graph=True,axhline_value=0,axhline_label='', \
|
787
|
+
printout=True,sortby='tpw_mean',trailing=7,trend_threshhold=0.01, \
|
788
|
+
annotate=False,mktidx='auto',source='auto'):
|
789
|
+
"""
|
790
|
+
功能:多只股票,多个rar,综合对比和排列。支持股票和投资组合
|
791
|
+
"""
|
792
|
+
|
793
|
+
#避免下面的循环出错
|
794
|
+
if isinstance(rar,str):
|
795
|
+
rar=[rar]
|
796
|
+
if isinstance(ret_type,list):
|
797
|
+
ret_type=ret_type[0]
|
798
|
+
if isinstance(RF,list):
|
799
|
+
RF=RF[0]
|
800
|
+
if isinstance(regression_period,list):
|
801
|
+
regression_period=regression_period[0]
|
802
|
+
|
803
|
+
#print(" Starting to compare multiple tickers with multiple RARs ......")
|
804
|
+
|
805
|
+
df=pd.DataFrame()
|
806
|
+
for r in rar:
|
807
|
+
#with HiddenPrints(): #此项将压制所有print输出,造成表头脚注不显示
|
808
|
+
_,df_tmp=compare_mticker_1rar(ticker,start,end,r, \
|
809
|
+
ret_type,RF,regression_period, \
|
810
|
+
graph,axhline_value,axhline_label, \
|
811
|
+
printout,sortby,trailing,trend_threshhold, \
|
812
|
+
annotate,mktidx,source,style_print=True)
|
813
|
+
if df_tmp is None:
|
814
|
+
break
|
815
|
+
else:
|
816
|
+
dft=df_tmp[['比较对象','推荐标记']]
|
817
|
+
dft.rename(columns={'推荐标记':r},inplace=True)
|
818
|
+
|
819
|
+
if len(df)==0: #第一个
|
820
|
+
df=dft
|
821
|
+
else:
|
822
|
+
df=pd.merge(df,dft,how='left',left_on='比较对象',right_on='比较对象')
|
823
|
+
|
824
|
+
df['综合推荐']=df[rar].sum(axis=1)
|
825
|
+
df.sort_values(by='综合推荐',ascending=False,inplace=True)
|
826
|
+
|
827
|
+
df['综合推荐']=df['综合推荐'].apply(lambda x: generate_stars(hzlen(x) / len(rar)))
|
828
|
+
for c in list(df):
|
829
|
+
df.rename(columns={c:ectranslate(c)},inplace=True)
|
830
|
+
|
831
|
+
if printout:
|
832
|
+
# 设置显示选项为True,开启Unicode字符支持
|
833
|
+
pd.set_option('display.unicode.ambiguous_as_wide', True)
|
834
|
+
pd.set_option('display.unicode.east_asian_width', True)
|
835
|
+
pd.set_option('display.width', 180) #设置打印宽度(**重要**)
|
836
|
+
|
837
|
+
if sortby=='tpw_mean':
|
838
|
+
sortby_txt='近期优先加权平均值优先'
|
839
|
+
elif sortby=='min':
|
840
|
+
sortby_txt='最小值优先'
|
841
|
+
elif sortby=='mean':
|
842
|
+
sortby_txt='平均值优先'
|
843
|
+
elif sortby=='median':
|
844
|
+
sortby_txt='中位数值优先'
|
845
|
+
elif sortby=='trailing':
|
846
|
+
sortby_txt='短期均值走势优先'
|
847
|
+
|
848
|
+
titletxt='===风险调整收益综合对比:'+sortby_txt+'==='
|
849
|
+
print("\n"+titletxt)
|
850
|
+
|
851
|
+
df1=df.copy()
|
852
|
+
df1.reset_index(drop=True,inplace=True)
|
853
|
+
df1.index=df1.index + 1
|
854
|
+
|
855
|
+
df2= df1.style.set_properties(**{'text-align':'center'})
|
856
|
+
from IPython.display import display
|
857
|
+
display(df2)
|
858
|
+
|
859
|
+
"""
|
860
|
+
print(df1.to_string(justify='left'))
|
861
|
+
|
862
|
+
justify_dict={}
|
863
|
+
for c in df1.columns:
|
864
|
+
if c=='比较对象':
|
865
|
+
justify_dict[c]='left'
|
866
|
+
else:
|
867
|
+
justify_dict[c]='center'
|
868
|
+
print(df1.to_string(justify=justify_dict))
|
869
|
+
"""
|
870
|
+
|
871
|
+
"""
|
872
|
+
alignlist=['right','left']+['center']*(len(list(df1))-3)+['center','center']
|
873
|
+
try:
|
874
|
+
print(df1.to_markdown(index=True,tablefmt='plain',colalign=alignlist))
|
875
|
+
except:
|
876
|
+
#解决汉字编码gbk出错问题
|
877
|
+
df2=df1.to_markdown(index=True,tablefmt='plain',colalign=alignlist)
|
878
|
+
df3=df2.encode("utf-8",errors="strict")
|
879
|
+
print(df3)
|
880
|
+
|
881
|
+
print("\n$$$$$$$$ 左调节打印")
|
882
|
+
df2=df1.copy()
|
883
|
+
max_len=max([len(col) for col in df2.columns]) #找到最长的列名长度
|
884
|
+
for col in df2.columns:
|
885
|
+
df2[col]=df2[col].astype(str) #将每列的值强制转换为字符串类型
|
886
|
+
df2[col]=df2[col].apply(lambda x: x.ljust(max_len)) #调整每列的宽度
|
887
|
+
print(df2)
|
888
|
+
|
889
|
+
print("\n$$$$$$$$ tabulate打印")
|
890
|
+
from tabulate import tabulate
|
891
|
+
print(tabulate(df1,headers=list(df1)))
|
892
|
+
"""
|
893
|
+
|
894
|
+
#脚注
|
895
|
+
footnote1="注:风险调整收益基于"+ectranslate(ret_type)+","
|
896
|
+
if RF !=0:
|
897
|
+
footnote2="年化无风险利率"+str(round(RF*100,4))+'%'
|
898
|
+
else:
|
899
|
+
footnote2="假设年化无风险利率为零"
|
900
|
+
|
901
|
+
footnote3=''
|
902
|
+
if 'treynor' in rar or 'alpha' in rar:
|
903
|
+
footnote3="贝塔系数基于日收益率,回归期间"+str(regression_period)+"个自然日"
|
904
|
+
|
905
|
+
import datetime; todaydt = datetime.date.today()
|
906
|
+
footnote4="数据来源: 综合新浪/stooq/Yahoo,"+str(todaydt)+"统计"
|
907
|
+
if footnote3 !='':
|
908
|
+
footnotex=footnote1+footnote2+'\n'+footnote3+'\n'+footnote4
|
909
|
+
else:
|
910
|
+
footnotex=footnote1+footnote2+'\n'+footnote4
|
911
|
+
|
912
|
+
print("\n"+footnotex)
|
913
|
+
|
914
|
+
return df
|
915
|
+
|
916
|
+
#==============================================================================
|
917
|
+
if __name__=='__main__':
|
918
|
+
ticker="600519.SS"
|
919
|
+
ticker={'Market':('US','^SPX','中概教培组合'),'EDU':0.7,'TAL':0.3}
|
920
|
+
|
921
|
+
start="2024-1-1"
|
922
|
+
end="2024-3-15"
|
923
|
+
rar='sharpe'
|
924
|
+
ret_type=["Monthly Ret%","Annual Ret%"]
|
925
|
+
RF=0.01759
|
926
|
+
regression_period=365
|
927
|
+
|
928
|
+
graph=True; axhline_value=0; axhline_label=''
|
929
|
+
printout=False; sortby='tpw_mean'; trailing=5; trend_threshhold=0.001
|
930
|
+
annotate=False
|
931
|
+
mktidx='auto'; source='auto'
|
932
|
+
|
933
|
+
rars=compare_1ticker_1rar_mret(ticker,start,end,rar,ret_type,printout=True)
|
934
|
+
|
935
|
+
def compare_1ticker_1rar_mret(ticker,start,end,rar='sharpe', \
|
936
|
+
ret_type=["Annual Ret%","Monthly Ret%"], \
|
937
|
+
RF=0,regression_period=365, \
|
938
|
+
graph=True,axhline_value=0,axhline_label='', \
|
939
|
+
printout=False,sortby='tpw_mean',trailing=7,trend_threshhold=0.01, \
|
940
|
+
annotate=False,mktidx='auto',source='auto'):
|
941
|
+
"""
|
942
|
+
功能:一只股票,同一个rar,对比其不同的收益率类型,支持股票和投资组合
|
943
|
+
"""
|
944
|
+
|
945
|
+
import os,sys
|
946
|
+
class HiddenPrints:
|
947
|
+
def __enter__(self):
|
948
|
+
self._original_stdout = sys.stdout
|
949
|
+
sys.stdout = open(os.devnull, 'w')
|
950
|
+
|
951
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
952
|
+
sys.stdout.close()
|
953
|
+
sys.stdout = self._original_stdout
|
954
|
+
|
955
|
+
#转换字符串和列表,避免下面的循环出错
|
956
|
+
if isinstance(ticker,list):
|
957
|
+
ticker=ticker[0]
|
958
|
+
if isinstance(rar,list):
|
959
|
+
rar=rar[0]
|
960
|
+
if isinstance(ret_type,str):
|
961
|
+
ret_type=[ret_type]
|
962
|
+
if isinstance(RF,list):
|
963
|
+
RF=RF[0]
|
964
|
+
if isinstance(regression_period,list):
|
965
|
+
regression_period=regression_period[0]
|
966
|
+
print(" Starting to retrive and calculate",rar,"for",ticker_name(ticker),"on different types of return, please wait ......")
|
967
|
+
|
968
|
+
df=pd.DataFrame()
|
969
|
+
for t in ret_type:
|
970
|
+
#关闭print输出
|
971
|
+
with HiddenPrints():
|
972
|
+
df_tmp=get_rar(ticker,start,end,rar,ret_type=t, \
|
973
|
+
RF=RF,regression_period=regression_period,mktidx=mktidx,source=source)
|
974
|
+
|
975
|
+
if df_tmp is None:
|
976
|
+
break
|
977
|
+
else:
|
978
|
+
dft=df_tmp[[rar]]
|
979
|
+
dft.rename(columns={rar:"基于"+ectranslate(t)},inplace=True)
|
980
|
+
|
981
|
+
if len(df)==0: #第一个
|
982
|
+
df=dft
|
983
|
+
else:
|
984
|
+
df=pd.merge(df,dft,how='outer',left_index=True,right_index=True)
|
985
|
+
|
986
|
+
if len(df)==0:
|
987
|
+
print(" #Error(compare_mticker_1rar): rar data not available for",ticker_name(ticker),"between",start,end)
|
988
|
+
return None
|
989
|
+
|
990
|
+
#仅用于绘图和制表
|
991
|
+
df1=df.copy()
|
992
|
+
for c in list(df1):
|
993
|
+
if df1[c].max() > axhline_value and df1[c].min() < axhline_value:
|
994
|
+
axhline_label='零线'
|
995
|
+
#df1.rename(columns={c:"基于"+ectranslate(c)},inplace=True)
|
996
|
+
|
997
|
+
#共同脚注
|
998
|
+
footnote1="注:"
|
999
|
+
if RF !=0:
|
1000
|
+
footnote2="年化无风险利率为"+str(round(RF*100,4))+'%。'
|
1001
|
+
else:
|
1002
|
+
footnote2="假设年化无风险利率为零。"
|
1003
|
+
|
1004
|
+
footnote3=''
|
1005
|
+
if rar.lower() in ['treynor','alpha']:
|
1006
|
+
footnote3="贝塔系数基于日收益率,回归期间"+str(regression_period)+"个自然日"
|
1007
|
+
|
1008
|
+
import datetime; todaydt = datetime.date.today()
|
1009
|
+
footnote4="数据来源: 综合新浪/stooq/Yahoo,"+str(todaydt)+"统计"
|
1010
|
+
if footnote3 !='':
|
1011
|
+
footnotex=footnote1+footnote2+footnote3+'\n'+footnote4
|
1012
|
+
else:
|
1013
|
+
footnotex=footnote1+footnote2+footnote3+'\n'+footnote4
|
1014
|
+
|
1015
|
+
#绘图
|
1016
|
+
if graph:
|
1017
|
+
|
1018
|
+
title_txt="风险调整收益:"+ticker_name(ticker)
|
1019
|
+
y_label=ectranslate(rar)
|
1020
|
+
|
1021
|
+
draw_lines(df1,y_label,x_label=footnotex, \
|
1022
|
+
axhline_value=axhline_value,axhline_label=axhline_label, \
|
1023
|
+
title_txt=title_txt,data_label=False,annotate=annotate)
|
1024
|
+
|
1025
|
+
#制表
|
1026
|
+
recommenddf=pd.DataFrame()
|
1027
|
+
if printout:
|
1028
|
+
if sortby=='tpw_mean':
|
1029
|
+
sortby_txt='按推荐标记+近期优先加权平均值降序排列'
|
1030
|
+
elif sortby=='min':
|
1031
|
+
sortby_txt='按推荐标记+最小值降序排列'
|
1032
|
+
elif sortby=='mean':
|
1033
|
+
sortby_txt='按推荐标记+平均值降序排列'
|
1034
|
+
elif sortby=='median':
|
1035
|
+
sortby_txt='按推荐标记+中位数值降序排列'
|
1036
|
+
elif sortby=='trailing':
|
1037
|
+
sortby_txt='按推荐标记+短期均值走势降序排列'
|
1038
|
+
|
1039
|
+
title_txt='***** 风险调整收益评估:'+'基于'+ectranslate(rar)+','+ticker_name(ticker)+','+sortby_txt+' *****'
|
1040
|
+
|
1041
|
+
footnote6='期间范围:'+str(start)+'至'+str(end)+";近期范围:近"+str(trailing)+"个交易日。趋势变化率阈值:"+str(trend_threshhold)+"。"
|
1042
|
+
footnote7="近期趋势和星号为多项因素综合研判,最多五颗星星"
|
1043
|
+
footnotey=footnote6+footnote7+'\n'+footnotex
|
1044
|
+
|
1045
|
+
#删除含有Nan的行
|
1046
|
+
df1.dropna(inplace=True)
|
1047
|
+
|
1048
|
+
recommenddf=descriptive_statistics2(df1,title_txt,footnotey,decimals=4, \
|
1049
|
+
sortby=sortby,recommend_only=True,trailing=trailing, \
|
1050
|
+
trend_threshhold=trend_threshhold)
|
1051
|
+
|
1052
|
+
return df,recommenddf
|
1053
|
+
|
1054
|
+
#==============================================================================
|
1055
|
+
if __name__=='__main__':
|
1056
|
+
ticker="600519.SS"
|
1057
|
+
ticker={'Market':('US','^SPX','中概教培组合'),'EDU':0.7,'TAL':0.3}
|
1058
|
+
ticker={'Market':('China','000300.SS','白酒组合'),'600519.SS':0.2,'000858.SZ':0.3,'600809.SS':0.5}
|
1059
|
+
|
1060
|
+
start="2024-3-18"; end="2024-3-22"
|
1061
|
+
rar='alpha'
|
1062
|
+
rar='sharpe'
|
1063
|
+
ret_type="Annual Ret%"
|
1064
|
+
RF=[0.005,0.01759,0.05]
|
1065
|
+
regression_period=365
|
1066
|
+
|
1067
|
+
graph=True; axhline_value=0; axhline_label=''
|
1068
|
+
printout=False; sortby='tpw_mean'; trailing=5; trend_threshhold=0.001
|
1069
|
+
annotate=False
|
1070
|
+
mktidx='auto'; source='auto'
|
1071
|
+
|
1072
|
+
rars=compare_1ticker_1rar_1ret_mRF(ticker,start,end,rar,ret_type,RF)
|
1073
|
+
|
1074
|
+
def compare_1ticker_1rar_1ret_mRF(ticker,start,end,rar='sharpe', \
|
1075
|
+
ret_type="Annual Ret%", \
|
1076
|
+
RF=[0,0.02,0.05], \
|
1077
|
+
regression_period=365, \
|
1078
|
+
graph=True,axhline_value=0,axhline_label='', \
|
1079
|
+
printout=False,sortby='tpw_mean',trailing=7,trend_threshhold=0.01, \
|
1080
|
+
annotate=False,mktidx='auto',source='auto'):
|
1081
|
+
"""
|
1082
|
+
功能:一只股票,相同的rar,相同的收益率类型,不同的无风险收益率
|
1083
|
+
支持股票和投资组合
|
1084
|
+
"""
|
1085
|
+
|
1086
|
+
import os,sys
|
1087
|
+
class HiddenPrints:
|
1088
|
+
def __enter__(self):
|
1089
|
+
self._original_stdout = sys.stdout
|
1090
|
+
sys.stdout = open(os.devnull, 'w')
|
1091
|
+
|
1092
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
1093
|
+
sys.stdout.close()
|
1094
|
+
sys.stdout = self._original_stdout
|
1095
|
+
|
1096
|
+
#转换字符串和列表,避免下面的循环出错
|
1097
|
+
if isinstance(ticker,list):
|
1098
|
+
ticker=ticker[0]
|
1099
|
+
if isinstance(rar,list):
|
1100
|
+
rar=rar[0]
|
1101
|
+
if isinstance(ret_type,list):
|
1102
|
+
ret_type=[ret_type]
|
1103
|
+
if isinstance(RF,float):
|
1104
|
+
RF=[RF]
|
1105
|
+
if isinstance(regression_period,list):
|
1106
|
+
regression_period=regression_period[0]
|
1107
|
+
print(" Starting to retrive and calculate",rar,"for",ticker_name(ticker),"on different RF, please wait ......")
|
1108
|
+
|
1109
|
+
df=pd.DataFrame()
|
1110
|
+
for t in RF:
|
1111
|
+
#关闭print输出
|
1112
|
+
with HiddenPrints():
|
1113
|
+
df_tmp=get_rar(ticker,start,end,rar,ret_type, \
|
1114
|
+
RF=t,regression_period=regression_period,mktidx=mktidx,source=source)
|
1115
|
+
|
1116
|
+
if df_tmp is None:
|
1117
|
+
break
|
1118
|
+
else:
|
1119
|
+
dft=df_tmp[[rar]]
|
1120
|
+
dft.rename(columns={rar:"基于无风险利率"+str(round(t*100,4))+'%'},inplace=True)
|
1121
|
+
|
1122
|
+
if len(df)==0: #第一个
|
1123
|
+
df=dft
|
1124
|
+
else:
|
1125
|
+
df=pd.merge(df,dft,how='outer',left_index=True,right_index=True)
|
1126
|
+
|
1127
|
+
if len(df)==0:
|
1128
|
+
print(" #Error(compare_mticker_1rar): rar data inaccessible for",ticker_name(ticker),"between",start,end)
|
1129
|
+
return None
|
1130
|
+
|
1131
|
+
#仅用于绘图和制表
|
1132
|
+
df1=df.copy()
|
1133
|
+
for c in list(df1):
|
1134
|
+
if df1[c].max() > axhline_value and df1[c].min() < axhline_value:
|
1135
|
+
axhline_label='零线'
|
1136
|
+
#df1.rename(columns={c:"基于无风险利率"+c},inplace=True)
|
1137
|
+
|
1138
|
+
#共同脚注
|
1139
|
+
footnote1="注:"+ectranslate(rar)+"基于"+ectranslate(ret_type)+'。'
|
1140
|
+
footnote2=""
|
1141
|
+
|
1142
|
+
footnote3=""
|
1143
|
+
if rar.lower() in ['treynor','alpha']:
|
1144
|
+
footnote3="贝塔系数基于日收益率,回归期间"+str(regression_period)+"个自然日"
|
1145
|
+
|
1146
|
+
import datetime; todaydt = datetime.date.today()
|
1147
|
+
footnote4="数据来源: 综合新浪/stooq/Yahoo,"+str(todaydt)+"统计"
|
1148
|
+
if footnote3 !='':
|
1149
|
+
footnotex=footnote1+footnote3+'\n'+footnote4
|
1150
|
+
else:
|
1151
|
+
footnotex=footnote1+footnote4
|
1152
|
+
|
1153
|
+
#绘图
|
1154
|
+
if graph:
|
1155
|
+
|
1156
|
+
title_txt="风险调整收益:"+ticker_name(ticker)
|
1157
|
+
y_label=ectranslate(rar)
|
1158
|
+
|
1159
|
+
draw_lines(df1,y_label,x_label=footnotex, \
|
1160
|
+
axhline_value=axhline_value,axhline_label=axhline_label, \
|
1161
|
+
title_txt=title_txt,data_label=False,annotate=annotate)
|
1162
|
+
|
1163
|
+
#制表
|
1164
|
+
recommenddf=pd.DataFrame()
|
1165
|
+
if printout:
|
1166
|
+
if sortby=='tpw_mean':
|
1167
|
+
sortby_txt='按推荐标记+近期优先加权平均值降序排列'
|
1168
|
+
elif sortby=='min':
|
1169
|
+
sortby_txt='按推荐标记+最小值降序排列'
|
1170
|
+
elif sortby=='mean':
|
1171
|
+
sortby_txt='按推荐标记+平均值降序排列'
|
1172
|
+
elif sortby=='median':
|
1173
|
+
sortby_txt='按推荐标记+中位数值降序排列'
|
1174
|
+
elif sortby=='trailing':
|
1175
|
+
sortby_txt='按推荐标记+短期均值走势降序排列'
|
1176
|
+
|
1177
|
+
title_txt='***** 风险调整收益评估:'+'基于'+ectranslate(rar)+','+ticker_name(ticker)+','+sortby_txt+' *****'
|
1178
|
+
|
1179
|
+
footnote6='期间范围:'+str(start)+'至'+str(end)+";近期范围:近"+str(trailing)+"个交易日。趋势变化率阈值:"+str(trend_threshhold)+"。"
|
1180
|
+
footnote7="近期趋势和星号为多项因素综合研判,最多五颗星星"
|
1181
|
+
footnotey=footnote6+footnote7+'\n'+footnotex
|
1182
|
+
|
1183
|
+
#删除含有Nan的行
|
1184
|
+
df1.dropna(inplace=True)
|
1185
|
+
|
1186
|
+
recommenddf=descriptive_statistics2(df1,title_txt,footnotey,decimals=4, \
|
1187
|
+
sortby=sortby,recommend_only=True,trailing=trailing, \
|
1188
|
+
trend_threshhold=trend_threshhold)
|
1189
|
+
|
1190
|
+
return df,recommenddf
|
1191
|
+
|
1192
|
+
#==============================================================================
|
1193
|
+
# 合成函数
|
1194
|
+
#==============================================================================
|
1195
|
+
if __name__=='__main__':
|
1196
|
+
ticker="600519.SS"
|
1197
|
+
ticker=["600519.SS","000858.SZ"]
|
1198
|
+
ticker={'Market':('US','^SPX','中概教培组合'),'EDU':0.7,'TAL':0.3}
|
1199
|
+
|
1200
|
+
start="2024-1-1"
|
1201
|
+
end="2024-3-22"
|
1202
|
+
|
1203
|
+
rar='alpha'
|
1204
|
+
rar=['sharpe','alpha']
|
1205
|
+
|
1206
|
+
ret_type="Annual Ret%"
|
1207
|
+
ret_type=["Monthly Ret%","Annual Ret%"]
|
1208
|
+
|
1209
|
+
RF=0.01759
|
1210
|
+
RF=[0.005,0.01759,0.05]
|
1211
|
+
|
1212
|
+
regression_period=365
|
1213
|
+
|
1214
|
+
graph=True; axhline_value=0; axhline_label=''
|
1215
|
+
printout=False; sortby='tpw_mean'; trailing=5; trend_threshhold=0.001
|
1216
|
+
annotate=False
|
1217
|
+
mktidx='auto'; source='auto'
|
1218
|
+
|
1219
|
+
rars=compare_rar_security(ticker,start,end,rar,ret_type,RF,printout=True)
|
1220
|
+
|
1221
|
+
|
1222
|
+
|
1223
|
+
rars=compare_rar_security(ticker,start,end,rar,ret_type,RF,printout=True)
|
1224
|
+
|
1225
|
+
def compare_rar_security(ticker,start,end,rar='sharpe', \
|
1226
|
+
ret_type="Annual Ret%", \
|
1227
|
+
RF=0, \
|
1228
|
+
regression_period=365, \
|
1229
|
+
graph=True,axhline_value=0,axhline_label='', \
|
1230
|
+
printout=False,sortby='tpw_mean',trailing=7,trend_threshhold=0.05, \
|
1231
|
+
annotate=False,mktidx='auto',source='auto'):
|
1232
|
+
"""
|
1233
|
+
功能:组合情况,可能多只股票,多个rar,多个收益率类型,多个无风险收益率
|
1234
|
+
|
1235
|
+
注意:trailing=7,trend_threshhold=0.05,更加贴合视觉效果
|
1236
|
+
"""
|
1237
|
+
|
1238
|
+
#情形1:多个证券
|
1239
|
+
if isinstance(ticker,list):
|
1240
|
+
if len(ticker) > 1:
|
1241
|
+
if isinstance(ret_type,list):
|
1242
|
+
ret_type=ret_type[0]
|
1243
|
+
if isinstance(RF,list):
|
1244
|
+
RF=RF[0]
|
1245
|
+
|
1246
|
+
rar_num=0
|
1247
|
+
if isinstance(rar,str):
|
1248
|
+
rar_num=1
|
1249
|
+
if isinstance(rar,list):
|
1250
|
+
rar_num=len(rar)
|
1251
|
+
if rar_num==1: rar=rar[0]
|
1252
|
+
|
1253
|
+
if rar_num ==1: #一个RAR
|
1254
|
+
df=compare_mticker_1rar(ticker,start,end,rar, \
|
1255
|
+
ret_type,RF,regression_period, \
|
1256
|
+
graph,axhline_value,axhline_label, \
|
1257
|
+
printout,sortby,trailing,trend_threshhold, \
|
1258
|
+
annotate,mktidx,source)
|
1259
|
+
return df
|
1260
|
+
|
1261
|
+
if rar_num >1: #多个RAR
|
1262
|
+
printout=True #否则无法运行descriptive_statistics2函数
|
1263
|
+
df=compare_mticker_mrar(ticker=ticker,start=start,end=end,rar=rar, \
|
1264
|
+
ret_type=ret_type,RF=RF,regression_period=regression_period, \
|
1265
|
+
graph=graph,axhline_value=axhline_value,axhline_label=axhline_label, \
|
1266
|
+
printout=printout,sortby=sortby,trailing=trailing,trend_threshhold=trend_threshhold, \
|
1267
|
+
annotate=annotate,mktidx=mktidx,source=source)
|
1268
|
+
return df
|
1269
|
+
else:
|
1270
|
+
#实际上是单个证券
|
1271
|
+
ticker=ticker[0]
|
1272
|
+
|
1273
|
+
#情形2:1只证券,多个RAR
|
1274
|
+
if isinstance(rar,list):
|
1275
|
+
if len(rar) > 1:
|
1276
|
+
if isinstance(ret_type,list):
|
1277
|
+
ret_type=ret_type[0]
|
1278
|
+
if isinstance(RF,list):
|
1279
|
+
RF=RF[0]
|
1280
|
+
|
1281
|
+
df=compare_1ticker_mrar(ticker,start,end,rar, \
|
1282
|
+
ret_type,RF,regression_period, \
|
1283
|
+
graph,axhline_value,axhline_label, \
|
1284
|
+
printout,sortby,trailing,trend_threshhold, \
|
1285
|
+
annotate,mktidx,source)
|
1286
|
+
return df
|
1287
|
+
else:
|
1288
|
+
#实际上是单个RAR
|
1289
|
+
rar=rar[0]
|
1290
|
+
|
1291
|
+
#情形3:1只证券,1个RAR,多个收益率类型
|
1292
|
+
if isinstance(ret_type,list):
|
1293
|
+
if len(ret_type) > 1:
|
1294
|
+
if isinstance(RF,list):
|
1295
|
+
RF=RF[0]
|
1296
|
+
|
1297
|
+
df=compare_1ticker_1rar_mret(ticker,start,end,rar, \
|
1298
|
+
ret_type,RF,regression_period, \
|
1299
|
+
graph,axhline_value,axhline_label, \
|
1300
|
+
printout,sortby,trailing,trend_threshhold, \
|
1301
|
+
annotate,mktidx,source)
|
1302
|
+
return df
|
1303
|
+
else:
|
1304
|
+
#实际上是单个收益率类型
|
1305
|
+
ret_type=ret_type[0]
|
1306
|
+
|
1307
|
+
#情形4:1只证券,1个RAR,1个收益率类型,多个RF
|
1308
|
+
if isinstance(RF,list):
|
1309
|
+
if len(RF) > 1:
|
1310
|
+
|
1311
|
+
df=compare_1ticker_1rar_1ret_mRF(ticker,start,end,rar, \
|
1312
|
+
ret_type, \
|
1313
|
+
RF, \
|
1314
|
+
regression_period, \
|
1315
|
+
graph,axhline_value,axhline_label, \
|
1316
|
+
printout,sortby,trailing,trend_threshhold, \
|
1317
|
+
annotate,mktidx,source)
|
1318
|
+
return df
|
1319
|
+
else:
|
1320
|
+
#实际上是单个RF
|
1321
|
+
RF=RF[0]
|
1322
|
+
|
1323
|
+
#情形5:1只证券,1个RAR,1个收益率类型,1个RF
|
1324
|
+
df=compare_1ticker_mrar(ticker,start,end,rar, \
|
1325
|
+
ret_type,RF,regression_period, \
|
1326
|
+
graph,axhline_value,axhline_label, \
|
1327
|
+
printout,sortby,trailing,trend_threshhold, \
|
1328
|
+
annotate,mktidx,source)
|
1329
|
+
return df
|
1330
|
+
|
1331
|
+
|
1332
|
+
#==============================================================================
|
1333
|
+
#==============================================================================
|
1334
|
+
|
1335
|
+
#==============================================================================
|
1336
|
+
#==============================================================================
|
1337
|
+
#==============================================================================
|
1338
|
+
#==============================================================================
|
1339
|
+
#==============================================================================
|