siat 2.10.13__py3-none-any.whl → 2.11.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/__init__.py +0 -0
- siat/allin.py +0 -0
- siat/common.py +1 -0
- siat/sector_china.py +12 -2
- siat/security_trend.py +378 -281
- siat/transaction.py +198 -321
- siat/translate.py +4 -2
- siat/valuation.py +948 -0
- {siat-2.10.13.dist-info → siat-2.11.2.dist-info}/METADATA +1 -1
- {siat-2.10.13.dist-info → siat-2.11.2.dist-info}/RECORD +12 -11
- {siat-2.10.13.dist-info → siat-2.11.2.dist-info}/WHEEL +0 -0
- {siat-2.10.13.dist-info → siat-2.11.2.dist-info}/top_level.txt +0 -0
siat/valuation.py
ADDED
@@ -0,0 +1,948 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
本模块功能:投资组合的风险调整收益率教学插件
|
4
|
+
所属工具包:证券投资分析工具SIAT
|
5
|
+
SIAT:Security Investment Analysis Tool
|
6
|
+
创建日期:2023年11月30日
|
7
|
+
最新修订日期:2023年11月30日
|
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.stock import *
|
23
|
+
from siat.security_prices import *
|
24
|
+
from siat.sector_china import *
|
25
|
+
from siat.grafix import *
|
26
|
+
|
27
|
+
import pandas as pd
|
28
|
+
import akshare as ak
|
29
|
+
|
30
|
+
import datetime as dt; todaydt=str(dt.date.today())
|
31
|
+
#==============================================================================
|
32
|
+
#==============================================================================
|
33
|
+
if __name__=='__main__':
|
34
|
+
ticker='PZU.PL'
|
35
|
+
ticker='PZU.WA'
|
36
|
+
|
37
|
+
indicators='pe'
|
38
|
+
indicators=['pe','pb']
|
39
|
+
indicators=['pe','pb','mv']
|
40
|
+
|
41
|
+
start='2023-1-1'; end='2023-11-30'
|
42
|
+
|
43
|
+
df_pl=get_stock_valuation_pl(ticker,indicators,start,end)
|
44
|
+
|
45
|
+
def get_stock_valuation_pl(ticker,indicators,start,end):
|
46
|
+
"""
|
47
|
+
功能:抓取一只波兰股票估值信息pe/pb/mv
|
48
|
+
"""
|
49
|
+
currency='PLN'
|
50
|
+
million=1000000
|
51
|
+
kilo=1000
|
52
|
+
# 判断是否波兰股票
|
53
|
+
ticker1=ticker.upper()
|
54
|
+
result,prefix,suffix=split_prefix_suffix(ticker1)
|
55
|
+
if (not result) or (suffix not in ['PL','WA']):
|
56
|
+
return None
|
57
|
+
iname=codetranslate(ticker)
|
58
|
+
|
59
|
+
if isinstance(indicators,str):
|
60
|
+
indicators=[indicators]
|
61
|
+
|
62
|
+
indicators1=[]
|
63
|
+
for i in indicators:
|
64
|
+
indicators1=indicators1+[i.upper()]
|
65
|
+
|
66
|
+
#屏蔽函数内print信息输出的类
|
67
|
+
import os, sys
|
68
|
+
class HiddenPrints:
|
69
|
+
def __enter__(self):
|
70
|
+
self._original_stdout = sys.stdout
|
71
|
+
sys.stdout = open(os.devnull, 'w')
|
72
|
+
|
73
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
74
|
+
sys.stdout.close()
|
75
|
+
sys.stdout = self._original_stdout
|
76
|
+
|
77
|
+
df=None
|
78
|
+
for i in indicators1:
|
79
|
+
t=prefix+'_'+i+'.PL'
|
80
|
+
with HiddenPrints():
|
81
|
+
dft=get_price(t,start,end)
|
82
|
+
if dft is None:
|
83
|
+
print(" #Warning(get_stock_valuation_pl): failed to retrieve",t)
|
84
|
+
continue
|
85
|
+
|
86
|
+
dft['ticker']=ticker1
|
87
|
+
dft['name']=iname
|
88
|
+
dft['currency']='CNY'
|
89
|
+
|
90
|
+
if i=='MV':
|
91
|
+
#dft[i]=dft['Close'] * million
|
92
|
+
dft[i]=dft['Close'] / kilo
|
93
|
+
else:
|
94
|
+
dft[i]=dft['Close']
|
95
|
+
|
96
|
+
dft1=dft[[i,'name','currency']]
|
97
|
+
|
98
|
+
if df is None:
|
99
|
+
df=dft1
|
100
|
+
else:
|
101
|
+
#df=pd.merge(df,dft1,how='inner',left_index=True,right_index=True)
|
102
|
+
df=pd.merge(df,dft1,how='outer',left_index=True,right_index=True)
|
103
|
+
|
104
|
+
# 去掉多余的name/currency重复列
|
105
|
+
if df is None: return None
|
106
|
+
collist=list(df)
|
107
|
+
if not (('name' in collist) and ('currency' in collist)):
|
108
|
+
df.rename(columns={'name_x':'name','currency_x':'currency'},inplace=True)
|
109
|
+
|
110
|
+
collist=list(df)
|
111
|
+
collist1=[]
|
112
|
+
for c in collist:
|
113
|
+
if "_" not in c:
|
114
|
+
collist1=collist1+[c]
|
115
|
+
|
116
|
+
df1=df[collist1]
|
117
|
+
|
118
|
+
return df1
|
119
|
+
|
120
|
+
|
121
|
+
#==============================================================================
|
122
|
+
if __name__=='__main__':
|
123
|
+
ticker='JD'
|
124
|
+
|
125
|
+
indicators='pe'
|
126
|
+
indicators=['pe','pb']
|
127
|
+
indicators=['pe','pb','mv']
|
128
|
+
|
129
|
+
start='2023-1-1'; end='2023-11-30'
|
130
|
+
|
131
|
+
df_us=get_stock_valuation_us(ticker,indicators,start,end)
|
132
|
+
|
133
|
+
def get_stock_valuation_us(ticker,indicators,start,end):
|
134
|
+
"""
|
135
|
+
功能:抓取一只美股股票估值信息pe/pb/mv
|
136
|
+
"""
|
137
|
+
currency='USD'
|
138
|
+
million=1000000
|
139
|
+
kilo=1000
|
140
|
+
|
141
|
+
# 判断是否美股股票
|
142
|
+
ticker1=ticker.upper()
|
143
|
+
result,prefix,suffix=split_prefix_suffix(ticker1)
|
144
|
+
if result or suffix != '': # 非美股
|
145
|
+
return None
|
146
|
+
iname=codetranslate(ticker)
|
147
|
+
|
148
|
+
if isinstance(indicators,str):
|
149
|
+
indicators=[indicators]
|
150
|
+
|
151
|
+
indicators1=[]
|
152
|
+
for i in indicators:
|
153
|
+
indicators1=indicators1+[i.upper()]
|
154
|
+
|
155
|
+
#屏蔽函数内print信息输出的类
|
156
|
+
import os, sys
|
157
|
+
class HiddenPrints:
|
158
|
+
def __enter__(self):
|
159
|
+
self._original_stdout = sys.stdout
|
160
|
+
sys.stdout = open(os.devnull, 'w')
|
161
|
+
|
162
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
163
|
+
sys.stdout.close()
|
164
|
+
sys.stdout = self._original_stdout
|
165
|
+
|
166
|
+
df=None
|
167
|
+
for i in indicators1:
|
168
|
+
t=prefix+'_'+i+'.US'
|
169
|
+
with HiddenPrints():
|
170
|
+
dft=get_price(t,start,end)
|
171
|
+
if dft is None:
|
172
|
+
print(" #Warning(get_stock_valuation_us): failed to retrieve",t)
|
173
|
+
continue
|
174
|
+
|
175
|
+
dft['ticker']=ticker1
|
176
|
+
dft['name']=iname
|
177
|
+
dft['currency']='CNY'
|
178
|
+
|
179
|
+
if i=='MV':
|
180
|
+
#dft[i]=dft['Close'] * million
|
181
|
+
dft[i]=dft['Close'] / kilo
|
182
|
+
else:
|
183
|
+
dft[i]=dft['Close']
|
184
|
+
dft1=dft[[i,'name','currency']]
|
185
|
+
|
186
|
+
if df is None:
|
187
|
+
df=dft1
|
188
|
+
else:
|
189
|
+
df=pd.merge(df,dft1,how='outer',left_index=True,right_index=True)
|
190
|
+
|
191
|
+
# 去掉多余的name/currency重复列
|
192
|
+
if df is None: return None
|
193
|
+
collist=list(df)
|
194
|
+
if not (('name' in collist) and ('currency' in collist)):
|
195
|
+
df.rename(columns={'name_x':'name','currency_x':'currency'},inplace=True)
|
196
|
+
|
197
|
+
collist=list(df)
|
198
|
+
collist1=[]
|
199
|
+
for c in collist:
|
200
|
+
if "_" not in c:
|
201
|
+
collist1=collist1+[c]
|
202
|
+
|
203
|
+
df1=df[collist1]
|
204
|
+
|
205
|
+
return df1
|
206
|
+
|
207
|
+
#==============================================================================
|
208
|
+
if __name__=='__main__':
|
209
|
+
ticker='600519.SS'
|
210
|
+
ticker='002504.SZ'
|
211
|
+
ticker='835579.BJ'
|
212
|
+
ticker='00700.HK'
|
213
|
+
|
214
|
+
indicators='pe'
|
215
|
+
indicators=['pe','pb']
|
216
|
+
indicators=['pe','pb','mv']
|
217
|
+
|
218
|
+
start='2023-1-1'; end='2023-11-30'
|
219
|
+
|
220
|
+
df_cnhk=get_stock_valuation_cn_hk(ticker,indicators,start,end)
|
221
|
+
|
222
|
+
def get_stock_valuation_cn_hk(ticker,indicators,start,end):
|
223
|
+
"""
|
224
|
+
功能:抓取一只A股或港股股票估值信息pe/pb/mv
|
225
|
+
"""
|
226
|
+
result,startdt,enddt=check_period(start,end)
|
227
|
+
|
228
|
+
yi=100000000
|
229
|
+
ten=10
|
230
|
+
# 判断是否A股或港股股票
|
231
|
+
ticker1=ticker.upper()
|
232
|
+
result,prefix,suffix=split_prefix_suffix(ticker1)
|
233
|
+
if (not result) or (suffix not in ['SS','SZ','BJ','HK']):
|
234
|
+
return None
|
235
|
+
iname=codetranslate(ticker)
|
236
|
+
|
237
|
+
if suffix in ['SS','SZ','BJ']: currency='CNY'
|
238
|
+
else: currency='HKD'
|
239
|
+
|
240
|
+
if isinstance(indicators,str):
|
241
|
+
indicators=[indicators]
|
242
|
+
|
243
|
+
indicators1=[]
|
244
|
+
for i in indicators:
|
245
|
+
indicators1=indicators1+[i.upper()]
|
246
|
+
|
247
|
+
#屏蔽函数内print信息输出的类
|
248
|
+
import os, sys
|
249
|
+
class HiddenPrints:
|
250
|
+
def __enter__(self):
|
251
|
+
self._original_stdout = sys.stdout
|
252
|
+
sys.stdout = open(os.devnull, 'w')
|
253
|
+
|
254
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
255
|
+
sys.stdout.close()
|
256
|
+
sys.stdout = self._original_stdout
|
257
|
+
|
258
|
+
# 评估时间间隔: 取样日期间隔长短不同,必须做
|
259
|
+
delta=date_delta(start,end)
|
260
|
+
if delta <= 365:
|
261
|
+
period="近一年"
|
262
|
+
elif delta <= 365*3:
|
263
|
+
period="近三年"
|
264
|
+
elif delta <= 365*5:
|
265
|
+
period="近五年"
|
266
|
+
elif delta <= 365*10:
|
267
|
+
period="近十年"
|
268
|
+
else:
|
269
|
+
period="全部"
|
270
|
+
|
271
|
+
indicator_list_en=['PE','PB','MV','PCF']
|
272
|
+
indicator_list_cn=['市盈率(TTM)','市净率','总市值','市现率']
|
273
|
+
# 市现率PCF=股价 / 每股现金流
|
274
|
+
# 市销率PS或PSR=股价 / 每股销售额
|
275
|
+
df=None
|
276
|
+
for i in indicators1:
|
277
|
+
pos=indicator_list_en.index(i)
|
278
|
+
t=indicator_list_cn[pos]
|
279
|
+
"""
|
280
|
+
with HiddenPrints():
|
281
|
+
if suffix in ['SS','SZ','BJ']:
|
282
|
+
dft=ak.stock_zh_valuation_baidu(symbol=prefix,indicator=t,period=period)
|
283
|
+
elif suffix in ['HK']:
|
284
|
+
dft=ak.stock_hk_valuation_baidu(symbol=prefix,indicator=t,period=period)
|
285
|
+
"""
|
286
|
+
try:
|
287
|
+
if suffix in ['SS','SZ','BJ']:
|
288
|
+
dft=ak.stock_zh_valuation_baidu(symbol=prefix,indicator=t,period=period)
|
289
|
+
elif suffix in ['HK']:
|
290
|
+
dft=ak.stock_hk_valuation_baidu(symbol=prefix,indicator=t,period=period)
|
291
|
+
except:
|
292
|
+
print(" #Warning(get_stock_valuation_cn_hk): failed to retrieve",i,"for",prefix)
|
293
|
+
continue
|
294
|
+
|
295
|
+
dft['Date']=dft['date'].apply(lambda x: pd.to_datetime(x))
|
296
|
+
dft.set_index('Date',inplace=True)
|
297
|
+
dft['ticker']=ticker1
|
298
|
+
dft['name']=iname
|
299
|
+
dft['currency']='CNY'
|
300
|
+
if i=='MV':
|
301
|
+
#dft[i]=dft['value'] * yi
|
302
|
+
dft[i]=dft['value'] / ten
|
303
|
+
else:
|
304
|
+
dft[i]=dft['value']
|
305
|
+
dftp=dft[(dft.index >= startdt) & (dft.index <= enddt)]
|
306
|
+
dft1=dftp[[i,'name','currency']]
|
307
|
+
|
308
|
+
if df is None:
|
309
|
+
df=dft1
|
310
|
+
else:
|
311
|
+
df=pd.merge(df,dft1,how='outer',left_index=True,right_index=True)
|
312
|
+
|
313
|
+
# 去掉多余的name/currency重复列
|
314
|
+
if df is None: return None
|
315
|
+
collist=list(df)
|
316
|
+
if not (('name' in collist) and ('currency' in collist)):
|
317
|
+
df.rename(columns={'name_x':'name','currency_x':'currency'},inplace=True)
|
318
|
+
|
319
|
+
collist=list(df)
|
320
|
+
collist1=[]
|
321
|
+
for c in collist:
|
322
|
+
if "_" not in c:
|
323
|
+
collist1=collist1+[c]
|
324
|
+
|
325
|
+
df1=df[collist1]
|
326
|
+
|
327
|
+
return df1
|
328
|
+
|
329
|
+
#==============================================================================
|
330
|
+
if __name__=='__main__':
|
331
|
+
ticker='光伏设备(申万)'
|
332
|
+
ticker='中证500'
|
333
|
+
ticker='801735.SW'
|
334
|
+
|
335
|
+
indicators='pe'
|
336
|
+
indicators=['pe','pb','div yield']
|
337
|
+
indicators=['pe','pb']
|
338
|
+
|
339
|
+
start='2023-1-1'; end='2023-11-30'
|
340
|
+
|
341
|
+
df_index=get_index_valuation_funddb(ticker,indicators,start,end)
|
342
|
+
|
343
|
+
def get_index_valuation_funddb(ticker,indicators,start,end):
|
344
|
+
"""
|
345
|
+
功能:抓取一个申万或中证行业估值信息pe/pb/dividend(股息率)
|
346
|
+
"""
|
347
|
+
result,startdt,enddt=check_period(start,end)
|
348
|
+
|
349
|
+
# 判断是否申万或中证股票
|
350
|
+
ticker1=ticker.upper()
|
351
|
+
result,prefix,suffix=split_prefix_suffix(ticker1)
|
352
|
+
if result and suffix in ['SW']:
|
353
|
+
iname=industry_sw_name(prefix)+"(申万)"
|
354
|
+
else:
|
355
|
+
iname=ticker1
|
356
|
+
|
357
|
+
if isinstance(indicators,str):
|
358
|
+
indicators=[indicators]
|
359
|
+
|
360
|
+
indicators1=[]
|
361
|
+
for i in indicators:
|
362
|
+
indicators1=indicators1+[i.upper()]
|
363
|
+
|
364
|
+
indicator_list_en=['PE','PB','DIV YIELD']
|
365
|
+
indicator_list_cn=['市盈率','市净率','股息率']
|
366
|
+
indicators2=[]
|
367
|
+
for i in indicators1:
|
368
|
+
if i in indicator_list_en:
|
369
|
+
indicators2=indicators2+[i]
|
370
|
+
|
371
|
+
#屏蔽函数内print信息输出的类
|
372
|
+
import os, sys
|
373
|
+
class HiddenPrints:
|
374
|
+
def __enter__(self):
|
375
|
+
self._original_stdout = sys.stdout
|
376
|
+
sys.stdout = open(os.devnull, 'w')
|
377
|
+
|
378
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
379
|
+
sys.stdout.close()
|
380
|
+
sys.stdout = self._original_stdout
|
381
|
+
|
382
|
+
# 股息率=每股股利 / 股价
|
383
|
+
df=None
|
384
|
+
for i in indicators2:
|
385
|
+
pos=indicator_list_en.index(i)
|
386
|
+
t=indicator_list_cn[pos]
|
387
|
+
try:
|
388
|
+
with HiddenPrints():
|
389
|
+
dft=ak.index_value_hist_funddb(symbol=iname,indicator=t)
|
390
|
+
except:
|
391
|
+
print(" #Error(get_industry_valuation_sw_zz): failed to retrieve info for industry",ticker)
|
392
|
+
industry_list=list(ak.index_value_name_funddb()['指数名称'])
|
393
|
+
industry_sw=[]
|
394
|
+
industry_zz=[]
|
395
|
+
industry_gz=[]
|
396
|
+
industry_others=[]
|
397
|
+
for i in industry_list:
|
398
|
+
if '(申万)' in i:
|
399
|
+
industry_sw=industry_sw+[i]
|
400
|
+
elif '中证' in i:
|
401
|
+
industry_zz=industry_zz+[i]
|
402
|
+
elif '国证' in i:
|
403
|
+
industry_gz=industry_gz+[i]
|
404
|
+
else:
|
405
|
+
industry_others=industry_others+[i]
|
406
|
+
print(" Supported industry indexes:")
|
407
|
+
printlist(industry_sw,numperline=5,beforehand=' ',separator=' ')
|
408
|
+
printlist(industry_zz,numperline=5,beforehand=' ',separator=' ')
|
409
|
+
printlist(industry_gz,numperline=5,beforehand=' ',separator=' ')
|
410
|
+
printlist(industry_others,numperline=5,beforehand=' ',separator=' ')
|
411
|
+
|
412
|
+
return None
|
413
|
+
|
414
|
+
if dft is None: continue
|
415
|
+
|
416
|
+
dft['Date']=dft['日期'].apply(lambda x: pd.to_datetime(x))
|
417
|
+
dft.set_index('Date',inplace=True)
|
418
|
+
dft['name']=iname
|
419
|
+
dft['currency']=''
|
420
|
+
dft[i]=dft[t]
|
421
|
+
dftp=dft[(dft.index >= startdt) & (dft.index <= enddt)]
|
422
|
+
|
423
|
+
dft1=dftp[[i,'name','currency']]
|
424
|
+
"""
|
425
|
+
if not (dft1 is None):
|
426
|
+
columns=create_tuple_for_columns(dft1,iname)
|
427
|
+
dft1.columns=pd.MultiIndex.from_tuples(columns)
|
428
|
+
"""
|
429
|
+
if df is None:
|
430
|
+
df=dft1
|
431
|
+
else:
|
432
|
+
df=pd.merge(df,dft1,how='outer',left_index=True,right_index=True)
|
433
|
+
|
434
|
+
# 去掉多余的name/currency重复列
|
435
|
+
if df is None: return None
|
436
|
+
collist=list(df)
|
437
|
+
if not (('name' in collist) and ('currency' in collist)):
|
438
|
+
df.rename(columns={'name_x':'name','currency_x':'currency'},inplace=True)
|
439
|
+
|
440
|
+
collist=list(df)
|
441
|
+
collist1=[]
|
442
|
+
for c in collist:
|
443
|
+
if "_" not in c:
|
444
|
+
collist1=collist1+[c]
|
445
|
+
|
446
|
+
df1=df[collist1]
|
447
|
+
|
448
|
+
return df1
|
449
|
+
|
450
|
+
#==============================================================================
|
451
|
+
if __name__=='__main__':
|
452
|
+
print(is_alphanumeric("abc123")) # True
|
453
|
+
print(is_alphanumeric("abcd123!")) # False
|
454
|
+
print(is_alphanumeric("1234567890")) # True
|
455
|
+
print(is_alphanumeric("Hello World")) # False
|
456
|
+
print(is_alphanumeric("中证500"))
|
457
|
+
|
458
|
+
def is_alphanumeric(string):
|
459
|
+
import re
|
460
|
+
pattern = r'^[a-zA-Z0-9]+$' # 定义正则表达式模式
|
461
|
+
|
462
|
+
if re.match(pattern, string):
|
463
|
+
return True
|
464
|
+
else:
|
465
|
+
return False
|
466
|
+
|
467
|
+
|
468
|
+
#==============================================================================
|
469
|
+
if __name__=='__main__':
|
470
|
+
code='H30533.ZZ'
|
471
|
+
code='801730.SW'
|
472
|
+
|
473
|
+
funddb_name(code)
|
474
|
+
|
475
|
+
|
476
|
+
def funddb_name(code):
|
477
|
+
"""
|
478
|
+
翻译指数代码为韭圈儿名称。指数估值专用!
|
479
|
+
输入:指数代码。输出:韭圈儿指数名称
|
480
|
+
"""
|
481
|
+
import pandas as pd
|
482
|
+
ecdict=pd.DataFrame([
|
483
|
+
|
484
|
+
# 申万行业/主题指数
|
485
|
+
['801735.SW','光伏设备(申万)'],
|
486
|
+
['801730.SW','电力设备(申万)'],
|
487
|
+
['801780.SW','银行(申万)'],
|
488
|
+
['801740.SW','国防军工(申万)'],
|
489
|
+
['801720.SW','建筑装饰(申万)'],
|
490
|
+
['801110.SW','家用电器(申万)'],
|
491
|
+
['801102.SW','通信设备(申万)'],
|
492
|
+
['801194.SW','保险Ⅱ(申万)'],
|
493
|
+
['801770.SW','通信(申万)'],
|
494
|
+
['801050.SW','有色金属(申万)'],
|
495
|
+
['801812.SW','中盘指数(申万)'],
|
496
|
+
['801152.SW','生物制品(申万)'],
|
497
|
+
['801811.SW','大盘指数(申万)'],
|
498
|
+
['801970.SW','环保(申万)'],
|
499
|
+
['801120.SW','食品饮料(申万)'],
|
500
|
+
['801170.SW','交通运输(申万)'],
|
501
|
+
['801150.SW','医药生物(申万)'],
|
502
|
+
['801980.SW','美容护理(申万)'],
|
503
|
+
['801160.SW','公用事业(申万)'],
|
504
|
+
['801950.SW','煤炭(申万)'],
|
505
|
+
['801151.SW','化学制药(申万)'],
|
506
|
+
['801130.SW','纺织服饰(申万)'],
|
507
|
+
['801960.SW','石油石化(申万)'],
|
508
|
+
['801890.SW','机械设备(申万)'],
|
509
|
+
['801790.SW','非银金融(申万)'],
|
510
|
+
['801813.SW','小盘指数(申万)'],
|
511
|
+
['801030.SW','基础化工(申万)'],
|
512
|
+
['801193.SW','券商Ⅱ(申万)'],
|
513
|
+
['801210.SW','社会服务(申万)'],
|
514
|
+
['801140.SW','轻工制造(申万)'],
|
515
|
+
['801760.SW','传媒(申万)'],
|
516
|
+
['801710.SW','建筑材料(申万)'],
|
517
|
+
['801080.SW','电子(申万)'],
|
518
|
+
['801040.SW','钢铁(申万)'],
|
519
|
+
['801200.SW','商贸零售(申万)'],
|
520
|
+
['801017.SW','养殖业(申万)'],
|
521
|
+
['801180.SW','房地产(申万)'],
|
522
|
+
['801230.SW','综合(申万)'],
|
523
|
+
['801010.SW','农林牧渔(申万)'],
|
524
|
+
['801880.SW','汽车(申万)'],
|
525
|
+
['801736.SW','风电设备(申万)'],
|
526
|
+
['801750.SW','计算机(申万)'],
|
527
|
+
|
528
|
+
# 沪深交易所行业指数
|
529
|
+
['399976.SZ','CS新能车'],
|
530
|
+
['399995.SZ','中证基建工程'],
|
531
|
+
['399812.SZ','养老产业'],
|
532
|
+
['000986.SS','全指能源'],
|
533
|
+
['399986.SZ','中证银行'],
|
534
|
+
['000992.SS','全指金融地产'],
|
535
|
+
['000991.SS','全指医药'],
|
536
|
+
['399285.SZ','物联网50'],
|
537
|
+
['399997.SZ','中证白酒'],
|
538
|
+
|
539
|
+
['000987.SS','全指材料'],
|
540
|
+
['000993.SS','全指信息'],
|
541
|
+
['399610.SZ','TMT50'],
|
542
|
+
['399975.SZ','证券公司'],
|
543
|
+
['399804.SZ','中证体育'],
|
544
|
+
['000992.SS','全指金融地产'],
|
545
|
+
['000991.SS','全指医药'],
|
546
|
+
['399285.SZ','物联网50'],
|
547
|
+
['399997.SZ','中证白酒'],
|
548
|
+
|
549
|
+
# 中证行业指数
|
550
|
+
['H30533.ZZ','中国互联网50'],
|
551
|
+
['931151.ZZ','光伏产业'],
|
552
|
+
['930614.ZZ','环保50'],
|
553
|
+
['000812.ZZ','细分机械'],
|
554
|
+
['931755.ZZ','SEEE碳中和'],
|
555
|
+
['931719.ZZ','CS电池'],
|
556
|
+
['930771.ZZ','中证新能源'],
|
557
|
+
['930697.ZZ','家用电器'],
|
558
|
+
['000994.ZZ','全指通信'],
|
559
|
+
['931160.ZZ','通信设备'],
|
560
|
+
|
561
|
+
['930820.ZZ','CS高端制'],
|
562
|
+
['000811.ZZ','细分有色'],
|
563
|
+
['930632.ZZ','CS稀金属'],
|
564
|
+
['000986.SS','全指能源'],
|
565
|
+
['930716.ZZ','CS物流'],
|
566
|
+
['000990.ZZ','全指消费'],
|
567
|
+
['930726.ZZ','CS生医'],
|
568
|
+
['930712.ZZ','CS物联网'],
|
569
|
+
['930598.ZZ','稀土产业'],
|
570
|
+
['H30178.ZZ','医疗保健'],
|
571
|
+
|
572
|
+
['931079.ZZ','5G通信'],
|
573
|
+
['931456.ZZ','中国教育'],
|
574
|
+
['931484.ZZ','CS医药创新'],
|
575
|
+
['H30217.ZZ','医疗器械'],
|
576
|
+
['H30205.ZZ','饮料指数'],
|
577
|
+
['000995.ZZ','全指公用'],
|
578
|
+
['000813.ZZ','细分化工'],
|
579
|
+
['930651.ZZ','CS计算机'],
|
580
|
+
['H30199.ZZ','中证全指电力指数'],
|
581
|
+
['930652.ZZ','CS电子'],#中证电子指数
|
582
|
+
|
583
|
+
['H30202.ZZ','软件指数'],
|
584
|
+
['931009.ZZ','建筑材料'],
|
585
|
+
['930713.ZZ','CS人工智'],
|
586
|
+
['h30184.ZZ','中证全指半导体'],
|
587
|
+
['000949.ZZ','中证农业'],
|
588
|
+
['931775.ZZ','中证全指房地产'],
|
589
|
+
|
590
|
+
|
591
|
+
# 主要市场通用指数
|
592
|
+
['399006.SZ','创业板指'],#创业板指数
|
593
|
+
['^FTSE','英国富时100'],
|
594
|
+
['^HSI','恒生指数'],
|
595
|
+
['000009.SS','上证380'],
|
596
|
+
['^CAC','法国CAC40'],
|
597
|
+
['^RTS','俄罗斯RTS'],
|
598
|
+
['399102.SZ','创业板综'],#创业板综合指数
|
599
|
+
['^VN30','胡志明指数'],
|
600
|
+
['000010.SS','上证180'],
|
601
|
+
['000300.SS','沪深300'],
|
602
|
+
|
603
|
+
['000906.SS','中证800'],
|
604
|
+
['399330.SZ','深证100'],
|
605
|
+
['^BSESN','印度SENSEX30'],
|
606
|
+
['399001.SZ','深证成指'],
|
607
|
+
['000905.SS','中证500'],
|
608
|
+
['000001.SS','上证指数'],
|
609
|
+
['000016.SS','上证50'],
|
610
|
+
['000852.SS','中证1000'],
|
611
|
+
['^N225','日经225'],
|
612
|
+
['399303.SZ','国证2000'],
|
613
|
+
|
614
|
+
['000903.SS','中证100'],
|
615
|
+
['^SPX','标普500'],['^GSPC','标普500'],
|
616
|
+
['899050.BJ','北证50'],
|
617
|
+
['^KS11','韩国综合指数'],
|
618
|
+
['^DJI','道琼斯工业指数'],
|
619
|
+
['^NDX','纳斯达克100'],
|
620
|
+
['932000.ZZ','中证2000'],#中证规模指数系列
|
621
|
+
['^AXAT','澳洲标普200'],#澳大利亚标普200指数
|
622
|
+
|
623
|
+
], columns=['eword','cword'])
|
624
|
+
|
625
|
+
try:
|
626
|
+
cword=ecdict[ecdict['eword']==code]['cword'].values[0]
|
627
|
+
except:
|
628
|
+
#未查到代码名称,返回原代码
|
629
|
+
cword=code
|
630
|
+
|
631
|
+
return cword
|
632
|
+
#==============================================================================
|
633
|
+
|
634
|
+
if __name__=='__main__':
|
635
|
+
tickers='PZU.PL'
|
636
|
+
tickers='JD'
|
637
|
+
tickers='600519.SS'
|
638
|
+
tickers='00700.HK'
|
639
|
+
tickers='光伏设备(申万)'
|
640
|
+
tickers='中证500'
|
641
|
+
tickers='801735.SW'
|
642
|
+
|
643
|
+
tickers=['PZU.PL','WIG.PL']
|
644
|
+
tickers=['PZU.PL','JD','600519.SS','00700.HK','801735.SW','光伏设备(申万)','中证500']
|
645
|
+
|
646
|
+
indicators='pe'
|
647
|
+
indicators=['pe','pb']
|
648
|
+
indicators=['pe','pb','mv']
|
649
|
+
|
650
|
+
start='2023-1-1'; end='2023-11-30'
|
651
|
+
|
652
|
+
df_mix=get_valuation(tickers,indicators,start,end)
|
653
|
+
|
654
|
+
def get_valuation(tickers,indicators,start,end):
|
655
|
+
"""
|
656
|
+
功能:获取估值信息pe/pb/mv
|
657
|
+
若tickers为多个,则indicators取第一个
|
658
|
+
若tickers为单个,则indicators取所有
|
659
|
+
"""
|
660
|
+
|
661
|
+
if isinstance(tickers,str):
|
662
|
+
tickers=[tickers]
|
663
|
+
|
664
|
+
# 若遇到指数,先转换为韭圈儿的行业名称,以免被误认为股票代码
|
665
|
+
tickers1=[]
|
666
|
+
for t in tickers:
|
667
|
+
t1=funddb_name(t)
|
668
|
+
tickers1=tickers1+[t1]
|
669
|
+
|
670
|
+
if isinstance(indicators,str):
|
671
|
+
indicators=[indicators]
|
672
|
+
|
673
|
+
# 若为多个证券代码,则仅取第一个指标
|
674
|
+
if len(tickers)>1:
|
675
|
+
indicators1=[indicators[0]]
|
676
|
+
else:
|
677
|
+
indicators1=indicators
|
678
|
+
|
679
|
+
# 百度股市百事通不支持指数估值,遇到指数代码需要先转为名称获取韭圈儿估值数据
|
680
|
+
|
681
|
+
|
682
|
+
df=None
|
683
|
+
for t in tickers1:
|
684
|
+
print(" Searchng valuation info for",t,"......")
|
685
|
+
t1=t.upper()
|
686
|
+
result,prefix,suffix=split_prefix_suffix(t1)
|
687
|
+
iname=codetranslate(t1)
|
688
|
+
|
689
|
+
gotit=False
|
690
|
+
# A股或港股?
|
691
|
+
if not gotit and (result and suffix in ['SS','SZ','BJ','HK']):
|
692
|
+
dft=get_stock_valuation_cn_hk(t1,indicators1,start,end)
|
693
|
+
if dft is not None: gotit=True
|
694
|
+
|
695
|
+
# 波兰股?
|
696
|
+
if not gotit and (result and suffix in ['PL','WA']):
|
697
|
+
dft=get_stock_valuation_pl(t1,indicators1,start,end)
|
698
|
+
if dft is not None: gotit=True
|
699
|
+
|
700
|
+
# 申万指数代码?
|
701
|
+
if not gotit and (result and suffix in ['SW']):
|
702
|
+
dft=get_index_valuation_funddb(t1,indicators1,start,end)
|
703
|
+
if dft is not None:
|
704
|
+
gotit=True
|
705
|
+
iname=industry_sw_name(t1)
|
706
|
+
|
707
|
+
# 美股?
|
708
|
+
if not gotit and (not result and (is_alphanumeric(prefix) or '^' in prefix)):
|
709
|
+
dft=get_stock_valuation_us(t1,indicators1,start,end)
|
710
|
+
if dft is not None: gotit=True
|
711
|
+
|
712
|
+
# 行业指数名称?
|
713
|
+
if not gotit and (not result):
|
714
|
+
dft=get_index_valuation_funddb(t1,indicators1,start,end)
|
715
|
+
if dft is not None: gotit=True
|
716
|
+
|
717
|
+
if not gotit:
|
718
|
+
print(" #Warning(get_valuation): failed to retrieve info for",t1)
|
719
|
+
continue
|
720
|
+
|
721
|
+
if not (dft is None):
|
722
|
+
columns=create_tuple_for_columns(dft,iname)
|
723
|
+
dft.columns=pd.MultiIndex.from_tuples(columns)
|
724
|
+
|
725
|
+
# 合成
|
726
|
+
if df is None:
|
727
|
+
df=dft
|
728
|
+
else:
|
729
|
+
#df=pd.merge(df,dft,how='inner',left_index=True,right_index=True)
|
730
|
+
df=pd.merge(df,dft,how='outer',left_index=True,right_index=True)
|
731
|
+
|
732
|
+
# 缺失值填充
|
733
|
+
if not (df is None):
|
734
|
+
#df.fillna(method='backfill',inplace=True)
|
735
|
+
df.fillna(method='ffill',inplace=True)
|
736
|
+
|
737
|
+
return df
|
738
|
+
|
739
|
+
|
740
|
+
#==============================================================================
|
741
|
+
if __name__=='__main__':
|
742
|
+
tickers='PZU.PL'
|
743
|
+
indicators='PE'
|
744
|
+
start='2023-1-1'; end='2023-11-30'
|
745
|
+
loc1='best'
|
746
|
+
|
747
|
+
tickers='PZU.PL'
|
748
|
+
indicators=['PE','PB']
|
749
|
+
start='2023-1-1'; end='2023-11-30'
|
750
|
+
twinx=True
|
751
|
+
loc1='lower left'; loc2='upper right'
|
752
|
+
|
753
|
+
tickers=['JD','PDD']
|
754
|
+
indicators='PE'
|
755
|
+
start='2023-1-1'; end='2023-11-30'
|
756
|
+
twinx=True
|
757
|
+
loc1='lower left'; loc2='upper right'
|
758
|
+
|
759
|
+
tickers=['600519.SS','000858.SZ']
|
760
|
+
indicators='PE'
|
761
|
+
start='2023-1-1'; end='2023-11-30'
|
762
|
+
twinx=True
|
763
|
+
loc1='lower left'; loc2='upper right'
|
764
|
+
|
765
|
+
tickers=['JD','PDD','BABA']
|
766
|
+
indicators='PE'
|
767
|
+
start='2023-1-1'; end='2023-11-30'
|
768
|
+
loc1='best'
|
769
|
+
|
770
|
+
tickers='JD'
|
771
|
+
indicators=['PE','PB','MV']
|
772
|
+
start='2023-1-1'; end='2023-11-30'
|
773
|
+
loc1='best'
|
774
|
+
|
775
|
+
def security_valuation(tickers,indicators,start,end, \
|
776
|
+
twinx=False,loc1='best',loc2='best', \
|
777
|
+
graph=True,annotate=False):
|
778
|
+
"""
|
779
|
+
功能:绘制估值走势
|
780
|
+
"""
|
781
|
+
|
782
|
+
# 获取估值信息
|
783
|
+
df=get_valuation(tickers,indicators,start,end)
|
784
|
+
if df is None:
|
785
|
+
print(" #Warning(security_valuation): retrieved none of",indicators,"for",tickers)
|
786
|
+
return None
|
787
|
+
|
788
|
+
if not graph: return df
|
789
|
+
|
790
|
+
# 判断估值信息结构
|
791
|
+
names=[]
|
792
|
+
indicators=[]
|
793
|
+
|
794
|
+
mcollist=list(df)
|
795
|
+
for mc in mcollist:
|
796
|
+
if mc[0] not in ['name','currency']:
|
797
|
+
indicators=indicators+[mc[0]]
|
798
|
+
names=names+[mc[1]]
|
799
|
+
|
800
|
+
names1=list(set(names))
|
801
|
+
indicators1=list(set(indicators))
|
802
|
+
|
803
|
+
name_num=len(names1)
|
804
|
+
indicator_num=len(indicators1)
|
805
|
+
|
806
|
+
import datetime
|
807
|
+
# 绘制一条线+均值/中位数虚线
|
808
|
+
if name_num * indicator_num == 1:
|
809
|
+
i=indicators1[0]
|
810
|
+
t=names1[0]
|
811
|
+
df2=df[i]
|
812
|
+
df2.rename(columns={t:i},inplace=True)
|
813
|
+
|
814
|
+
df2['平均值']=df2[i].mean()
|
815
|
+
df2['中位数']=df2[i].median()
|
816
|
+
|
817
|
+
titletxt="证券估值走势:"+t
|
818
|
+
|
819
|
+
footnote1="注:PE为市盈率,PB为市净率,MV为总市值(十亿交易所所在地货币单位)"
|
820
|
+
todaydt = datetime.date.today()
|
821
|
+
footnote9="数据来源: baidu/stooq/funddb/swhysc,"+str(todaydt)
|
822
|
+
footnote=footnote1+'\n'+footnote9
|
823
|
+
|
824
|
+
ylabeltxt=i
|
825
|
+
|
826
|
+
draw_lines(df2,y_label=ylabeltxt,x_label=footnote, \
|
827
|
+
axhline_value=0,axhline_label='', \
|
828
|
+
title_txt=titletxt,data_label=False, \
|
829
|
+
resample_freq='D',loc=loc1)
|
830
|
+
|
831
|
+
return df
|
832
|
+
|
833
|
+
# 绘制双线: 一只证券,两个指标。允许twinx双轴绘图
|
834
|
+
if name_num == 1 and indicator_num == 2:
|
835
|
+
t=names1[0]
|
836
|
+
i1=indicators1[0]; i2=indicators1[1]
|
837
|
+
df2_1=df[i1]; df2_2=df[i2]
|
838
|
+
df2_1.rename(columns={t:i1},inplace=True)
|
839
|
+
df2_2.rename(columns={t:i2},inplace=True)
|
840
|
+
|
841
|
+
titletxt="证券估值走势对比:"+t
|
842
|
+
|
843
|
+
footnote1="注:PE为市盈率,PB为市净率,MV为总市值(十亿交易所所在地货币单位)"
|
844
|
+
todaydt = datetime.date.today()
|
845
|
+
footnote9="数据来源: baidu/stooq/funddb/swhysc,"+str(todaydt)
|
846
|
+
footnote=footnote1+'\n'+footnote9
|
847
|
+
|
848
|
+
colname1=label1=i1
|
849
|
+
colname2=label2=i2
|
850
|
+
|
851
|
+
plot_line2(df2_1,'',colname1,label1, \
|
852
|
+
df2_2,'',colname2,label2, \
|
853
|
+
ylabeltxt='',titletxt=titletxt,footnote=footnote, \
|
854
|
+
twinx=twinx, \
|
855
|
+
resample_freq='D',loc1=loc1,loc2=loc2, \
|
856
|
+
color1='red',color2='blue')
|
857
|
+
return df
|
858
|
+
|
859
|
+
# 绘制双线: 两只证券,一个指标。允许twinx双轴绘图
|
860
|
+
if name_num == 2 and indicator_num == 1:
|
861
|
+
t1=names1[0]; t2=names1[1]
|
862
|
+
i=indicators1[0]
|
863
|
+
df2_1=pd.DataFrame(df[i,t1])[i]; df2_2=pd.DataFrame(df[i,t2])[i]
|
864
|
+
df2_1.rename(columns={t1:i},inplace=True)
|
865
|
+
df2_2.rename(columns={t2:i},inplace=True)
|
866
|
+
|
867
|
+
titletxt="证券估值走势对比:"+i
|
868
|
+
|
869
|
+
footnote1="注:PE为市盈率,PB为市净率,MV为总市值(十亿交易所所在地货币单位)"
|
870
|
+
todaydt = datetime.date.today()
|
871
|
+
footnote9="数据来源: baidu/stooq/funddb/swhysc,"+str(todaydt)
|
872
|
+
footnote=footnote1+'\n'+footnote9
|
873
|
+
|
874
|
+
colname1=i; label1=t1
|
875
|
+
colname2=i; label2=t2
|
876
|
+
|
877
|
+
if twinx:
|
878
|
+
ylabeltxt=''
|
879
|
+
else:
|
880
|
+
ylabeltxt=i
|
881
|
+
|
882
|
+
plot_line2(df2_1,'',colname1,label1, \
|
883
|
+
df2_2,'',colname2,label2, \
|
884
|
+
ylabeltxt=ylabeltxt,titletxt=titletxt,footnote=footnote, \
|
885
|
+
twinx=twinx, \
|
886
|
+
resample_freq='D',loc1=loc1,loc2=loc2, \
|
887
|
+
color1='red',color2='blue')
|
888
|
+
|
889
|
+
return df
|
890
|
+
|
891
|
+
# 绘制多线:多只证券,一个指标。简单多线绘图
|
892
|
+
if name_num > 2 and indicator_num == 1:
|
893
|
+
i=indicators1[0]
|
894
|
+
df2=df[i]
|
895
|
+
|
896
|
+
titletxt="证券估值走势:"+i
|
897
|
+
|
898
|
+
footnote1="注:PE为市盈率,PB为市净率,MV为总市值(十亿交易所所在地货币单位)"
|
899
|
+
todaydt = datetime.date.today()
|
900
|
+
footnote9="数据来源: baidu/stooq/funddb/swhysc,"+str(todaydt)
|
901
|
+
footnote=footnote1+'\n'+footnote9
|
902
|
+
|
903
|
+
ylabeltxt=i
|
904
|
+
|
905
|
+
draw_lines(df2,y_label=ylabeltxt,x_label=footnote, \
|
906
|
+
axhline_value=0,axhline_label='', \
|
907
|
+
title_txt=titletxt,data_label=False, \
|
908
|
+
resample_freq='D',loc=loc1,annotate=annotate)
|
909
|
+
|
910
|
+
return df
|
911
|
+
|
912
|
+
# 绘制多线:一只证券,多个指标。简单多线绘图
|
913
|
+
if name_num == 1 and indicator_num > 2:
|
914
|
+
t=names1[0]
|
915
|
+
df2=None
|
916
|
+
for i in indicators1:
|
917
|
+
dft=pd.DataFrame(df[i,t])[i]
|
918
|
+
dft.rename(columns={t:i},inplace=True)
|
919
|
+
|
920
|
+
if df2 is None:
|
921
|
+
df2=dft
|
922
|
+
else:
|
923
|
+
df2=pd.merge(df2,dft,left_index=True,right_index=True)
|
924
|
+
|
925
|
+
titletxt="证券估值走势:"+t
|
926
|
+
|
927
|
+
footnote1="注:PE为市盈率,PB为市净率,MV为总市值(十亿交易所所在地货币单位)"
|
928
|
+
todaydt = datetime.date.today()
|
929
|
+
footnote9="数据来源: baidu/stooq/funddb/swhysc,"+str(todaydt)
|
930
|
+
footnote=footnote1+'\n'+footnote9
|
931
|
+
|
932
|
+
ylabeltxt=''
|
933
|
+
|
934
|
+
draw_lines(df2,y_label=ylabeltxt,x_label=footnote, \
|
935
|
+
axhline_value=0,axhline_label='', \
|
936
|
+
title_txt=titletxt,data_label=False, \
|
937
|
+
resample_freq='D',loc=loc1,annotate=annotate)
|
938
|
+
|
939
|
+
return df
|
940
|
+
|
941
|
+
|
942
|
+
|
943
|
+
#==============================================================================
|
944
|
+
#==============================================================================
|
945
|
+
#==============================================================================
|
946
|
+
#==============================================================================
|
947
|
+
#==============================================================================
|
948
|
+
|