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/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
+