siat 3.0.35__py3-none-any.whl → 3.0.40__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.
@@ -181,7 +181,7 @@ def get_expanding_sharpe_sortino(ticker,start,end,rar_name="sharpe", \
181
181
  #增加距离开始日期的天数
182
182
  date0=pd.to_datetime(rardf2.index[0])
183
183
  if 'date' not in list(rardf2):
184
- if 'Date' not in list(rardf2):
184
+ if 'Date' in list(rardf2):
185
185
  rardf2['date']=rardf2['Date']
186
186
  else:
187
187
  rardf2['date']=rardf2.index
@@ -204,6 +204,7 @@ def get_expanding_sharpe_sortino(ticker,start,end,rar_name="sharpe", \
204
204
 
205
205
  #rardf2[risk_type]=rardf2[ret_type].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1)*np.sqrt(len(x)-1))
206
206
  rardf2[risk_type]=rardf2[ret_type].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1))
207
+ #rardf2[risk_type]=rardf2[ret_type].expanding(min_periods=5).apply(lambda x: np.std(x,ddof=1))
207
208
  elif 'sortino' in rar_name_lower:
208
209
  risk_type=ret_type_nopct+' LPSD'
209
210
  if pct_flag:
@@ -211,6 +212,7 @@ def get_expanding_sharpe_sortino(ticker,start,end,rar_name="sharpe", \
211
212
 
212
213
  #rardf2[risk_type]=rardf2[ret_type].expanding(min_periods=1).apply(lambda x: lpsd(x)*np.sqrt(len(x)-1))
213
214
  rardf2[risk_type]=rardf2[ret_type].expanding(min_periods=1).apply(lambda x: lpsd(x))
215
+ #rardf2[risk_type]=rardf2[ret_type].expanding(min_periods=5).apply(lambda x: lpsd(x))
214
216
 
215
217
 
216
218
  #计算RAR
@@ -399,18 +401,21 @@ def get_expanding_treynor_alpha(ticker,start,end,rar_name="alpha", \
399
401
  RF_daily=RF/365 * 100
400
402
  pretdf[lndretx]=pretdf[lagdretx].apply(lambda x: np.log(1+x/100))
401
403
  pretdf[lndrety]=pretdf[lagdrety].apply(lambda x: np.log(1+x/100))
402
-
403
404
  pretdf[varx]=pretdf[lndretx].expanding(min_periods=1).apply(lambda x: (np.exp(sum(x))-1)*100)
404
405
  pretdf[vary]=pretdf[lndrety].expanding(min_periods=1).apply(lambda x: (np.exp(sum(x))-1)*100)
405
-
406
+ """
407
+ pretdf[varx]=pretdf[lndretx].expanding(min_periods=5).apply(lambda x: (np.exp(sum(x))-1)*100)
408
+ pretdf[vary]=pretdf[lndrety].expanding(min_periods=5).apply(lambda x: (np.exp(sum(x))-1)*100)
409
+ """
406
410
  else:
407
411
  pretdf[lndretx]=pretdf[pretdfcols[0]].apply(lambda x: np.log(1+x))
408
412
  pretdf[lndrety]=pretdf[pretdfcols[1]].apply(lambda x: np.log(1+x))
409
-
410
413
  pretdf[varx]=pretdf[lndretx].expanding(min_periods=1).apply(lambda x: (np.exp(sum(x))-1))
411
414
  pretdf[vary]=pretdf[lndrety].expanding(min_periods=1).apply(lambda x: (np.exp(sum(x))-1))
412
-
413
-
415
+ """
416
+ pretdf[varx]=pretdf[lndretx].expanding(min_periods=5).apply(lambda x: (np.exp(sum(x))-1))
417
+ pretdf[vary]=pretdf[lndrety].expanding(min_periods=5).apply(lambda x: (np.exp(sum(x))-1))
418
+ """
414
419
  pretdf['Date']=pretdf.index
415
420
  pretdf['days']=pretdf['Date'].apply(lambda x: days_between_dates(date0,pd.to_datetime(x)))
416
421
 
siat/sector_china.py CHANGED
@@ -672,11 +672,13 @@ def industry_sw_list_all():
672
672
  import pandas as pd
673
673
  import akshare as ak
674
674
 
675
- symboltypes=["市场表征", "一级行业", "二级行业", "风格指数"]
676
- indextypecodes=['F','I','T','S']
675
+ symboltypes=["市场表征", "一级行业", "二级行业", "风格指数","大类风格指数","金创指数"]
676
+ indextypecodes=['F','1','2','S','B','C']
677
677
  industry=pd.DataFrame()
678
678
  for s in symboltypes:
679
- dft = ak.index_realtime_sw(symbol=s)
679
+ try:
680
+ dft = ak.index_realtime_sw(symbol=s)
681
+ except: continue
680
682
 
681
683
  pos=symboltypes.index(s)
682
684
  dft['指数类别代码']=indextypecodes[pos]
@@ -742,8 +744,8 @@ def display_industry_sw(sw_level='1',numberPerLine=4,colalign='left'):
742
744
  """
743
745
  按照类别打印申万行业列表,名称(代码),每行5个, 套壳函数
744
746
  """
745
- itype_list=['I','T','3','F','S']
746
- sw_level_list=['1','2','3','F','S']
747
+ itype_list=['1','2','3','F','S','B','C']
748
+ sw_level_list=['1','2','3','F','S','B','C']
747
749
  pos=sw_level_list.index(sw_level)
748
750
  itype=itype_list[pos]
749
751
 
@@ -769,15 +771,15 @@ def print_industry_sw(itype='I',numberPerLine=5,colalign='left'):
769
771
  df1=df[df['type']==itype]
770
772
  df1['name_code']=df1.apply(lambda x: x['name']+'('+x['code']+'.SW'+')',axis=1)
771
773
 
772
- symboltypes=["市场表征", "一级行业", "二级行业", "三级行业", "风格指数"]
773
- indextypecodes=['F','I','T','3','S']
774
+ symboltypes=["市场表征", "一级行业", "二级行业", "三级行业", "风格指数", "大类风格指数","金创指数"]
775
+ indextypecodes=['F','1','2','3','S','B','C']
774
776
  pos=indextypecodes.index(itype)
775
777
  iname=symboltypes[pos]
776
778
 
777
779
  ilist=list(df1['name_code'])
778
780
  print("\n*** 申万行业分类:"+iname+",共计"+str(len(ilist))+'个行业(板块)')
779
781
 
780
- if itype=='T': numberPerLine=4
782
+ if itype=='2': numberPerLine=4
781
783
  if itype=='3': numberPerLine=3
782
784
 
783
785
  printInLine_md(ilist,numberPerLine=numberPerLine,colalign=colalign)
@@ -952,7 +954,7 @@ def industry_ranking_sw(start,end,measure='Exp Ret%', \
952
954
  """
953
955
  完整版,全流程
954
956
  功能:模板,遍历某类申万指数,计算某项业绩指标,汇集排序
955
- itype: F表征指数,I行业指数,S风格指数
957
+ itype: F表征指数,n=1/2/3行业指数,S风格指数,B大类风格指数,C金创指数
956
958
  period="day"; choice of {"day", "week", "month"}
957
959
  绘图:柱状图,可选
958
960
  """
@@ -973,11 +975,17 @@ def industry_ranking_sw(start,end,measure='Exp Ret%', \
973
975
  df=pd.DataFrame(columns=['date','ticker','start','end','item','value'])
974
976
 
975
977
  print("\nSearching industry prices, it may take great time, please wait ...")
978
+
979
+ fail_list=[]
976
980
  for i in ilist:
977
981
 
978
982
  print(" Processing industry",i,"\b, please wait ...")
979
983
  #抓取指数价格,选取期间范围
980
- dft = ak.index_hist_sw(symbol=i,period="day")
984
+ try:
985
+ dft = ak.index_hist_sw(symbol=i,period="day")
986
+ except:
987
+ fail_list=fail_list+[i]
988
+ continue
981
989
 
982
990
  dft['ticker']=dft['代码']
983
991
  dft['date']=dft['日期'].apply(lambda x: pd.to_datetime(x))
@@ -1017,7 +1025,7 @@ def industry_ranking_sw(start,end,measure='Exp Ret%', \
1017
1025
  df['name']=df['ticker'].apply(lambda x: industry_sw_name(x))
1018
1026
  df.set_index('name',inplace=True)
1019
1027
  colname='value'
1020
- titletxt="行业板块分析:业绩排名"
1028
+ titletxt="行业分析:业绩排名"
1021
1029
  import datetime; today=datetime.date.today()
1022
1030
  footnote0=ectranslate(measure)+' ==>\n'
1023
1031
  footnote1='申万行业分类,观察期:'+start+'至'+iend+'\n'
@@ -1026,6 +1034,13 @@ def industry_ranking_sw(start,end,measure='Exp Ret%', \
1026
1034
 
1027
1035
  plot_barh(df,colname,titletxt,footnote,axisamp=axisamp)
1028
1036
  #plot_barh2(df,colname,titletxt,footnote)
1037
+
1038
+ if len(fail_list) > 0:
1039
+ print(" Unable to retrieve",len(fail_list),"industry(ies) as follows:")
1040
+ if len(fail_list) >= 10:
1041
+ printInLine_md(fail_list,numberPerLine=10,colalign='left',font_size='16px')
1042
+ else:
1043
+ printInLine_md(fail_list,numberPerLine=len(fail_list),colalign='left',font_size='16px')
1029
1044
 
1030
1045
  return df
1031
1046
 
@@ -1122,7 +1137,7 @@ def industry_ranking_sw2(industrylist,start,end,measure='Exp Ret%', \
1122
1137
  df.dropna(inplace=True)
1123
1138
 
1124
1139
  colname='value'
1125
- titletxt="行业板块分析:业绩排名"
1140
+ titletxt="行业分析:业绩排名"
1126
1141
  import datetime; today=datetime.date.today()
1127
1142
  footnote0=ectranslate(measure)+' ==>\n'
1128
1143
  footnote1='申万行业分类,观察期:'+start+'至'+iend+'\n'
@@ -1151,11 +1166,11 @@ def get_industry_sw(itype='I',period="day",industry_list='all'):
1151
1166
  """
1152
1167
 
1153
1168
  #检查itype的合理性
1154
- typelist=['F','I','T','3','S','A']
1169
+ typelist=['F','1','2','3','S','B','C','A']
1155
1170
  if not (itype in typelist):
1156
1171
  print(" #Error(get_industry_sw): unsupported industry category",itype)
1157
1172
  print(" Supported industry category",typelist)
1158
- print(" F: Featured, I-Industry, S-Styled, A-All (more time))")
1173
+ print(" F: Featured, n-Level n Industry, S-Styled, B- Big Styled, C- Financial Innovation, A-All (more time))")
1159
1174
  return None
1160
1175
 
1161
1176
  #获得指数代码
@@ -1176,7 +1191,7 @@ def get_industry_sw(itype='I',period="day",industry_list='all'):
1176
1191
  import datetime
1177
1192
  df=pd.DataFrame()
1178
1193
 
1179
- print(" *** Searching industry information, please wait ...")
1194
+ print(" Searching industry information, please wait ...")
1180
1195
  num=len(ilist)
1181
1196
  if num <= 10:
1182
1197
  steps=5
@@ -1184,6 +1199,7 @@ def get_industry_sw(itype='I',period="day",industry_list='all'):
1184
1199
  steps=10
1185
1200
 
1186
1201
  total=len(ilist)
1202
+ fail_list=[]
1187
1203
  for i in ilist:
1188
1204
 
1189
1205
  #print(" Retrieving information for industry",i)
@@ -1192,7 +1208,8 @@ def get_industry_sw(itype='I',period="day",industry_list='all'):
1192
1208
  try:
1193
1209
  dft = ak.index_hist_sw(symbol=i,period="day")
1194
1210
  except:
1195
- print(" #Warning(get_industry_sw): unsupported industry",i)
1211
+ #print(" #Warning(get_industry_sw): unsupported industry",i)
1212
+ fail_list=fail_list+[i]
1196
1213
  continue
1197
1214
 
1198
1215
  dft['ticker']=dft['代码']
@@ -1217,8 +1234,19 @@ def get_industry_sw(itype='I',period="day",industry_list='all'):
1217
1234
  print_progress_percent(current,total,steps=steps,leading_blanks=2)
1218
1235
 
1219
1236
  #num=list(set(list(df['ticker'])))
1220
- print(" Successfully retrieved",len(df),"records in",len(ilist),"industries")
1237
+ if len(fail_list)==0:
1238
+ print(" Successfully retrieved",len(df),"records in",len(ilist),"industries")
1239
+ else:
1240
+ print("\n Successfully retrieved",len(df),"records in",len(ilist),"industries")
1221
1241
  #print(" Successfully retrieved",len(df),"records in",num,"industries")
1242
+
1243
+ if len(fail_list) > 0:
1244
+ print(" Failed to retrieve",len(fail_list),"industry(ies) as follows:")
1245
+ if len(fail_list) >= 10:
1246
+ printInLine_md(fail_list,numberPerLine=10,colalign='left',font_size='16px')
1247
+ else:
1248
+ printInLine_md(fail_list,numberPerLine=len(fail_list),colalign='left',font_size='16px')
1249
+
1222
1250
  return df
1223
1251
 
1224
1252
 
@@ -1260,15 +1288,15 @@ def get_industry_sw2(industry_list,period="day"):
1260
1288
  steps=10
1261
1289
 
1262
1290
  total=len(ilist)
1291
+ fail_list=[]
1263
1292
  for i in ilist:
1264
-
1265
1293
  #print(" Retrieving information for industry",i)
1266
-
1267
1294
  #抓取指数价格
1268
1295
  try:
1269
1296
  dft = ak.index_hist_sw(symbol=i,period="day")
1270
1297
  except:
1271
- print(" #Warning(get_industry_sw): unsupported industry",i)
1298
+ #print(" #Warning(get_industry_sw): unsupported industry",i)
1299
+ fail_list=fail_list+[i]
1272
1300
  continue
1273
1301
 
1274
1302
  dft['ticker']=dft['代码']
@@ -1293,8 +1321,19 @@ def get_industry_sw2(industry_list,period="day"):
1293
1321
  print_progress_percent(current,total,steps=steps,leading_blanks=2)
1294
1322
 
1295
1323
  #num=list(set(list(df['ticker'])))
1296
- print(" Successfully retrieved",len(df),"records in",len(ilist),"industries")
1324
+ if len(fail_list) > 0:
1325
+ print("\n Successfully retrieved",len(df),"records in",len(ilist),"industries")
1326
+ else:
1327
+ print(" Successfully retrieved",len(df),"records in",len(ilist),"industries")
1297
1328
  #print(" Successfully retrieved",len(df),"records in",num,"industries")
1329
+ if len(fail_list) > 0:
1330
+ print(" Failed to retrieve",len(fail_list),"industry(ies) as follows:")
1331
+ if len(fail_list) >= 10:
1332
+ printInLine_md(fail_list,numberPerLine=10,colalign='left',font_size='16px')
1333
+ else:
1334
+ printInLine_md(fail_list,numberPerLine=len(fail_list),colalign='left',font_size='16px')
1335
+
1336
+
1298
1337
  return df
1299
1338
 
1300
1339
  #==============================================================================
@@ -1336,7 +1375,7 @@ def calc_industry_sw(df,start,end):
1336
1375
  idf=pd.DataFrame()
1337
1376
  idfall=pd.DataFrame()
1338
1377
 
1339
- print(" *** Calculating industry performance, please wait ...")
1378
+ print(" Calculating industry performance, please wait ...")
1340
1379
  num=len(ilist)
1341
1380
  if num <= 10:
1342
1381
  steps=5
@@ -1413,7 +1452,7 @@ if __name__=='__main__':
1413
1452
 
1414
1453
  def rank_industry_sw(idf,measure='Exp Ret%',industries=[], \
1415
1454
  graph=True,axisamp=0.8,px=False,maxitems=32, \
1416
- printout=False):
1455
+ printout=False,facecolor='papayawhip',font_size='16px'):
1417
1456
  """
1418
1457
  功能:遍历某类申万指数的某项业绩指标,汇集排序
1419
1458
  绘图:水平柱状图
@@ -1483,11 +1522,18 @@ def rank_industry_sw(idf,measure='Exp Ret%',industries=[], \
1483
1522
  gdf2.reset_index(inplace=True)
1484
1523
  gdf2.index=gdf2.index+1
1485
1524
  gdf2.columns=['行业名称','行业代码',ectranslate(measure),'开始日期','结束日期']
1486
-
1525
+ """
1487
1526
  print("***",titletxt,'\n')
1488
1527
  alignlist=['center']+['left']*(len(list(gdf2))-1)
1489
1528
  print(gdf2.to_markdown(index=True,tablefmt='plain',colalign=alignlist))
1529
+ """
1530
+ #确定表格字体大小
1531
+ titile_font_size=font_size
1532
+ heading_font_size=data_font_size=str(int(font_size.replace('px',''))-1)+'px'
1490
1533
 
1534
+ df_display_CSS(gdf2,titletxt=titletxt,footnote=footnote,facecolor=facecolor, \
1535
+ titile_font_size=titile_font_size,heading_font_size=heading_font_size, \
1536
+ data_font_size=data_font_size)
1491
1537
 
1492
1538
  if graph:
1493
1539
  if (len(gdf1) <= maxitems):
@@ -2094,12 +2140,12 @@ def get_industry_data_sw(start,end,sw_level='1'):
2094
2140
  功能:获得申万行业历史数据, 套壳函数
2095
2141
  start: 开始日期
2096
2142
  end: 结束日期
2097
- sw_level: '1', '2', '3', 'F', 'S'
2143
+ sw_level: '1', '2', '3', 'F', 'S', 'B', 'C'
2098
2144
 
2099
2145
  返回:idf, idfall,供进一步分析使用。
2100
2146
  """
2101
- itype_list=['I','T','3','F','S']
2102
- sw_level_list=['1','2','3','F','S']
2147
+ itype_list=['1','2','3','F','S', 'B', 'C']
2148
+ sw_level_list=['1','2','3','F','S', 'B', 'C']
2103
2149
  pos=sw_level_list.index(sw_level)
2104
2150
  itype=itype_list[pos]
2105
2151
 
@@ -2112,7 +2158,7 @@ if __name__ =="__main__":
2112
2158
 
2113
2159
  # 新冠疫情三年
2114
2160
  start='2023-1-1'; end='2023-4-10'
2115
- itype='T'
2161
+ itype='F'
2116
2162
 
2117
2163
  idf,idfall=get_industry_info_sw(start,end,itype='I')
2118
2164
 
@@ -2190,7 +2236,8 @@ if __name__ =="__main__":
2190
2236
 
2191
2237
  def rank_msecurity_performance(tickers,start,end, \
2192
2238
  measures=['Exp Ret%'], \
2193
- market_index='000001.SS',window=252,colalign='right'):
2239
+ market_index='000001.SS',window=252,colalign='right', \
2240
+ facecolor='papayawhip',font_size='16px'):
2194
2241
  """
2195
2242
  功能:列示多只股票多个指标的对比,从高到低
2196
2243
 
@@ -2264,6 +2311,7 @@ def rank_msecurity_performance(tickers,start,end, \
2264
2311
  df1.reset_index(inplace=True)
2265
2312
  df1.rename(columns={'index':'股票'},inplace=True)
2266
2313
 
2314
+ """
2267
2315
  alignlist=['left']+[colalign]*(len(allmeasures)-1)
2268
2316
 
2269
2317
  print("\n*** 股票多重指标比较:按夏普比率降序排列\n")
@@ -2273,6 +2321,22 @@ def rank_msecurity_performance(tickers,start,end, \
2273
2321
  print(" 表中的夏普比率/索替诺比率/阿尔法值均为TTM滚动值")
2274
2322
  import datetime; today=datetime.date.today()
2275
2323
  print(" 数据来源:新浪财经/东方财富,"+str(today)+'统计')
2324
+ """
2325
+ titletxt="股票多重指标分析:按夏普比率降序排列"
2326
+ footnote1="观察期:"+start+'至'+end+',表中数据为'+end+'快照'
2327
+ footnote2="表中的夏普比率/索替诺比率/阿尔法值均为TTM滚动值"
2328
+ import datetime; todaydt=datetime.date.today()
2329
+ footnote3="数据来源:新浪财经/东方财富,"+str(todaydt)+'统计'
2330
+ footnote=footnote1+'\n'+footnote2+'n'+footnote3
2331
+
2332
+ #确定表格字体大小
2333
+ titile_font_size=font_size
2334
+ heading_font_size=data_font_size=str(int(font_size.replace('px',''))-1)+'px'
2335
+
2336
+ df_display_CSS(fsdf6,titletxt=titletxt,footnote=footnote,facecolor=facecolor, \
2337
+ titile_font_size=titile_font_size,heading_font_size=heading_font_size, \
2338
+ data_font_size=data_font_size)
2339
+
2276
2340
 
2277
2341
  return df1
2278
2342
  #==============================================================================
@@ -2440,7 +2504,8 @@ if __name__=='__main__':
2440
2504
  top=10
2441
2505
  printout=True
2442
2506
 
2443
- def mixed_industry_stocks(industries=['煤炭','医药生物'],top=10,printout=True):
2507
+ def mixed_industry_stocks(industries=['煤炭','医药生物'],top=10,printout=True, \
2508
+ facecolor='papayawhip',font_size='16px'):
2444
2509
  """
2445
2510
  功能:将不同行业指数(industries)中的前top个(按指数内权重降序)成分股合成为字典,等权重
2446
2511
  """
@@ -2475,16 +2540,27 @@ def mixed_industry_stocks(industries=['煤炭','医药生物'],top=10,printout=T
2475
2540
  df_print=df[['序号','证券名称','证券代码','初始权重','行业名称','行业代码']]
2476
2541
 
2477
2542
  if printout:
2478
- alignlist=['center']+['center']*(len(list(df_print))-1)
2543
+ #alignlist=['center']+['center']*(len(list(df_print))-1)
2479
2544
 
2480
2545
  if len(industries) > 1:
2481
- print("\n*** 混合行业投资组合的成分股:初始等权重\n")
2546
+ #print("\n*** 混合行业投资组合的成分股:初始等权重\n")
2547
+ titletxt="多行业投资组合的成分股:初始等权重"
2482
2548
  else:
2483
- print("\n*** 单一行业投资组合的成分股:初始等权重\n")
2549
+ #print("\n*** 单一行业投资组合的成分股:初始等权重\n")
2550
+ titletxt="单一行业投资组合的成分股:初始等权重"
2484
2551
 
2485
- print(df_print.to_markdown(index=False,tablefmt='plain',colalign=alignlist))
2486
- import datetime; today=datetime.date.today()
2487
- print("\n*** 数据来源:申万宏源,统计日期:"+str(today))
2552
+ #print(df_print.to_markdown(index=False,tablefmt='plain',colalign=alignlist))
2553
+ import datetime; todaydt=datetime.date.today()
2554
+ #print("\n*** 数据来源:申万宏源,统计日期:"+str(today))
2555
+ footnote="数据来源:申万宏源,统计日期:"+str(todaydt)
2556
+
2557
+ #确定表格字体大小
2558
+ titile_font_size=font_size
2559
+ heading_font_size=data_font_size=str(int(font_size.replace('px',''))-1)+'px'
2560
+
2561
+ df_display_CSS(df_print,titletxt=titletxt,footnote=footnote,facecolor=facecolor, \
2562
+ titile_font_size=titile_font_size,heading_font_size=heading_font_size, \
2563
+ data_font_size=data_font_size)
2488
2564
 
2489
2565
  # 生成成分股字典
2490
2566
  stock_dict=df.set_index(['证券代码'])['初始权重'].to_dict()
@@ -2503,8 +2579,7 @@ def find_peers_china(industry='',top=20,rank=20,sw_level='2'):
2503
2579
  """
2504
2580
  功能:
2505
2581
  当industry = '',显示申万二级行业分类
2506
-
2507
- sw_level:申万一级行业'1',二级行业'2',三级行业'3',其他'F'和'S'
2582
+ sw_level:申万一级行业'1',二级行业'2',三级行业'3',其他'F'、'S'、'B'、'C'
2508
2583
  """
2509
2584
 
2510
2585
  # 避免混淆
@@ -2513,8 +2588,8 @@ def find_peers_china(industry='',top=20,rank=20,sw_level='2'):
2513
2588
 
2514
2589
  # 默认情形处理
2515
2590
  if industry == '':
2516
- itype_list=['I','T','3','F','S']
2517
- sw_level_list=['1','2','3','F','S']
2591
+ itype_list=['1','2','3','F','S','B','C']
2592
+ sw_level_list=['1','2','3','F','S','B','C']
2518
2593
  pos=sw_level_list.index(sw_level)
2519
2594
  itype=itype_list[pos]
2520
2595
 
@@ -2612,7 +2687,13 @@ def get_sw_index(ticker,start,end):
2612
2687
 
2613
2688
  import akshare as ak
2614
2689
  import pandas as pd
2615
- dft = ak.index_hist_sw(symbol=symbol,period="day")
2690
+
2691
+ try:
2692
+ dft = ak.index_hist_sw(symbol=symbol,period="day")
2693
+ except:
2694
+ print(" #Error(get_sw_index): failed to retrieve info for Shenwan industry index",symbol)
2695
+ return None
2696
+
2616
2697
  dft['ticker']=dft['代码'].apply(lambda x: x+'.SW')
2617
2698
 
2618
2699
  dft['name']=dft['代码'].apply(lambda x:industry_sw_name(x))
@@ -2636,6 +2717,7 @@ def get_sw_index(ticker,start,end):
2636
2717
  dft4.sort_index(inplace=True)
2637
2718
 
2638
2719
  df=dft4
2720
+
2639
2721
  return df
2640
2722
 
2641
2723
  #==============================================================================
@@ -2722,19 +2804,26 @@ if __name__ =="__main__":
2722
2804
  printout='smart'
2723
2805
 
2724
2806
 
2725
- def industry_scan_china(sw_level='F',indicator='Exp Ret%', \
2807
+ def industry_scan_china(sw_level='F', \
2726
2808
  start='MRY',end='default', \
2727
- printout='smart'):
2809
+ RF=0, \
2810
+ printout='smart', \
2811
+ facecolor='papayawhip',font_size='16px'):
2812
+ """
2813
+ 功能:扫描申万行业指数,按照投资收益率排名
2814
+ 申万行业分类sw_level:F--市场表征(默认),S--投资风格(策略),B--大类风格,C--金创,
2815
+ 1--一级行业,2--二级行业,3--三级行业
2816
+ 评估期间start与end:允许MRM/MRQ/MRY(默认)/YTD/L3Y(近三年)/L5Y(近五年)
2817
+ RF:年化无风险收益率,默认0,可参照一年期国债收益率
2818
+ 筛选方式printout:smart--收益前10名与后10名(默认),winner--仅限收益为正的行业,
2819
+ loser--仅限收益为负的行业,50--收益前50名,-10--收益后10名,all--所有行业
2728
2820
  """
2729
- 功能:扫描申万行业指数,默认一级行业分类
2730
- 时间区间:start与end,允许MRM/MRQ/MRY/YTD/LTY(近三年)/LFY(近五年)
2731
- 显示:smart--指数增长前五名与后五名,'winner'--仅限增长的行业,'loser'--仅限下降的行业,
2732
- '50'--前50名,'-10'--后10名,'all'--所有行业
2733
- """
2734
- print(" Scanning Shenwan industry performance, which may take up to several hours ...")
2821
+ indicator='Exp Ret%'
2822
+
2823
+ print(" Collecting industry info, it may take very long time ... ...")
2735
2824
 
2736
2825
  # 检查申万行业
2737
- sw_level_list=['1','2','3','F','S']
2826
+ sw_level_list=['1','2','3','F','S','B','C']
2738
2827
  if sw_level not in sw_level_list:
2739
2828
  print(" #Warning(industry_scan_china): invalid Shenwan industry types for",sw_level)
2740
2829
  print(" Valid Shenwan industry types:",end='')
@@ -2751,7 +2840,11 @@ def industry_scan_china(sw_level='F',indicator='Exp Ret%', \
2751
2840
  return None
2752
2841
 
2753
2842
 
2754
- # 检查日期:截至日期
2843
+ # 检查日期:
2844
+ fromdate,todate=start_end_preprocess(start,end)
2845
+ import datetime as dt; todaydt=dt.date.today().strftime('%Y-%m-%d')
2846
+ """
2847
+ #截至日期
2755
2848
  import datetime as dt; todaydt=dt.date.today().strftime('%Y-%m-%d')
2756
2849
  end=end.lower()
2757
2850
  if end in ['default','today']:
@@ -2761,7 +2854,6 @@ def industry_scan_china(sw_level='F',indicator='Exp Ret%', \
2761
2854
  if not validdate:
2762
2855
  print(" #Warning(industry_scan_china): invalid date for",end)
2763
2856
  todate=todaydt
2764
-
2765
2857
  # 检查日期:开始日期
2766
2858
  start=start.lower()
2767
2859
  if start in ['default','mrm']: # 默认近一个月
@@ -2781,9 +2873,9 @@ def industry_scan_china(sw_level='F',indicator='Exp Ret%', \
2781
2873
  if not validdate:
2782
2874
  print(" #Warning(industry_scan_china): invalid date for",start,"/b, set to MRM")
2783
2875
  fromdate=date_adjust(todate,adjust=-31)
2784
-
2876
+ """
2785
2877
  # 获取申万行业类别内部标识
2786
- itype_list=['I','T','3','F','S']
2878
+ itype_list=['1','2','3','F','S','B','C']
2787
2879
  pos=sw_level_list.index(sw_level)
2788
2880
  itype=itype_list[pos]
2789
2881
 
@@ -2802,8 +2894,12 @@ def industry_scan_china(sw_level='F',indicator='Exp Ret%', \
2802
2894
  # 计算基础数据,本步骤所需时间较长
2803
2895
  idf,idfall=calc_industry_sw(df,fromdate,todate)
2804
2896
 
2805
- idf['sharpe']=idf['Exp Ret%'] / idf['Exp Ret Volatility%']
2806
- idf['sortino']=idf['Exp Ret%'] / idf['Exp Ret LPSD%']
2897
+ RF_daily=RF/365*100 #百分数
2898
+ #RF_daily=RF/365
2899
+ RF_days=RF_daily * calculate_days(fromdate, todate)
2900
+
2901
+ idf['sharpe']=(idf['Exp Ret%']-RF_days) / idf['Exp Ret Volatility%']
2902
+ idf['sortino']=(idf['Exp Ret%']-RF_days) / idf['Exp Ret LPSD%']
2807
2903
 
2808
2904
  # 排序
2809
2905
  idf.sort_values(indicator,ascending=False,inplace=True)
@@ -2858,12 +2954,67 @@ def industry_scan_china(sw_level='F',indicator='Exp Ret%', \
2858
2954
  inplace=True)
2859
2955
 
2860
2956
  # 显示
2861
- titletxt="申万行业业绩指标排行榜:"+"层级/类别"+sw_level+',共'+str(len(idf))+"个指数"
2862
- print("\n***",titletxt,'\n')
2957
+ if sw_level=='F':
2958
+ sw_level_txt='申万市场表征指数'
2959
+ elif sw_level=='S':
2960
+ sw_level_txt='申万投资风格指数'
2961
+ elif sw_level=='B':
2962
+ sw_level_txt='申万大类风格指数'
2963
+ elif sw_level=='C':
2964
+ sw_level_txt='申万金创指数'
2965
+ elif sw_level=='1':
2966
+ sw_level_txt='申万一级行业'
2967
+ elif sw_level=='2':
2968
+ sw_level_txt='申万二级行业'
2969
+ elif sw_level=='3':
2970
+ sw_level_txt='申万三级行业'
2971
+ else:
2972
+ sw_level_txt='未知类别'
2973
+
2974
+ if printout=='all':
2975
+ printout_txt='所有指数'
2976
+ elif printout=='smart':
2977
+ printout_txt='收益最高最低者各十名'
2978
+ if len(df2) <=20:
2979
+ printout_txt='所有指数'
2980
+ elif printout=='winner':
2981
+ printout_txt='收益为正者'
2982
+ elif printout=='loser':
2983
+ printout_txt='收益为负者'
2984
+ else:
2985
+ try:
2986
+ num=int(printout)
2987
+ if len(df2) > abs(num):
2988
+ if num > 0:
2989
+ printout_txt='收益前'+printout+"名"
2990
+ else:
2991
+ printout_txt='收益后'+str(abs(num))+"名"
2992
+ else:
2993
+ printout_txt='所有指数'
2994
+ except:
2995
+ printout_txt='未知筛选方式'
2996
+
2997
+ #titletxt="申万行业业绩排行榜:"+sw_level_txt+',共'+str(len(df_prt))+"个指数符合条件"
2998
+ titletxt="行业业绩排行榜:"+sw_level_txt+',筛选方式:'+printout_txt
2999
+ #print("\n***",titletxt,'\n')
3000
+ """
2863
3001
  alignlist=['center']+['left']*(len(list(df_prt))-1)
2864
3002
  print(df_prt.to_markdown(index=True,tablefmt='plain',colalign=alignlist))
3003
+ """
3004
+ #print("\n *** 数据来源:综合申万宏源/东方财富/新浪财经,",todaydt,"\b;分析期间:",fromdate+'至'+todate)
3005
+ #footnote1="筛选方式:all-所有,smart-收益最高最低各10个,winner-收益为正,loser-收益为负"
3006
+ footnote2="数据来源:综合申万宏源/东方财富/新浪财经,"+str(fromdate)+'至'+str(todate)+"。"+str(todaydt)+"统计"
3007
+ #footnote=footnote1+'\n'+footnote2
3008
+ footnote=footnote2
3009
+
3010
+ #确定表格字体大小
3011
+ titile_font_size=font_size
3012
+ heading_font_size=data_font_size=str(int(font_size.replace('px',''))-1)+'px'
2865
3013
 
2866
- print("\n *** 数据来源:综合申万宏源/东方财富/新浪财经,",todaydt,"\b;分析期间:",fromdate+'至'+todate)
3014
+ df_display_CSS(df_prt,titletxt=titletxt,footnote=footnote,facecolor=facecolor, \
3015
+ first_col_align='left',second_col_align='left', \
3016
+ titile_font_size=titile_font_size,heading_font_size=heading_font_size, \
3017
+ data_font_size=data_font_size)
2867
3018
 
2868
3019
  return df2
2869
3020
 
siat/security_prices.py CHANGED
@@ -1786,6 +1786,7 @@ def calc_expanding_return(drdf0,basedate):
1786
1786
  retname2="Exp Ret%"
1787
1787
  import numpy as np
1788
1788
  #drdf[retname1]=np.exp(drdf["log(Daily Ret)"].expanding(min_periods=1).sum())-1.0
1789
+ #drdf[retname1]=np.exp(drdf["log(Daily Ret)"].expanding(min_periods=5).sum())-1.0
1789
1790
  first_close=drdf.head(1)['Close'].values[0]
1790
1791
  drdf[retname1]=drdf['Close']/first_close-1
1791
1792
  drdf[retname2]=drdf[retname1]*100.0
@@ -1794,6 +1795,7 @@ def calc_expanding_return(drdf0,basedate):
1794
1795
  retname3="Exp Adj Ret"
1795
1796
  retname4="Exp Adj Ret%"
1796
1797
  #drdf[retname3]=np.exp(drdf["log(Daily Adj Ret)"].expanding(min_periods=1).sum())-1.0
1798
+ #drdf[retname3]=np.exp(drdf["log(Daily Adj Ret)"].expanding(min_periods=5).sum())-1.0
1797
1799
  first_aclose=drdf.head(1)['Adj Close'].values[0]
1798
1800
  drdf[retname3]=drdf['Adj Close']/first_aclose-1
1799
1801
  drdf[retname4]=drdf[retname3]*100.0
@@ -1859,11 +1861,13 @@ def expanding_price_volatility(df0,basedate):
1859
1861
  import numpy as np
1860
1862
  #df[retname1]=df["Close"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1)/np.mean(x)*np.sqrt(len(x)))
1861
1863
  df[retname1]=df["Close"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1)/np.mean(x))
1864
+ #df[retname1]=df["Close"].expanding(min_periods=5).apply(lambda x: np.std(x,ddof=1)/np.mean(x))
1862
1865
 
1863
1866
  #计算扩展窗口调整价格风险:基于调整收盘价
1864
1867
  retname3="Exp Adj Price Volatility"
1865
1868
  #df[retname3]=df["Adj Close"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1)/np.mean(x)*np.sqrt(len(x)))
1866
1869
  df[retname3]=df["Adj Close"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1)/np.mean(x))
1870
+ #df[retname3]=df["Adj Close"].expanding(min_periods=5).apply(lambda x: np.std(x,ddof=1)/np.mean(x))
1867
1871
 
1868
1872
  return df
1869
1873
 
@@ -1936,6 +1940,7 @@ def expanding_ret_volatility(df0,basedate):
1936
1940
 
1937
1941
  #df[retname1]=df["Daily Ret"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1)*np.sqrt(len(x)))
1938
1942
  df[retname1]=df["Daily Ret"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1))
1943
+ #df[retname1]=df["Daily Ret"].expanding(min_periods=5).apply(lambda x: np.std(x,ddof=1))
1939
1944
  df[retname2]=df[retname1]*100.0
1940
1945
 
1941
1946
  #计算扩展窗口调整收益率风险:基于调整收益率
@@ -1943,6 +1948,7 @@ def expanding_ret_volatility(df0,basedate):
1943
1948
  retname4="Exp Adj Ret Volatility%"
1944
1949
  #df[retname3]=df["Daily Adj Ret"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1)*np.sqrt(len(x)))
1945
1950
  df[retname3]=df["Daily Adj Ret"].expanding(min_periods=1).apply(lambda x: np.std(x,ddof=1))
1951
+ #df[retname3]=df["Daily Adj Ret"].expanding(min_periods=5).apply(lambda x: np.std(x,ddof=1))
1946
1952
  df[retname4]=df[retname3]*100.0
1947
1953
 
1948
1954
  return df
@@ -2051,6 +2057,7 @@ def expanding_ret_lpsd(df0,basedate):
2051
2057
  import numpy as np
2052
2058
  #df[retname1]=df["Daily Ret"].expanding(min_periods=1).apply(lambda x: lpsd(x)*np.sqrt(len(x)))
2053
2059
  df[retname1]=df["Daily Ret"].expanding(min_periods=1).apply(lambda x: lpsd(x))
2060
+ #df[retname1]=df["Daily Ret"].expanding(min_periods=5).apply(lambda x: lpsd(x))
2054
2061
  df[retname2]=df[retname1]*100.0
2055
2062
 
2056
2063
  #计算扩展窗口调整下偏标准差:基于调整收益率
@@ -2058,6 +2065,7 @@ def expanding_ret_lpsd(df0,basedate):
2058
2065
  retname4=retname3+'%'
2059
2066
  #df[retname3]=df["Daily Adj Ret"].expanding(min_periods=1).apply(lambda x: lpsd(x)*np.sqrt(len(x)))
2060
2067
  df[retname3]=df["Daily Adj Ret"].expanding(min_periods=1).apply(lambda x: lpsd(x))
2068
+ #df[retname3]=df["Daily Adj Ret"].expanding(min_periods=5).apply(lambda x: lpsd(x))
2061
2069
  df[retname4]=df[retname3]*100.0
2062
2070
 
2063
2071
  return df