siat 3.10.25__py3-none-any.whl → 3.10.125__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.
@@ -4,7 +4,7 @@
4
4
  所属工具包:证券投资分析工具SIAT
5
5
  SIAT:Security Investment Analysis Tool
6
6
  创建日期:2018年10月16日
7
- 最新修订日期:2020817
7
+ 最新修订日期:2025620
8
8
  作者:王德宏 (WANG Dehong, Peter)
9
9
  作者单位:北京外国语大学国际商学院
10
10
  作者邮件:wdehong2000@163.com
@@ -65,8 +65,9 @@ def calc_treynor_ratio(regdf):
65
65
  #计算特雷诺指数
66
66
  tr=ret_rf_mean/beta
67
67
 
68
- ret_mean=regdf['Close'].mean()
69
- return tr,ret_mean
68
+ #ret_mean=regdf['Ret%'].mean()
69
+ rp_mean=ret_rf_mean
70
+ return tr,rp_mean,beta
70
71
 
71
72
  #==============================================================================
72
73
  def calc_alpha_ratio(regdf):
@@ -75,17 +76,15 @@ def calc_alpha_ratio(regdf):
75
76
  输入:数据框,至少含有Ret-Rf和Mkt-Rf两项
76
77
  输出:詹森阿尔法指数,Ret-Rf均值
77
78
  """
78
- """
79
79
  #计算风险溢价Ret-RF均值
80
80
  ret_rf_mean=regdf['Ret-RF'].mean()
81
- """
82
81
  #使用CAPM回归计算投资组合的贝塔系数,这里得到的alpha就是Jensen's alpha
83
82
  from scipy import stats
84
83
  output=stats.linregress(regdf['Mkt-RF'],regdf['Ret-RF'])
85
84
  (beta,alpha,r_value,p_value,std_err)=output
86
85
 
87
- ret_mean=regdf['Close'].mean()
88
- return alpha,ret_mean
86
+ rp_mean=ret_rf_mean
87
+ return alpha,rp_mean,beta
89
88
 
90
89
  #==============================================================================
91
90
  def calc_sharpe_ratio(regdf):
@@ -101,8 +100,9 @@ def calc_sharpe_ratio(regdf):
101
100
  #计算夏普指数
102
101
  sr=ret_rf_mean/ret_rf_std
103
102
 
104
- ret_mean=regdf['Close'].mean()
105
- return sr,ret_mean
103
+ rp_mean=ret_rf_mean
104
+ beta=False
105
+ return sr,rp_mean,beta
106
106
 
107
107
  if __name__=='__main__':
108
108
  rfd=rf_daily_china('2021-10-1','2021-11-28',rate_period='1Y',rate_type='shibor')
@@ -131,15 +131,18 @@ def calc_sortino_ratio(regdf):
131
131
  #计算索梯诺指数
132
132
  sr=ret_rf_mean/ret_rf_lpsd
133
133
 
134
- ret_mean=regdf['Close'].mean()
135
- return sr,ret_mean
134
+ rp_mean=ret_rf_mean
135
+ beta=False
136
+ return sr,rp_mean,beta
136
137
 
137
138
  #==============================================================================
138
- def print_rar_ratio(regdf,portfolio,ret_mean,ratio_name,ratio):
139
+ def print_rar_ratio(regdf,portfolio,rp_mean,beta,ratio_name,ratio):
139
140
  """
140
141
  功能:打印风险调整后的收益率
141
- 输入:数据框,投资组合构成,收益溢价均值,指数名称,指数
142
+ 输入:数据框,投资组合构成,收益溢价均值,贝塔系数,指数名称,指数
142
143
  输出:打印
144
+
145
+ 注意:若贝塔系数为False则不打印
143
146
  """
144
147
 
145
148
  #从字典中提取信息
@@ -150,32 +153,41 @@ def print_rar_ratio(regdf,portfolio,ret_mean,ratio_name,ratio):
150
153
  '-'+str(regdf.index[0].day)
151
154
  date_end=str(regdf.index[-1].year)+'-'+str(regdf.index[-1].month)+ \
152
155
  '-'+str(regdf.index[-1].day)
153
- print("\n===== 风险调整收益率 =====")
154
- print("市场指数:",ectranslate(scope),'\b,',ticker_name(mktidx))
156
+ print("\n======== 风险调整收益率 ========")
157
+ print("证券资产:",portfolio_name(portfolio))
158
+ #print("市场指数:",ectranslate(scope),'\b,',ticker_name(mktidx))
159
+ print("市场指数:",ticker_name(mktidx))
155
160
  #print("成分股 :",ticker_name(stocklist))
156
161
  #print("持仓权重:",portionlist)
157
- print("计算期间:",date_start,"至",date_end)
162
+ print("样本期间:",date_start,"至",date_end)
158
163
  """
159
164
  print("日均收益率:",round(ret_mean,4),'\b%')
160
- """
161
165
  annual_ret=(1+ret_mean/100)**252-1
162
166
  print("年化收益率:",round(annual_ret,4))
163
- #print(ratio_name.capitalize(),"\b比率:",round(ratio,4),'\b%')
164
- print(ratio_name.capitalize(),"\b比率:",round(ratio,4))
167
+ """
168
+ if not isinstance(beta,bool):
169
+ print("贝塔系数:",round(beta,4))
170
+
171
+ print("风险溢价均值%:",round(rp_mean,4))
165
172
 
173
+ #print(ratio_name.capitalize(),"\b比率:",round(ratio,4),'\b%')
174
+ print(ratio_name.capitalize(),"\b比率%:",round(ratio,4))
175
+ """
166
176
  print("***投资组合构成:")
167
177
  print_tickerlist_sharelist(stocklist,portionlist,2)
168
-
178
+ """
169
179
 
170
- import datetime as dt; today=dt.date.today()
171
- print("*来源:新浪/stooq/FRED, "+str(today))
180
+ import datetime as dt; todaydt=dt.date.today()
181
+ print("数据来源:新浪/stooq, "+str(todaydt))
172
182
 
173
183
  return
174
184
  #==============================================================================
175
185
  if __name__=='__main__':
176
- start='2021-1-1'
177
- end='2021-11-30'
178
- portfolio={'Market':('US','^GSPC'),'EDU':0.4,'TAL':0.3,'TEDU':0.2,'RYB':0.1}
186
+ portfolio={'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
187
+ start='2024-6-1'
188
+ end='2025-5-30'
189
+ RF=0.04
190
+ printout=True
179
191
 
180
192
  rate_period='ON'
181
193
 
@@ -200,31 +212,43 @@ def treynor_ratio_portfolio(portfolio,start,end,RF=True,printout=True):
200
212
 
201
213
  #从字典中提取信息
202
214
  scope,mktidx,stocklist,portionlist,ticker_type=decompose_portfolio(portfolio)
203
-
204
- #第2步:计算投资组合的日收益率序列
205
- #抓取日投资组合价格
206
- sp=get_portfolio_prices(portfolio,start,end)
215
+
216
+ #第2步:获得无风险收益率/市场收益率序列
217
+ #获得期间的日无风险收益率(抓取的RF为百分比)
218
+ if isinstance(RF,bool):
219
+ print(" Searching for risk-free interest rate ...")
220
+ if scope=='China':
221
+ rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
222
+ else:
223
+ rf_df=get_rf(start,end,scope=scope,freq='daily')
224
+ if rf_df is None:
225
+ message=" #Error("+func_name+"): "+"no data available for rf in"
226
+ print(message,scope,start,end)
227
+ return None,None
228
+ RF=rf_df['RF'].mean()
229
+
230
+ #第3步:计算投资组合的日收益率序列
231
+ #抓取日投资组合价格:内含Mkt-RF和RF
232
+ sp=get_portfolio_prices(portfolio,start,end,RF=RF)
207
233
  #计算日收益率,表示为百分比
234
+ """
208
235
  import pandas as pd
209
236
  ret_pf=pd.DataFrame(sp['Close'].pct_change())*100.0
210
237
  ret_pf=ret_pf.dropna()
211
-
212
- #第3步:获得无风险收益率/市场收益率序列
213
- #获得期间的日无风险收益率(抓取的RF为百分比)
214
- print(" Searching for risk-free interest rate ...")
215
- if scope=='China':
216
- rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
217
- else:
218
- rf_df=get_rf(start,end,scope=scope,freq='daily')
219
- if rf_df is None:
220
- message=" #Error("+func_name+"): "+"no data available for rf in"
221
- print(message,scope,start,end)
222
- return None,None
238
+ """
239
+ ret_pf=sp
223
240
 
224
241
  #第4步:合并投资组合日收益率与无风险利率/市场收益率序列
225
- #合并rf_df与ret_pf
226
- reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
242
+ """
243
+ if isinstance(RF,bool):
244
+ #合并rf_df与ret_pf
245
+ reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
246
+ else:
247
+ reg=ret_pf
248
+ reg['RF']=RF/365 #日度无风险收益率%
227
249
  reg['Ret-RF']=reg['Close']-reg['RF']
250
+ """
251
+ reg=ret_pf
228
252
  reg=reg.dropna()
229
253
  if len(reg) == 0:
230
254
  message=" #Error("+func_name+"): "+"empty ret-rf data for regression"
@@ -233,14 +257,14 @@ def treynor_ratio_portfolio(portfolio,start,end,RF=True,printout=True):
233
257
 
234
258
  #第5步:计算风险调整后的收益率
235
259
  ##########风险调整后的收益率,计算开始##########
236
- tr,ret_mean=calc_treynor_ratio(reg)
260
+ tr,rp_mean,beta=calc_treynor_ratio(reg)
237
261
  ##########风险调整后的收益率,计算结束##########
238
262
 
239
263
  #第6步:打印结果
240
264
  if printout == True:
241
- print_rar_ratio(reg,portfolio,ret_mean,ratio_name,tr)
265
+ print_rar_ratio(reg,portfolio,rp_mean,beta,ratio_name,tr)
242
266
 
243
- return tr,ret_mean
267
+ return tr,rp_mean
244
268
 
245
269
 
246
270
  if __name__=='__main__':
@@ -250,79 +274,122 @@ if __name__=='__main__':
250
274
 
251
275
  #==============================================================================
252
276
  if __name__=='__main__':
253
- portfolio={'Market':('US','^GSPC'),'EDU':0.4,'TAL':0.3,'TEDU':0.2,'RYB':0.1}
254
- start='2019-06-01'
255
- end ='2019-06-30'
256
- scope='US'
277
+ portfolio={'Market':('US','^GSPC'),'EDU':0.6,'TAL':0.4}
278
+ start='2025-1-01'
279
+ end ='2025-5-30'
280
+ RF=0.04; printout=True
281
+ indicator='sharpe'
282
+ indicator='alpha'
257
283
 
258
284
 
259
- def rar_ratio_portfolio(portfolio,start,end,ratio_name='treynor',RF=True,printout=True):
285
+ def rar_ratio_portfolio(portfolio,start='MRY',end='today', \
286
+ indicator='sharpe', \
287
+ RF=0,printout=True):
260
288
  """
261
289
  功能:按天计算一个投资组合的风险调整后的收益率指数
262
290
  投资组合的结构:{'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
263
- 输入:投资组合,开始日期,结束日期
291
+ 输入:投资组合,开始日期,结束日期,rar种类
264
292
  输出:风险调整后的收益率指数
265
293
  """
294
+ ratio_name=indicator
266
295
 
267
296
  #第1步:各种准备和检查工作
268
297
  #设定错误信息的函数名
269
298
  func_name='rar_ratio_portfolio'
270
299
 
300
+ ratio_name=ratio_name.lower()
271
301
  ratio_list=['treynor','sharpe','sortino','alpha']
272
302
  if ratio_name not in ratio_list:
273
303
  message=" #Error("+func_name+"): "+"unsupported rar ratio type"
274
304
  print(message)
275
- return None,None
305
+ return None,None
306
+
307
+ start,end=start_end_preprocess(start,end)
276
308
  result,startdate,enddate=check_period(start,end)
277
309
  if not result:
278
310
  message=" #Error("+func_name+"): "+"invalid start or end date"
279
311
  print(message,start,end)
280
312
  return None,None
281
313
 
314
+ print(f" Calculating {ratio_name} ratio ...")
282
315
  #从字典中提取信息
283
316
  scope,mktidx,stocklist,portionlist,ticker_type=decompose_portfolio(portfolio)
317
+
318
+ #第2步:获得无风险收益率/市场收益率序列
319
+ #获得期间的日无风险收益率(抓取的RF为百分比)
320
+ rf_value_flag=True #RF以数值形式给出
321
+ if isinstance(RF,bool):
322
+ rf_value_flag=False
323
+ if RF:
324
+ print(" Searching for risk-free interest rate ...")
325
+ if scope=='China':
326
+ rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
327
+ else:
328
+ rf_df=get_rf(start,end,scope=scope,freq='daily')
329
+ if rf_df is None:
330
+ message=" #Error("+func_name+"): "+"no data available for rf in"
331
+ print(message,scope,start,end)
332
+ return None,None
333
+ RF=rf_df['RF'].mean()
334
+ else:
335
+ RF=0
336
+ rf_value_flag=True
337
+
338
+ #第3步:计算投资组合的日收益率序列
339
+ import os,sys
340
+ class HiddenPrints:
341
+ def __enter__(self):
342
+ self._original_stdout = sys.stdout
343
+ sys.stdout = open(os.devnull, 'w')
344
+
345
+ def __exit__(self, exc_type, exc_val, exc_tb):
346
+ sys.stdout.close()
347
+ sys.stdout = self._original_stdout
284
348
 
285
- #第2步:计算投资组合的日收益率序列
286
349
  #抓取日投资组合价格
287
- sp=get_portfolio_prices(portfolio,startdate,enddate)
350
+ with HiddenPrints():
351
+ sp=get_portfolio_prices(portfolio,startdate,enddate,RF=RF)
352
+ if sp is None:
353
+ print(" #Error(rar_ratio_portfolio): failed to retrieve portfolio information")
354
+ return None,None
355
+ if len(sp) == 0:
356
+ print(" #Error(rar_ratio_portfolio): no portfolio information found during the period")
357
+ return None,None
358
+ """
288
359
  #计算日收益率,表示为百分比
289
360
  import pandas as pd
290
361
  ret_pf=pd.DataFrame(sp['Close'].pct_change())*100.0
291
362
  ret_pf=ret_pf.dropna()
292
-
293
- #第3步:获得无风险收益率/市场收益率序列
294
- #获得期间的日无风险收益率(抓取的RF为百分比)
295
- print(" Searching for risk-free interest rate ...")
296
- if scope=='China':
297
- rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
298
- else:
299
- rf_df=get_rf(start,end,scope=scope,freq='daily')
300
- if rf_df is None:
301
- message=" #Error("+func_name+"): "+"no data available for rf in"
302
- print(message,scope,start,end)
303
- return None,None
304
363
 
305
364
  #第4步:合并投资组合日收益率与无风险利率/市场收益率序列
306
- #合并rf_df与ret_pf
307
- reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
365
+ if not rf_value_flag:
366
+ #合并rf_df与ret_pf
367
+ reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
368
+
369
+ else:
370
+ ret_pf['RF']=RF
371
+ reg=ret_pf
372
+
308
373
  reg['Ret-RF']=reg['Close']-reg['RF']
374
+ """
375
+ reg=sp
309
376
  reg=reg.dropna()
310
377
  if len(reg) == 0:
311
378
  message=" #Error("+func_name+"): "+"empty data for ratio calculation"
312
379
  print(message)
313
380
  return None,None
314
381
 
315
- #第5步:计算风险调整后的收益率
382
+ #第4步:计算风险调整后的收益率
316
383
  ##########风险调整后的收益率,计算开始##########
317
384
  calc_func='calc_'+ratio_name+'_ratio'
318
- rar,ret_mean=eval(calc_func)(reg)
385
+ rar,rp_mean,beta=eval(calc_func)(reg)
319
386
  ##########风险调整后的收益率,计算结束##########
320
387
 
321
- #第6步:打印结果
388
+ #第5步:打印结果
322
389
  if printout == True:
323
- print_rar_ratio(reg,portfolio,ret_mean,ratio_name,rar)
390
+ print_rar_ratio(reg,portfolio,rp_mean,beta,ratio_name,rar)
324
391
 
325
- return rar,ret_mean
392
+ return rar,rp_mean
326
393
 
327
394
 
328
395
  if __name__=='__main__':
@@ -339,7 +406,7 @@ if __name__=='__main__':
339
406
  window=30
340
407
  graph=True
341
408
 
342
- def rar_ratio_rolling(portfolio,start,end,ratio_name='treynor',RF=True, \
409
+ def rar_ratio_rolling(portfolio,start='MRY',end='today',indicator='sharpe',RF=0, \
343
410
  window=21,graph=True,source='auto'):
344
411
  """
345
412
  功能:滚动计算一个投资组合的风险调整后的收益率指数
@@ -351,6 +418,9 @@ def rar_ratio_rolling(portfolio,start,end,ratio_name='treynor',RF=True, \
351
418
 
352
419
  注意:当RF=False时有bug
353
420
  """
421
+ start,end=start_end_preprocess(start,end)
422
+
423
+ ratio_name=indicator.lower()
354
424
 
355
425
  #第1步:各种准备和检查工作
356
426
  print(" Start to calculate rar ratios, please wait ...")
@@ -369,48 +439,69 @@ def rar_ratio_rolling(portfolio,start,end,ratio_name='treynor',RF=True, \
369
439
  print(message,start,end)
370
440
  return None
371
441
  #估算数据提前量,重设开始日历日期
372
- startdate_delta=int(window/2*3)+30
442
+ #startdate_delta=int(window/20*30)+30
443
+ startdate_delta=int(window/20*31)
373
444
  startdate1=date_adjust(startdate, adjust=-startdate_delta)
374
445
 
375
446
  #从字典中提取信息
376
447
  scope,mktidx,stocklist,portionlist,ticker_type=decompose_portfolio(portfolio)
377
-
378
- #第2步:计算投资组合的日收益率序列
448
+ pname=portfolio_name(portfolio)
449
+ if pname == '': pname="投资组合"
450
+
451
+ #第2步:获得无风险收益率/市场收益率序列
452
+ #获得期间的日无风险收益率(抓取的RF为百分比)
453
+ rf_value_flag=True #RF以数值形式给出
454
+ if isinstance(RF,bool):
455
+ rf_value_flag=False
456
+ if RF:
457
+ print(" Searching for risk-free interest rate ...")
458
+ if scope=='China':
459
+ rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
460
+ else:
461
+ rf_df=get_rf(start,end,scope=scope,freq='daily')
462
+ if rf_df is None:
463
+ message=" #Error("+func_name+"): "+"no data available for rf in"
464
+ print(message,scope,start,end)
465
+ return None,None
466
+ RF=rf_df['RF'].mean()
467
+ else:
468
+ RF=0
469
+ rf_value_flag=True
470
+
471
+ #第3步:计算投资组合的日收益率序列
379
472
  #抓取日投资组合价格
380
- sp=get_portfolio_prices(portfolio,startdate1,enddate,source=source)
381
- if sp is None:
382
- message=" #Error("+func_name+"): "+"failed to retrieve portfolio info, pls try later"
383
- print(message)
384
- return None
473
+ sp=get_portfolio_prices(portfolio,startdate1,enddate,RF=RF)
474
+ if sp is None:
475
+ print(" #Error(rar_ratio_portfolio): failed to retrieve portfolio information")
476
+ return None,None
477
+ if len(sp) == 0:
478
+ print(" #Error(rar_ratio_portfolio): no portfolio information found during the period")
479
+ return None,None
480
+ """
385
481
  #计算日收益率,表示为百分比
386
482
  import pandas as pd
387
483
  ret_pf=pd.DataFrame(sp['Close'].pct_change())*100.0
388
484
  ret_pf=ret_pf.dropna()
389
-
390
- #第3步:获得无风险收益率/市场收益率序列
391
- #获得期间的日无风险收益率(抓取的RF为百分比)
392
- #print(" Searching for risk-free interest rate ...")
393
- if scope=='China':
394
- rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
395
- else:
396
- rf_df=get_rf(start,end,scope=scope,freq='daily')
397
- if rf_df is None:
398
- message=" #Error("+func_name+"): "+"no data available for rf in"
399
- print(message,scope,start,end)
400
- return None,None
401
485
 
402
486
  #第4步:合并投资组合日收益率与无风险利率/市场收益率序列
403
- #合并rf_df与ret_pf
404
- reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
405
- if len(reg) == 0:
406
- message=" #Error("+func_name+"): "+"empty data for ratio calculation"
407
- print(message)
408
- return None
487
+ if not rf_value_flag:
488
+ #合并rf_df与ret_pf
489
+ reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
490
+
491
+ else:
492
+ ret_pf['RF']=RF
493
+ reg=ret_pf
409
494
 
410
495
  reg['Ret-RF']=reg['Close']-reg['RF']
496
+ """
497
+ reg=sp
411
498
  reg=reg.dropna()
499
+ if len(reg) == 0:
500
+ message=" #Error("+func_name+"): "+"empty data for ratio calculation"
501
+ print(message)
502
+ return None,None
412
503
 
413
- #第5步:滚动计算风险调整后的收益率
504
+ #第4步:滚动计算风险调整后的收益率
414
505
  ##########风险调整后的收益率,计算开始##########
415
506
  #用于保存rar和ret_rf_mean
416
507
  import pandas as pd
@@ -447,10 +538,10 @@ def rar_ratio_rolling(portfolio,start,end,ratio_name='treynor',RF=True, \
447
538
  rars.set_index(['Date'],inplace=True)
448
539
  ##########风险调整后的收益率,计算结束##########
449
540
 
450
- #第6步:绘图
541
+ #第5步:绘图
451
542
  if graph == True:
452
543
  print(" Rendering graphics ...")
453
- draw_rar_ratio(rars,portfolio,ratio_name)
544
+ draw_rar_ratio(rars,portfolio,ratio_name,pname)
454
545
 
455
546
  return rars
456
547
 
@@ -459,7 +550,7 @@ if __name__=='__main__':
459
550
  pf1={'Market':('US','^GSPC'),'AAPL':0.5,'MSFT':0.3,'IBM':0.2}
460
551
  rars1=rar_ratio_rolling(pf1,'2020-1-1','2020-12-31',ratio_name='sharpe')
461
552
  #==============================================================================
462
- def draw_rar_ratio(rars,portfolio,ratio_name):
553
+ def draw_rar_ratio(rars,portfolio,ratio_name,pname):
463
554
  """
464
555
  功能:绘制滚动窗口曲线
465
556
  输入:滚动数据df,投资组合,指数名称
@@ -482,11 +573,13 @@ def draw_rar_ratio(rars,portfolio,ratio_name):
482
573
  plt.plot(rars['RAR'],label=labeltxt,color='red',lw=1)
483
574
  #plt.plot(rars['Mean(Ret)'],label='Stock(s) return(%)',color='blue',lw=1)
484
575
  plt.axhline(y=0.0,color='black',linestyle=':')
485
-
576
+ """
486
577
  titletxt='风险调整收益的滚动趋势'+'\n'+str(ticker_name(stocklist))
487
578
  if len(stocklist) > 1:
488
579
  titletxt=titletxt+'\n持仓比例: '+str(portionlist)
489
580
  """
581
+ titletxt='风险调整收益的滚动趋势:'+pname
582
+ """
490
583
  if len(stocklist) == 1:
491
584
  titletxt='风险调整收益的滚动趋势'+'\n('+ticker_name(stocklist)+')'
492
585
  """
@@ -554,35 +647,43 @@ def sharpe_ratio_portfolio(portfolio,start,end,RF=True,printout=True):
554
647
  return None,None
555
648
  """
556
649
 
557
- #抓取日投资组合价格
558
- sp=get_portfolio_prices(portfolio,start,end)
650
+ #获得期间的无风险收益率
651
+ if isinstance(RF,bool):
652
+ print(" Searching for risk-free interest rate ...")
653
+ if scope=='China':
654
+ rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
655
+ else:
656
+ rf_df=get_rf(start,end,scope=scope,freq='daily')
657
+ if rf_df is None:
658
+ message=" #Error("+func_name+"): "+"no data available for rf in"
659
+ print(message,scope,start,end)
660
+ return None,None
661
+ RF=rf_df['RF'].mean()
662
+
663
+ #抓取日投资组合价格:内含Mkt-RF和RF
664
+ sp=get_portfolio_prices(portfolio,start,end,RF=RF)
559
665
  #计算日收益率,表示为百分比
666
+ """
560
667
  import pandas as pd
561
668
  ret_pf=pd.DataFrame(sp['Close'].pct_change())*100.0
669
+ """
670
+ ret_pf=sp
562
671
  ret_pf=ret_pf.dropna()
563
-
564
- #获得期间的无风险收益率
565
- print(" Searching for risk-free interest rate ...")
566
- if scope=='China':
567
- rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
568
- else:
569
- rf_df=get_rf(start,end,scope=scope,freq='daily')
570
- if rf_df is None:
571
- message=" #Error("+func_name+"): "+"no data available for rf in"
572
- print(message,scope,start,end)
573
- return None,None
574
672
 
575
673
  #强制转换索引格式,彻底消除下面并表的潜在隐患
674
+ """
576
675
  rf_df['ffdate']=rf_df.index.astype('str')
577
676
  rf_df['ffdate']=pd.to_datetime(rf_df['ffdate'])
578
677
  rf_df.set_index(['ffdate'],inplace=True)
579
-
678
+ """
679
+ """
580
680
  #合并rf_df与ret_pf
581
681
  reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
582
682
  reg['Ret-RF']=reg['Close']-reg['RF']
583
683
  reg=reg.dropna()
584
-
684
+ """
585
685
  #计算风险溢价Ret-RF均值和标准差
686
+ reg=ret_pf
586
687
  ret_rf_mean=reg['Ret-RF'].mean()
587
688
  ret_rf_std=reg['Ret-RF'].std()
588
689
 
@@ -596,20 +697,23 @@ def sharpe_ratio_portfolio(portfolio,start,end,RF=True,printout=True):
596
697
  date_end=str(reg.index[-1].year)+'-'+str(reg.index[-1].month)+ \
597
698
  '-'+str(reg.index[-1].day)
598
699
  print("\n===== 风险调整收益率 =====")
599
-
700
+ """
600
701
  _,_,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
601
702
  if len(tickerlist)==1:
602
703
  product=str(ticker_name(tickerlist,'bond'))
603
704
  else:
604
705
  product=str(ticker_name(tickerlist,'bond'))+' by '+str(sharelist)
605
- print("投资组合:",product)
606
- print("计算期间:",date_start,"至",date_end,"(可用日期)")
607
- print("风险溢价均值(%):",round(ret_rf_mean,4))
608
- print("夏普比率(%) :",round(sr,4))
706
+ """
707
+ print("证券资产:",portfolio_name(portfolio))
708
+ print("样本期间:",date_start,"至",date_end,"(可用日期)")
709
+ print("风险溢价均值%:",round(ret_rf_mean,4))
710
+ print("风险溢价标准差%:",round(ret_rf_std,4))
711
+ print("夏普比率%:",round(sr,4))
609
712
  import datetime as dt; today=dt.date.today()
610
713
  print("*数据来源:新浪/stooq/FRED,"+str(today))
611
714
 
612
- return sr,ret_rf_mean
715
+ beta=False
716
+ return sr,ret_rf_mean,beta
613
717
 
614
718
 
615
719
  if __name__=='__main__':
@@ -649,24 +753,28 @@ def sortino_ratio_portfolio(portfolio,start,end,RF=True,printout=True):
649
753
  return None,None
650
754
  """
651
755
 
756
+ #获得期间的无风险收益率
757
+ if isinstance(RF,bool):
758
+ print(" Searching for risk-free interest rate ...")
759
+ if scope=='China':
760
+ rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
761
+ else:
762
+ rf_df=get_rf(start,end,scope=scope,freq='daily')
763
+ if rf_df is None:
764
+ message=" #Error("+func_name+"): "+"no data available for rf in"
765
+ print(message,scope,start,end)
766
+ return None,None
767
+ RF=rf_df['RF'].mean()
768
+
652
769
  #抓取日投资组合价格
653
- sp=get_portfolio_prices(portfolio,start,end)
770
+ sp=get_portfolio_prices(portfolio,start,end,RF=RF)
771
+ ret_pf=sp
772
+ """
654
773
  #计算日收益率,表示为百分比
655
774
  import pandas as pd
656
775
  ret_pf=pd.DataFrame(sp['Close'].pct_change())*100.0
657
776
  ret_pf=ret_pf.dropna()
658
777
 
659
- #获得期间的无风险收益率
660
- print(" Searching for risk-free interest rate ...")
661
- if scope=='China':
662
- rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
663
- else:
664
- rf_df=get_rf(start,end,scope=scope,freq='daily')
665
- if rf_df is None:
666
- message=" #Error("+func_name+"): "+"no data available for rf in"
667
- print(message,scope,start,end)
668
- return None,None
669
-
670
778
  #强制转换索引格式,彻底消除下面并表的潜在隐患
671
779
  rf_df['ffdate']=rf_df.index.astype('str')
672
780
  rf_df['ffdate']=pd.to_datetime(rf_df['ffdate'])
@@ -675,6 +783,8 @@ def sortino_ratio_portfolio(portfolio,start,end,RF=True,printout=True):
675
783
  #合并rf_df与ret_pf
676
784
  reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
677
785
  reg['Ret-RF']=reg['Close']-reg['RF']
786
+ """
787
+ reg=ret_pf
678
788
  reg=reg.dropna()
679
789
 
680
790
  #计算风险溢价Ret-RF均值和下偏标准差LPSD
@@ -692,17 +802,18 @@ def sortino_ratio_portfolio(portfolio,start,end,RF=True,printout=True):
692
802
  date_end=str(reg.index[-1].year)+'-'+str(reg.index[-1].month)+ \
693
803
  '-'+str(reg.index[-1].day)
694
804
  print("\n===== 风险调整收益率 =====")
695
-
805
+ """
696
806
  _,_,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
697
807
  if len(tickerlist)==1:
698
808
  product=str(ticker_name(tickerlist,'bond'))
699
809
  else:
700
810
  product=str(ticker_name(tickerlist,'bond'))+' by '+str(sharelist)
701
- print("投资组合:",product)
702
- print("计算期间:",date_start,"至",date_end, \
703
- "(可用日期)")
704
- print("风险溢价均值(%):",round(ret_rf_mean,4))
705
- print("索替诺比率(%) :",round(sr,4))
811
+ """
812
+ print("证券资产:",portfolio_name(portfolio))
813
+ print("样本期间:",date_start,"至",date_end,"(可用日期)")
814
+ print("风险溢价均值%:",round(ret_rf_mean,4))
815
+ print("下偏标准差%:",round(ret_rf_lpsd,4))
816
+ print("索替诺比率%:",round(sr,4))
706
817
 
707
818
  import datetime as dt; today=dt.date.today()
708
819
  print("*数据来源:新浪/stooq/FRED,"+str(today))
@@ -746,24 +857,28 @@ def jensen_alpha_portfolio(portfolio,start,end,RF=True,printout=True):
746
857
  return None,None
747
858
  """
748
859
 
749
- #抓取日投资组合价格
750
- sp=get_portfolio_prices(portfolio,start,end)
860
+ #获得期间的无风险收益率
861
+ if isinstance(RF,bool):
862
+ print(" Searching for risk-free interest rate ...")
863
+ if scope=='China':
864
+ rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
865
+ else:
866
+ rf_df=get_rf(start,end,scope=scope,freq='daily')
867
+ if rf_df is None:
868
+ message=" #Error("+func_name+"): "+"no data available for rf in"
869
+ print(message,scope,start,end)
870
+ return None,None
871
+ RF=rf_df['RF'].mean()
872
+
873
+ #抓取日投资组合价格:内含Mkt-RF和RF
874
+ sp=get_portfolio_prices(portfolio,start,end,RF=RF)
751
875
  #计算日收益率,表示为百分比
876
+ ret_pf=sp
877
+ """
752
878
  import pandas as pd
753
879
  ret_pf=pd.DataFrame(sp['Close'].pct_change())*100.0
754
880
  ret_pf=ret_pf.dropna()
755
881
 
756
- #获得期间的无风险收益率
757
- print(" Searching for risk-free interest rate ...")
758
- if scope=='China':
759
- rf_df=get_mkt_rf_daily_china(mktidx,start,end,rate_period='1Y',rate_type='shibor',RF=RF)
760
- else:
761
- rf_df=get_rf(start,end,scope=scope,freq='daily')
762
- if rf_df is None:
763
- message=" #Error("+func_name+"): "+"no data available for rf in"
764
- print(message,scope,start,end)
765
- return None,None
766
-
767
882
  #强制转换索引格式,彻底消除下面并表的潜在隐患
768
883
  rf_df['ffdate']=rf_df.index.astype('str')
769
884
  rf_df['ffdate']=pd.to_datetime(rf_df['ffdate'])
@@ -779,6 +894,8 @@ def jensen_alpha_portfolio(portfolio,start,end,RF=True,printout=True):
779
894
  #合并rf_df与ret_pf
780
895
  reg=pd.merge(ret_pf,rf_df,how='inner',left_index=True,right_index=True)
781
896
  reg['Ret-RF']=reg['Close']-reg['RF']
897
+ """
898
+ reg=ret_pf
782
899
  reg=reg.dropna()
783
900
  if len(reg) == 0:
784
901
  print(" #Error(jensen_alpha_portfolio): empty data for regression.")
@@ -797,21 +914,23 @@ def jensen_alpha_portfolio(portfolio,start,end,RF=True,printout=True):
797
914
  date_end=str(reg.index[-1].year)+'-'+str(reg.index[-1].month)+ \
798
915
  '-'+str(reg.index[-1].day)
799
916
  print("\n===== 风险调整收益率 =====")
800
-
917
+ """
801
918
  _,_,tickerlist,sharelist,ticker_type=decompose_portfolio(portfolio)
802
919
  if len(tickerlist)==1:
803
920
  product=str(ticker_name(tickerlist,'bond'))
804
921
  else:
805
922
  product=str(ticker_name(tickerlist,'bond'))+' by '+str(sharelist)
806
- print("投资组合:",product)
807
- print("计算期间:",date_start,"至",date_end,"(可用日期)")
808
- print("风险溢价均值(%):",round(ret_rf_mean,4))
809
- print("詹森阿尔法(%) :",round(alpha,4))
923
+ """
924
+ print("证券资产:",portfolio_name(portfolio))
925
+ print("样本期间:",date_start,"至",date_end,"(可用日期)")
926
+ print("贝塔系数:",round(beta,4))
927
+ print("风险溢价均值%:",round(ret_rf_mean,4))
928
+ print("詹森阿尔法%:",round(alpha,4))
810
929
 
811
930
  import datetime as dt; today=dt.date.today()
812
931
  print("*数据来源:新浪/stooq/FRED,"+str(today))
813
932
 
814
- return alpha,ret_rf_mean
933
+ return alpha,ret_rf_mean,beta
815
934
 
816
935
 
817
936
  if __name__=='__main__':