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.
- siat/common.py +36 -16
- siat/financials2.py +29 -17
- siat/financials_china.py +33 -9
- siat/financials_china2.py +184 -75
- siat/risk_adjusted_return2.py +11 -6
- siat/sector_china.py +210 -59
- siat/security_prices.py +8 -0
- siat/stock_china.py +35 -13
- siat/translate.py +492 -149
- {siat-3.0.35.dist-info → siat-3.0.40.dist-info}/METADATA +1 -1
- {siat-3.0.35.dist-info → siat-3.0.40.dist-info}/RECORD +13 -13
- {siat-3.0.35.dist-info → siat-3.0.40.dist-info}/WHEEL +0 -0
- {siat-3.0.35.dist-info → siat-3.0.40.dist-info}/top_level.txt +0 -0
siat/risk_adjusted_return2.py
CHANGED
@@ -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'
|
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','
|
675
|
+
symboltypes=["市场表征", "一级行业", "二级行业", "风格指数","大类风格指数","金创指数"]
|
676
|
+
indextypecodes=['F','1','2','S','B','C']
|
677
677
|
industry=pd.DataFrame()
|
678
678
|
for s in symboltypes:
|
679
|
-
|
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=['
|
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','
|
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=='
|
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表征指数,
|
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
|
-
|
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','
|
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,
|
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("
|
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
|
-
|
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
|
-
|
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("
|
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
|
-
|
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=['
|
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='
|
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;
|
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=['
|
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
|
-
|
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',
|
2807
|
+
def industry_scan_china(sw_level='F', \
|
2726
2808
|
start='MRY',end='default', \
|
2727
|
-
|
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
|
-
|
2731
|
-
|
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=['
|
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
|
-
|
2806
|
-
|
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
|
-
|
2862
|
-
|
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
|
-
|
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
|