siat 3.7.28__py3-none-any.whl → 3.8.1__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/economy2.py ADDED
@@ -0,0 +1,914 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ 本模块功能:宏观经济基本面分析
4
+ 所属工具包:证券投资分析工具SIAT
5
+ SIAT:Security Investment Analysis Tool
6
+ 创建日期:2025年3月9日
7
+ 最新修订日期:2025年3月10日
8
+ 作者:王德宏 (WANG Dehong, Peter)
9
+ 作者单位:北京外国语大学国际商学院
10
+ 版权所有:王德宏
11
+ 用途限制:仅限研究与教学使用,不可商用!商用需要额外授权。
12
+ 特别声明:作者不对使用本工具进行证券投资导致的任何损益负责!
13
+ """
14
+ #==============================================================================
15
+ #关闭所有警告
16
+ import warnings; warnings.filterwarnings('ignore')
17
+ from siat.grafix import *
18
+ from siat.common import *
19
+ from siat.translate import *
20
+ #==============================================================================
21
+ import pandas as pd
22
+ from pandas_datareader import wb
23
+ import requests
24
+
25
+ #==============================================================================
26
+ if __name__=='__main__':
27
+ key_words='GDP per capita'
28
+ top=5; note=False
29
+
30
+ indicator_wb(key_words='GDP',top=10,note=False)
31
+
32
+ def find_economic_indicator(key_words='GDP',top=10, \
33
+ note=False,translate=False,source='wb'):
34
+ """
35
+ ===========================================================================
36
+ 功能:查找宏观经济指标代码
37
+ 参数:
38
+ key_words:关键词,支持多关键词,使用空格隔开,默认'GDP'
39
+ top:显示最相似的若干个指标,默认10
40
+ note:是否显示指标的描述,默认否False
41
+ translate:是否调用AI大模型进行翻译,若翻译可能影响反应速度,默认否False
42
+ source:信息来源,默认'wb'
43
+
44
+ 注意:有时网络可能拒绝访问,可以换个时段再次进行访问
45
+ """
46
+ if source=='wb':
47
+ df=indicator_wb(key_words=key_words,top=top,note=note,translate=translate)
48
+ else:
49
+ print(" Sorry, the source option currently only supports wb (World Bank)")
50
+
51
+ return
52
+
53
+ def indicator_wb(key_words='GDP',top=20,note=False,translate=False):
54
+ """
55
+ ============================================================================
56
+ 功能:在世界银行数据库中查找宏观经济指标的代码
57
+ 参数:
58
+ key_words:可包括多个关键词,使用空格隔开,不区分大小写
59
+ top:输出相似度最高的,默认5个
60
+ note:是否显示每个指标的注释,默认False
61
+
62
+ 输出:基于文本相似度,输出可能的指标代码及其简介
63
+
64
+ 返回值:可能的指标代码及其简介
65
+ """
66
+
67
+ # 拆分关键词字符串为列表
68
+ words_list=key_words.split(' ')
69
+
70
+ # 循环查找每个关键词,并合成
71
+ df=None
72
+ for word in words_list:
73
+ try:
74
+ df_tmp=wb.search(word)
75
+ except:
76
+ print(" Sorry, currently source rejected connection, try again later")
77
+ return None
78
+
79
+ # 合并
80
+ if df is None:
81
+ df=df_tmp
82
+ else:
83
+ df=pd.concat([df,df_tmp])
84
+
85
+ # 去重
86
+ df.drop_duplicates(subset=['id'],keep='first',inplace=True)
87
+
88
+ # 去掉名称中的逗号、左右括号、美元符号和百分号,以免降低模糊匹配度
89
+ import re
90
+ #df['name2']=df['name'].apply(lambda x: re.sub('[(),$%]', '', x))
91
+ df['name2']=df['name'].apply(lambda x: re.sub('[(),]', '', x))
92
+
93
+ # 匹配相似度
94
+ df2=fuzzy_search_wb(df,key_words=key_words,column='name2',top=top)
95
+
96
+ # 遍历输出
97
+ #print('') #空一行
98
+ for row in df2.itertuples():
99
+
100
+ print(f"{row.id}",end=': ')
101
+ if not translate:
102
+ print(f"{row.name}",end='')
103
+ else:
104
+ print(f"{row.name}[{lang_auto2(row.name)}]",end='')
105
+ if row.unit != '':
106
+ print(f", unit: {row.unit}")
107
+ else:
108
+ print('')
109
+ if note:
110
+ if row.sourceNote != '':
111
+ print(f"{row.sourceNote}")
112
+ if translate:
113
+ print(f"{lang_auto2(row.sourceNote)}")
114
+ print('') #空一行
115
+
116
+ return df2
117
+
118
+ #==============================================================================
119
+ if __name__=='__main__':
120
+ key_words='GDP per capita'
121
+ column='name'
122
+ top=10
123
+
124
+ def fuzzy_search_wb(df,key_words='GDP per capita',column='name',top=10):
125
+ """
126
+ ===========================================================================
127
+ 功能:给定key_words,模糊搜索df的column字段,列出匹配度最高的10个指标及其解释
128
+ 参数:
129
+ df:wb.search产生的指标集
130
+ column:需要搜索的字段名,默认'id'
131
+ key_words:需要匹配的关键词组,默认'GDP'
132
+ top:列出模糊匹配度最高的若干个指标,默认10
133
+
134
+ 输出:无
135
+ 返回:指标列表
136
+ """
137
+
138
+ # 将关键词组和列中的每个值都转换为小写单词集合
139
+ def normalize_text(text):
140
+ return set(text.lower().split())
141
+
142
+ # 应用函数并比较集合
143
+ df["normalized_"+column] = df[column].apply(normalize_text)
144
+ key_words_set = normalize_text(key_words)
145
+
146
+ # 计算相似度(基于集合的交集和并集)
147
+ def calculate_similarity(text_set, key_words_set):
148
+ intersection = text_set.intersection(key_words_set)
149
+ union = text_set.union(key_words_set)
150
+ return len(intersection) / len(union)
151
+
152
+ df["similarity"] = df["normalized_"+column].apply(lambda x: calculate_similarity(x, key_words_set))
153
+
154
+ # 按相似度降序
155
+ df.sort_values(['similarity'], ascending = False, inplace=True)
156
+
157
+ df2=df[['id','name','unit','sourceNote']].head(top)
158
+
159
+ return df2
160
+
161
+ #==============================================================================
162
+ if __name__ =="__main__":
163
+ indicator="NY.GDP.MKTP.KN"
164
+ indicator="6.0.GDP_current"
165
+ indicator="XYZ123"
166
+
167
+ indicator_name_wb(indicator)
168
+
169
+ def indicator_name_wb(indicator='NY.GDP.MKTP.KN'):
170
+ """
171
+ ===========================================================================
172
+ 功能:抓取World Bank网页上指标的名称
173
+ indicator:WB指标名称,默认'NY.GDP.MKTP.KN'
174
+ """
175
+
176
+ # 构造 API 请求 URL
177
+ url = f"https://api.worldbank.org/v2/indicator/{indicator}?format=json"
178
+
179
+ # 发送请求
180
+ response = requests.get(url)
181
+ data = response.json()
182
+
183
+ # 提取指标名称
184
+ try:
185
+ indicator_name = data[1][0]['name']
186
+ except:
187
+ indicator_name = ''
188
+
189
+ return indicator_name
190
+
191
+
192
+ #==============================================================================
193
+ if __name__ =="__main__":
194
+ ticker='CN'
195
+ indicator="NY.GDP.MKTP.KN"
196
+ indicator="GC.XPN.TOTL.GD.ZS"
197
+
198
+ start='2010'; end='2025'; power=3
199
+
200
+ zeroline=False
201
+ attention_value=''; attention_value_area=''
202
+ attention_point=''; attention_point_area=''
203
+ average_value=False
204
+ datatag=False; graph=True
205
+ mark_top=True; mark_bottom=True; mark_end=True
206
+ facecolor='whitesmoke';loc='best'
207
+
208
+
209
+ df=economy_indicator_wb(ticker,indicator,start,end,power=3)
210
+
211
+ def economy_indicator_wb(ticker='CN',indicator='NY.GDP.MKTP.KN', \
212
+ start='L10Y',end='today',translate=False, \
213
+ zeroline=False, \
214
+ attention_value='',attention_value_area='', \
215
+ attention_point='',attention_point_area='', \
216
+ average_value=False, \
217
+ datatag=False,power=0,graph=True, \
218
+ mark_top=True,mark_bottom=True,mark_end=True, \
219
+ facecolor='whitesmoke',loc='best',maxticks=30):
220
+ """
221
+ ===========================================================================
222
+ 功能:绘制一个国家的一个宏观经济指标走势
223
+ 参数:
224
+ ticker:国家编码,两位,默认'CN'
225
+ indicator:宏观经济指标,默认GDP (constant LCU),即本币不变价格GDP
226
+ start:开始日期,默认近十年
227
+ end:结束日期,默认当前日期
228
+ zeroline:是否绘制零线,默认False
229
+ attention_value:纵轴关注值或其列表,默认无''
230
+ attention_value_area:纵轴关注值区间强调,默认无''
231
+ attention_point:横轴关注值或其列表,默认无''
232
+ attention_point_area:横轴关注值区间强调,默认无''
233
+ average_value:是否绘制均值线,默认否False
234
+ datatag:是否标记折线中的各个数据点的数值,默认否False
235
+ power:是否绘制趋势线,默认否0
236
+ graph:是否绘图,默认是True
237
+ mark_top, mark_bottom, mark_end:是否标记最高、最低和末端点:默认是True
238
+ facecolor:背景颜色,默认'whitesmoke'
239
+ loc:图例位置,默认自动'best'
240
+
241
+ 输出:图形
242
+ 返回值:数据表
243
+ """
244
+ # 检测指标是否存在,并取得指标名称
245
+ indicator_name=indicator_name_wb(indicator)
246
+ if indicator_name == '':
247
+ print(f" #Error(economy_indicator_wb): no indicator found for {indicator}")
248
+ return None
249
+
250
+ # 日期具体化
251
+ start,end=start_end_preprocess(start,end)
252
+
253
+ # 下载数据
254
+ try:
255
+ pricedf=wb.download(indicator=indicator,country=ticker,start=start,end=end)
256
+ except:
257
+ print(f" #Error(economy_indicator_wb): either {indicator} deprecated or {ticker} does not exist")
258
+ return None
259
+
260
+ # 是否返回None
261
+ if pricedf is None:
262
+ print(f" #Error(economy_indicator_wb): no data found on {indicator} for {ticker}")
263
+ return None
264
+ # 是否返回空的数据表
265
+ if len(pricedf) == 0:
266
+ print(f" #Error(economy_indicator_wb): zero data found on {indicator} for {ticker}")
267
+ return None
268
+ # 是否返回数据表但内容均为NaN
269
+ if pricedf[indicator].isnull().all():
270
+ print(f" #Error(economy_indicator_wb): all empty data found on {indicator} for {ticker}")
271
+ return None
272
+
273
+ pricedf.reset_index(inplace=True)
274
+ pricedf.set_index('year',inplace=True)
275
+ pricedf.rename(columns={indicator:indicator_name},inplace=True)
276
+ country=pricedf['country'].values[0]
277
+ pricedf.sort_index(inplace=True)
278
+ #pricedf.drop(columns='country',inplace=True)
279
+
280
+ erdf3=pricedf
281
+
282
+ # 换算数量单位
283
+ ind_max=erdf3[indicator_name].max(); ind_min=erdf3[indicator_name].min()
284
+ ind_median=erdf3[indicator_name].median()
285
+
286
+ kilo=1000; million=kilo * 1000; billion=million * 1000
287
+ trillion=billion * 1000; quadrillion=trillion * 1000
288
+
289
+ if ind_median > quadrillion:
290
+ unit=text_lang('单位:千万亿','in Quadrillions'); unit_amount=quadrillion
291
+ elif ind_median > trillion:
292
+ unit=text_lang('单位:万亿','in Trillions'); unit_amount=trillion
293
+ elif ind_median > billion:
294
+ unit=text_lang('单位:十亿','in Billions'); unit_amount=billion
295
+ elif ind_median > million:
296
+ unit=text_lang('单位:百万','in Millions'); unit_amount=million
297
+ elif ind_median > kilo:
298
+ unit=text_lang('单位:千','in Thousands'); unit_amount=kilo
299
+ else:
300
+ unit=''; unit_amount=1
301
+
302
+ erdf3['unit']=unit
303
+
304
+ if unit != '':
305
+ erdf3[indicator_name]=erdf3[indicator_name].apply(lambda x: round(x/unit_amount,2))
306
+
307
+ # 绘图
308
+ if not graph:
309
+ return erdf3
310
+
311
+ # 判断是否绘制零线
312
+ if ind_max * ind_min <0:
313
+ zeroline=True
314
+
315
+ titletxt1=text_lang("经济趋势分析","Economic Trend Analysis")
316
+ titletxt=titletxt1+': '+country+', '+indicator_name
317
+ if unit != '':
318
+ titletxt=titletxt+', '+unit
319
+
320
+ import datetime; todaydt = datetime.date.today()
321
+ sourcetxt=text_lang("数据来源:世界银行","Data source: World Bank")
322
+ footnote=sourcetxt+', '+str(todaydt)
323
+ collabel=indicator_name
324
+
325
+ ylabeltxt=indicator_name
326
+
327
+ # 为避免绘图出错,对空值进行插值
328
+ erdf3.interpolate(method='linear',limit_direction='both',inplace=True)
329
+
330
+ # 翻译:挪到绘图函数中
331
+ """
332
+ if translate:
333
+ ylabeltxt=lang_auto2(ylabeltxt)
334
+ titletxt=lang_auto2(titletxt)
335
+ """
336
+ try:
337
+ plot_line(erdf3,indicator_name,collabel,ylabeltxt,titletxt,footnote,datatag=datatag, \
338
+ power=power,zeroline=zeroline, \
339
+ average_value=average_value, \
340
+ attention_value=attention_value,attention_value_area=attention_value_area, \
341
+ attention_point=attention_point,attention_point_area=attention_point_area, \
342
+ mark_top=mark_top,mark_bottom=mark_bottom,mark_end=mark_end, \
343
+ facecolor=facecolor,loc=loc,maxticks=30,translate=translate)
344
+ except Exception as e:
345
+ # 捕获所有异常
346
+ print(f"Error:{e}")
347
+ print("Details:")
348
+ import traceback
349
+ traceback.print_exc()
350
+
351
+ return erdf3
352
+
353
+
354
+ #==============================================================
355
+ if __name__ =="__main__":
356
+ ticker='CN'
357
+ indicator=['NY.GDP.MKTP.CN','NY.GDP.MKTP.KN','NY.GDP.MKTP.CD','XYZ']
358
+ start='2010'
359
+ end='2025'
360
+
361
+ attention_value=''; attention_value_area=''
362
+ attention_point=''; attention_point_area=''
363
+ band_area=''
364
+ graph=True
365
+ smooth=True
366
+ loc='best'
367
+ facecolor='whitesmoke'
368
+ date_range=False
369
+ date_freq=False
370
+ annotate=False
371
+ annotate_value=False
372
+ mark_top=True; mark_bottom=True; mark_end=True
373
+ maxticks=30
374
+
375
+ df=economy_mindicators_wb(ticker,measures,fromdate,todate)
376
+
377
+ def economy_mindicators_wb(ticker='CN',indicator=['NY.GDP.MKTP.CN','NY.GDP.MKTP.KN'], \
378
+ start='L10Y',end='today', \
379
+ attention_value='',attention_value_area='', \
380
+ attention_point='',attention_point_area='', \
381
+ band_area='', \
382
+ graph=True,smooth=False,loc='best',facecolor='whitesmoke', \
383
+ date_range=False,date_freq=False, \
384
+ annotate=False,annotate_value=False, \
385
+ mark_top=False,mark_bottom=False,mark_end=False, \
386
+ maxticks=30,translate=False):
387
+ """
388
+ ===========================================================================
389
+ 功能:单个国家,多个宏观经济指标对比
390
+ 主要参数:
391
+ ticker:国家代码,默认'CN'
392
+ indicator:指标代码列表,默认['NY.GDP.MKTP.CN','NY.GDP.MKTP.KN']
393
+ start:开始日期,默认'L10Y'
394
+ end:截止日期,默认'today'
395
+ attention_value:纵轴关注值或其列表,默认无''
396
+ attention_value_area:纵轴关注值区间强调,默认无''
397
+ attention_point:横轴关注值或其列表,默认无''
398
+ attention_point_area:横轴关注值区间强调,默认无''
399
+ band_area:两条曲线之间强调,默认无''
400
+ graph:是否绘图,默认True
401
+ loc:图例位置,默认自动'best'
402
+ facecolor:画布背景颜色,默认'whitesmoke'
403
+ annotate:是否在曲线末端标注,默认否False
404
+ annotate_value:是否标注曲线末端值,默认否False
405
+ mark_top, mark_bottom, mark_end:是否标注最大、最小、末端值,默认否
406
+ maxticks=30:限制横轴刻度最大数量
407
+
408
+ date_range=False:指定开始结束日期绘图
409
+ date_freq=False:指定横轴日期间隔,例如'D'、'2D'、'W'、'M'等,横轴一般不超过25个标注,否则会重叠
410
+
411
+ 输出:图形
412
+ 返回值:数据表
413
+ """
414
+ DEBUG=False
415
+
416
+ measures=indicator
417
+ fromdate,todate=start_end_preprocess(start,end)
418
+
419
+ #处理ticker,允许1个
420
+ if isinstance(ticker,list):
421
+ if len(ticker) >= 1:
422
+ ticker=ticker[0]
423
+ else:
424
+ print(" #Error(economy_mindicators_wb): need at least 1 country to continue")
425
+ return None
426
+
427
+ #处理measures,允许多个
428
+ if isinstance(measures,str):
429
+ measures=[measures]
430
+
431
+ #屏蔽函数内print信息输出的类
432
+ import os, sys
433
+ class HiddenPrints:
434
+ def __enter__(self):
435
+ self._original_stdout = sys.stdout
436
+ sys.stdout = open(os.devnull, 'w')
437
+
438
+ def __exit__(self, exc_type, exc_val, exc_tb):
439
+ sys.stdout.close()
440
+ sys.stdout = self._original_stdout
441
+
442
+ df=pd.DataFrame(); have_data=False
443
+ indicator_list=[]; unit_list=[]
444
+ for m in measures:
445
+ print(f" Searching indicator {m} ... ...")
446
+
447
+ with HiddenPrints():
448
+ dftmp=economy_indicator_wb(ticker=ticker,indicator=m, \
449
+ start=fromdate,end=todate, \
450
+ graph=False)
451
+ if dftmp is None:
452
+ print(f" #Warning(economy_mindicators_wb): none found for {m} with {ticker}")
453
+ continue
454
+ #return None
455
+ if len(dftmp) ==0:
456
+ print(f" #Warning(economy_mindicators_wb): empty record found on {m} for {ticker}")
457
+ continue
458
+ #return None
459
+
460
+ have_data=True
461
+
462
+ country=dftmp['country'].values[0]
463
+ unit=dftmp['unit'].values[0]
464
+ unit_list=unit_list+[unit]
465
+ dftmp.drop(columns=['country','unit'],inplace=True)
466
+ indicator_name=list(dftmp)[0]
467
+
468
+ if m in band_area:
469
+ band_area = [indicator_name if x == m else x for x in band_area]
470
+
471
+ indicator_list=indicator_list+[indicator_name]
472
+
473
+ if len(df)==0:
474
+ df=dftmp
475
+ else:
476
+ df=pd.merge(df,dftmp,left_index=True,right_index=True)
477
+
478
+ if not graph: return df
479
+
480
+ if not have_data:
481
+ #print(f" #Error(economy_mindicators_wb): no record found on {indicator} for {ticker}")
482
+ return None
483
+
484
+ # 绘图
485
+ titletxt=text_lang("经济趋势分析","Economic Trend Analysis")+': '+country
486
+
487
+ y_label=text_lang('经济指标',"Economic Indicator")
488
+ import datetime; todaydt = datetime.date.today()
489
+ footnote2=text_lang("数据来源:世界银行","Data source: World Bank")+', '+str(todaydt)
490
+
491
+ one_unit=False
492
+ if len(set(unit_list)) == 1: one_unit=True
493
+ if one_unit:
494
+ x_label=footnote2
495
+ if unit != '':
496
+ titletxt=titletxt+', '+unit
497
+ else:
498
+ #footnote1='The units of '+str(indicator_list)+' are '+str(unit_list)+' respectively'
499
+ footnote1=text_lang('注:','Notes: '); indicator_list_len=len(indicator_list)
500
+ for ind in indicator_list:
501
+ pos=indicator_list.index(ind)
502
+ footnote1=footnote1+ind+' '+unit_list[pos]
503
+ if (pos < indicator_list_len - 1):
504
+ if (pos+1) % 2 != 0:
505
+ footnote1=footnote1+', '
506
+ else:
507
+ footnote1=footnote1+'\n'
508
+
509
+ x_label=footnote1+'\n'+footnote2
510
+
511
+ axhline_value=0; axhline_label=''
512
+ above_zero=0; below_zero=0
513
+ for c in list(df):
514
+ c_max=df[c].max(); c_min=df[c].min()
515
+ try:
516
+ if c_max>0 or c_min>0: above_zero+=1
517
+ if c_max<0 or c_min<0: below_zero+=1
518
+ except: continue
519
+
520
+ if above_zero>0 and below_zero>0: #有正有负
521
+ if DEBUG:
522
+ print("DEBUG: draw axhline=0")
523
+ axhline_value=0
524
+ axhline_label=text_lang('零线',"Zeroline")
525
+
526
+ # 为避免绘图出错,对空值进行插值
527
+ df.interpolate(method='linear',limit_direction='both',inplace=True)
528
+
529
+ draw_lines2(df,y_label,x_label,axhline_value,axhline_label,titletxt, \
530
+ data_label=False,resample_freq='1D',smooth=smooth, \
531
+ date_range=date_range,date_freq=date_freq,date_fmt='%Y-%m-%d', \
532
+ attention_value=attention_value,attention_value_area=attention_value_area, \
533
+ attention_point=attention_point,attention_point_area=attention_point_area, \
534
+ annotate=annotate,annotate_value=annotate_value, \
535
+ mark_top=mark_top,mark_bottom=mark_bottom,mark_end=mark_end,facecolor=facecolor, \
536
+ band_area=band_area,loc=loc,maxticks=maxticks,translate=translate)
537
+
538
+ return df
539
+
540
+
541
+ #==============================================================================
542
+ if __name__ =="__main__":
543
+ tickers=['CN','US','JP']
544
+ indicator='NY.GDP.MKTP.PP.CD'
545
+ start='L20Y'; end='today'
546
+
547
+ attention_value=''; attention_value_area=''
548
+ attention_point=''; attention_point_area=''
549
+ axhline_value=0; axhline_label=''
550
+ preprocess='none'; linewidth=1.5
551
+ scaling_option='start'
552
+ plus_sign=False
553
+ graph=True; loc='best'; facecolor='whitesmoke'
554
+ annotate=False; annotate_value=False
555
+ smooth=True
556
+ mark_top=True; mark_bottom=True; mark_end=False
557
+ maxticks=30
558
+
559
+
560
+
561
+ def economy_mtickers_wb(ticker=['CN','US','JP'],indicator='NY.GDP.MKTP.PP.CD', \
562
+ start='L15Y',end='today', \
563
+ attention_value='',attention_value_area='', \
564
+ attention_point='',attention_point_area='', \
565
+ axhline_value=0,axhline_label='', \
566
+ preprocess='none',linewidth=1.5, \
567
+ scaling_option='start', \
568
+ plus_sign=False, \
569
+ graph=True,facecolor='whitesmoke', \
570
+ band_area='',loc='best', \
571
+ annotate=False,annotate_value=False, \
572
+ smooth=False, \
573
+ mark_top=True,mark_bottom=True,mark_end=False, \
574
+ maxticks=30,translate=False):
575
+ """
576
+ ===========================================================================
577
+ 功能:比较并绘制多个国家的单宏观经济指标曲线
578
+ 主要参数:
579
+ ticker:国家代码,默认['CN','US','JP']
580
+ indicator:宏观经济指标,默认'NY.GDP.MKTP.PP.CD',即GDP PPP
581
+ start:开始日期,默认'L20Y'
582
+ end:截止日期,默认'today'
583
+ attention_value:纵轴关注值或其列表,默认无''
584
+ attention_value_area:纵轴关注区间强调,默认无''
585
+ attention_point:横轴关注值或其列表,默认无''
586
+ attention_point_area:横轴关注区间强调,默认无''
587
+ preprocess:数据预处理,默认无'none'
588
+ linewidth:曲线宽度,默认1.5
589
+ scaling_option:数据缩放方法,默认'start'
590
+ plus_sign:在缩放处理时,纵轴刻度是否带加减号,默认否False
591
+ graph:是否绘图,默认是True
592
+ loc:图例位置,默认自动处理'best'
593
+ facecolor:画布背景颜色,默认'whitesmoke'
594
+ annotate:是否标注曲线末端,默认否False
595
+ annotate_value:是否标注曲线末端数值,默认否False
596
+ mark_top:是否标注最大值,默认是True
597
+ mark_bottom:是否标注最小值,默认是True
598
+ mark_end:是否标注曲线末端值,默认否False
599
+ maxticks:设定横轴刻度数量最大值,默认30
600
+
601
+ 注意:
602
+ ticker中须含有2个及以上国家代码,
603
+ indicator为单一指标,
604
+ axhline_label不为空时绘制水平线
605
+
606
+ preprocess:是否对绘图数据进行预处理,仅适用于指标数量级差异较大的数据,
607
+ 不适用于比例、比率和百分比等数量级较为一致的指标。
608
+ standardize: 标准化处理,(x - mean(x))/std(x)
609
+ normalize: 归一化处理,(x - min(x))/(max(x) - min(x))
610
+ logarithm: 对数处理,np.log(x)
611
+ scaling:缩放处理,五种选项scaling_option
612
+ (mean均值,min最小值,start开始值,percentage相对每条曲线起点值的百分比,
613
+ change%相对每条曲线起点值变化的百分比)
614
+ change%方式的图形更接近于持有收益率(Exp Ret%),设为默认的缩放方式。
615
+
616
+ """
617
+ DEBUG=False
618
+
619
+ tickers=ticker; measure=indicator
620
+ start,end=start_end_preprocess(start,end)
621
+
622
+ tickers=upper_ticker(tickers)
623
+ if not isinstance(tickers,list):
624
+ tickers=[tickers]
625
+
626
+ # 去掉重复代码:有必要,重复代码将导致后续处理出错KeyError: 0!
627
+ tickers=list(set(tickers))
628
+
629
+ if isinstance(measure,list):
630
+ measure=measure[0]
631
+
632
+ #屏蔽函数内print信息输出的类
633
+ import os, sys
634
+ class HiddenPrints:
635
+ def __enter__(self):
636
+ self._original_stdout = sys.stdout
637
+ sys.stdout = open(os.devnull, 'w')
638
+
639
+ def __exit__(self, exc_type, exc_val, exc_tb):
640
+ sys.stdout.close()
641
+ sys.stdout = self._original_stdout
642
+
643
+ #循环获取指标
644
+ #import pandas as pd
645
+ #from functools import reduce
646
+
647
+ dfs=pd.DataFrame(); have_data=False
648
+ country_list=[]; unit_list=[]
649
+ for t in tickers:
650
+ print(f" Looking indicator info for {t} ... ...")
651
+ with HiddenPrints():
652
+ df_tmp=economy_indicator_wb(ticker=t,indicator=measure, \
653
+ start=start,end=end,graph=False)
654
+ if df_tmp is None:
655
+ print(f" #Warning(economy_mticker_wb): indicator info not found for {t}")
656
+ continue
657
+ if len(df_tmp)==0:
658
+ print(f" #Warning(economy_mticker_wb): zero info found for {t} between {start} and {end}")
659
+ continue
660
+
661
+ have_data=True
662
+
663
+ country=df_tmp['country'].values[0]
664
+ country_list=country_list+[country]
665
+ unit=df_tmp['unit'].values[0]
666
+ unit_list=unit_list+[unit]
667
+ df_tmp.drop(columns=['country','unit'],inplace=True)
668
+ indicator_name=list(df_tmp)[0]
669
+
670
+ if t in band_area:
671
+ band_area = [country if x == t else x for x in band_area]
672
+
673
+ df_tmp.rename(columns={indicator_name:country},inplace=True)
674
+
675
+ if len(dfs)==0:
676
+ dfs=df_tmp
677
+ else:
678
+ dfs=pd.concat([dfs,df_tmp],axis=1,join='outer')
679
+
680
+ if dfs is None:
681
+ print(f" #Error(economy_mticker_wb): no records found for {measure}")
682
+ return None
683
+ if len(dfs)==0:
684
+ print(" #Error(economy_mticker_wb): zero records found for {measure}")
685
+ return None
686
+
687
+ # 若不绘图则返回原始数据
688
+ if not graph: return dfs
689
+
690
+ if not have_data:
691
+ #print(f" #Error(economy_mticker_wb): no record found on {indicator} for {ticker}")
692
+ return None
693
+
694
+ # 绘图
695
+ titletxt=text_lang("经济趋势分析","Economic Trend Analysis")+': '+indicator_name
696
+ y_label=indicator_name
697
+
698
+ import datetime; todaydt = datetime.date.today()
699
+ footnote2=text_lang("数据来源:世界银行","Data source: World Bank")+', '+str(todaydt)
700
+
701
+ one_unit=False
702
+ if len(set(unit_list)) == 1: one_unit=True
703
+ if one_unit:
704
+ x_label=footnote2
705
+ else:
706
+ footnote1=text_lang('注:','Notes: '); country_list_len=len(country_list)
707
+ for ind in country_list:
708
+ pos=country_list.index(ind)
709
+ footnote1=footnote1+ind+' '+unit_list[pos]
710
+ if (pos < country_list_len - 1):
711
+ if (pos+1) % 2 != 0:
712
+ footnote1=footnote1+', '
713
+ else:
714
+ footnote1=footnote1+'\n'
715
+
716
+ x_label=footnote1+'\n'+footnote2
717
+
718
+ if preprocess == 'scaling' and scaling_option == 'change%':
719
+ title_txt2=text_lang("增减幅度%","Change%")
720
+ titletxt=titletxt+', '+title_txt2
721
+ axhline_value=0
722
+ axhline_label="零线"
723
+ else:
724
+ if one_unit and unit != '':
725
+ titletxt=titletxt+', '+unit
726
+
727
+ # 为避免出错,对空值进行插值
728
+ dfs.interpolate(method='linear',limit_direction='both',inplace=True)
729
+ # 标准化处理
730
+ try:
731
+ dfs2,axhline_label,x_label,y_label,plus_sign=df_preprocess(dfs,measure, \
732
+ axhline_label=axhline_label,x_label=x_label,y_label=y_label, \
733
+ preprocess=preprocess,scaling_option=scaling_option)
734
+ except:
735
+ print(" #Error(economy_mticker_wb): preprocess failed, returning dfs for further check")
736
+ return dfs
737
+
738
+ if DEBUG:
739
+ print("DEBUG: dfs2=",list(dfs2))
740
+
741
+ above_zero=0; below_zero=0
742
+ for c in list(dfs2):
743
+ c_max=dfs2[c].max(); c_min=dfs2[c].min()
744
+ try:
745
+ if c_max>0 or c_min>0: above_zero+=1
746
+ if c_max<0 or c_min<0: below_zero+=1
747
+ except: continue
748
+
749
+ if DEBUG:
750
+ print("DEBUG: above_zero=",above_zero,'below_zero=',below_zero)
751
+
752
+ if above_zero>0 and below_zero>0: #有正有负
753
+ if axhline_label=='':
754
+ axhline_label='零线'
755
+
756
+ draw_lines(dfs2,y_label,x_label,axhline_value,axhline_label,titletxt, \
757
+ data_label=False,resample_freq='D',smooth=smooth,linewidth=linewidth, \
758
+ band_area=band_area,loc=loc, \
759
+ attention_value=attention_value,attention_value_area=attention_value_area, \
760
+ attention_point=attention_point,attention_point_area=attention_point_area, \
761
+ annotate=annotate,annotate_value=annotate_value,plus_sign=plus_sign, \
762
+ mark_top=mark_top,mark_bottom=mark_bottom,mark_end=mark_end,facecolor=facecolor, \
763
+ maxticks=30,translate=translate)
764
+
765
+ return dfs2
766
+
767
+ #==============================================================================
768
+
769
+
770
+ def economy_trend2(ticker='CN',indicator='NY.GDP.MKTP.KN', \
771
+ start='L10Y',end='today',translate=False, \
772
+ attention_value='',attention_value_area='', \
773
+ attention_point='',attention_point_area='', \
774
+ mark_top=False,mark_bottom=False,mark_end=False, \
775
+ graph=True,facecolor='whitesmoke',loc='best',maxticks=30, \
776
+
777
+ zeroline=False,average_value=False, \
778
+ datatag=False,power=0, \
779
+
780
+ annotate=False,annotate_value=False,smooth=False, \
781
+
782
+ band_area='',date_range=False,date_freq=False, \
783
+
784
+ axhline_value=0,axhline_label='', \
785
+ preprocess='none',linewidth=1.5, \
786
+ scaling_option='start', \
787
+ plus_sign=False, \
788
+ ):
789
+ """
790
+ 功能:分析宏观经济指标,支持单国家单指标、多国家单指标、单国家多指标
791
+ 主要公共参数:
792
+ ticker:国家编码,两位,默认'CN'
793
+ indicator:宏观经济指标,默认GDP (constant LCU),即本币不变价格GDP
794
+ start:开始日期,默认近十年
795
+ end:结束日期,默认当前日期
796
+ attention_value:纵轴关注值或其列表,默认无''
797
+ attention_value_area:纵轴关注值区间强调,默认无''
798
+ attention_point:横轴关注值或其列表,默认无''
799
+ attention_point_area:横轴关注值区间强调,默认无''
800
+ graph:是否绘图,默认是True
801
+ mark_top, mark_bottom, mark_end:是否标记最高、最低和末端点:默认是True
802
+ facecolor:背景颜色,默认'whitesmoke'
803
+ loc:图例位置,默认自动'best'
804
+
805
+ 仅支持单国家单指标的参数:
806
+ zeroline:是否绘制零线,默认False
807
+ average_value:是否绘制均值线,默认否False
808
+ datatag:是否标记折线中的各个数据点的数值,默认否False
809
+ power:是否绘制趋势线,默认否0
810
+
811
+ 支持多国家单指标、单国家多指标的参数:
812
+ annotate:是否在曲线末端标注,默认否False
813
+ annotate_value:是否标注曲线末端值,默认否False
814
+
815
+ 仅支持单国家多指标的参数:
816
+ band_area:两条曲线之间强调,默认无''
817
+ date_range:指定开始结束日期绘图,默认False
818
+ date_freq:指定横轴日期间隔,默认False。
819
+ 可指定'D'、'2D'、'W'、'M'等,横轴一般不超过25个标注,否则会重叠
820
+
821
+ 仅支持多国家单指标的参数:
822
+ preprocess:数据预处理,默认无'none'
823
+ scaling_option:数据缩放方法,默认'start',仅当preprocess为非'none'时有效
824
+ plus_sign:在缩放处理时,纵轴刻度是否带加减号,默认否False
825
+ linewidth:曲线宽度,默认1.5
826
+
827
+ 输出:绘图
828
+ 返回值:数据表
829
+
830
+ 其他:套壳函数
831
+ """
832
+ # 判断ticker个数
833
+ ticker_num=0
834
+ if isinstance(ticker,str): ticker_num=1
835
+ if isinstance(ticker,list):
836
+ if len(ticker)==1:
837
+ ticker_num=1
838
+ ticker=ticker[0]
839
+ else:
840
+ ticker_num=len(ticker)
841
+
842
+ # 判断indicator个数
843
+ indicator_num=0
844
+ if isinstance(indicator,str): indicator_num=1
845
+ if isinstance(indicator,list):
846
+ if len(indicator)==1:
847
+ indicator_num=1
848
+ indicator=indicator[0]
849
+ else:
850
+ indicator_num=len(indicator)
851
+
852
+ # 单国家+单指标
853
+ if ticker_num==1 and indicator_num==1:
854
+ df=economy_indicator_wb(ticker=ticker,indicator=indicator, \
855
+ start=start,end=end,translate=translate, \
856
+ attention_value=attention_value, \
857
+ attention_value_area=attention_value_area, \
858
+ attention_point=attention_point, \
859
+ attention_point_area=attention_point_area, \
860
+ mark_top=mark_top,mark_bottom=mark_bottom,mark_end=mark_end, \
861
+ graph=graph,facecolor=facecolor,loc=loc,maxticks=maxticks, \
862
+
863
+ power=power,average_value=average_value, \
864
+ zeroline=zeroline,datatag=datatag)
865
+ return df
866
+
867
+ # 多国家:仅使用第一个指标
868
+ if ticker_num > 1:
869
+ df=economy_mtickers_wb(ticker=ticker,indicator=indicator, \
870
+ start=start,end=end,translate=translate, \
871
+ attention_value=attention_value, \
872
+ attention_value_area=attention_value_area, \
873
+ attention_point=attention_point, \
874
+ attention_point_area=attention_point_area, \
875
+ mark_top=mark_top,mark_bottom=mark_bottom,mark_end=mark_end, \
876
+ graph=graph,loc=loc,facecolor=facecolor,maxticks=maxticks, \
877
+ band_area=band_area, \
878
+ annotate=annotate,annotate_value=annotate_value,smooth=smooth, \
879
+
880
+ preprocess=preprocess,scaling_option=scaling_option, \
881
+ plus_sign=plus_sign,linewidth=linewidth, \
882
+ axhline_value=axhline_value,axhline_label=axhline_label)
883
+ return df
884
+
885
+ # 单国家:使用多个指标
886
+ if ticker_num == 1 and indicator_num > 1:
887
+ df=economy_mindicators_wb(ticker=ticker,indicator=indicator, \
888
+ start=start,end=end,translate=translate, \
889
+ attention_value=attention_value, \
890
+ attention_value_area=attention_value_area, \
891
+ attention_point=attention_point, \
892
+ attention_point_area=attention_point_area, \
893
+ mark_top=mark_top,mark_bottom=mark_bottom,mark_end=mark_end, \
894
+ graph=graph,facecolor=facecolor,loc=loc,maxticks=maxticks, \
895
+
896
+ annotate=annotate,annotate_value=annotate_value,smooth=smooth, \
897
+
898
+ band_area=band_area, \
899
+ date_range=date_range,date_freq=date_freq)
900
+ return df
901
+
902
+ print(" #Warning: need at least 1 country and at leats 1 indicator")
903
+
904
+ return None
905
+
906
+
907
+ #==============================================================================
908
+ #==============================================================================
909
+ #==============================================================================
910
+ #==============================================================================
911
+ #==============================================================================
912
+ #==============================================================================
913
+ #==============================================================================
914
+