siat 3.10.131__py3-none-any.whl → 3.10.132__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.
- build/lib/build/lib/siat/__init__.py +75 -0
- build/lib/build/lib/siat/allin.py +137 -0
- build/lib/build/lib/siat/assets_liquidity.py +915 -0
- build/lib/build/lib/siat/beta_adjustment.py +1058 -0
- build/lib/build/lib/siat/beta_adjustment_china.py +548 -0
- build/lib/build/lib/siat/blockchain.py +143 -0
- build/lib/build/lib/siat/bond.py +2900 -0
- build/lib/build/lib/siat/bond_base.py +992 -0
- build/lib/build/lib/siat/bond_china.py +100 -0
- build/lib/build/lib/siat/bond_zh_sina.py +143 -0
- build/lib/build/lib/siat/capm_beta.py +783 -0
- build/lib/build/lib/siat/capm_beta2.py +887 -0
- build/lib/build/lib/siat/common.py +5360 -0
- build/lib/build/lib/siat/compare_cross.py +642 -0
- build/lib/build/lib/siat/copyrights.py +18 -0
- build/lib/build/lib/siat/cryptocurrency.py +667 -0
- build/lib/build/lib/siat/economy.py +1471 -0
- build/lib/build/lib/siat/economy2.py +1853 -0
- build/lib/build/lib/siat/esg.py +536 -0
- build/lib/build/lib/siat/event_study.py +815 -0
- build/lib/build/lib/siat/fama_french.py +1521 -0
- build/lib/build/lib/siat/fin_stmt2_yahoo.py +982 -0
- build/lib/build/lib/siat/financial_base.py +1160 -0
- build/lib/build/lib/siat/financial_statements.py +598 -0
- build/lib/build/lib/siat/financials.py +2339 -0
- build/lib/build/lib/siat/financials2.py +1278 -0
- build/lib/build/lib/siat/financials_china.py +4433 -0
- build/lib/build/lib/siat/financials_china2.py +2212 -0
- build/lib/build/lib/siat/fund.py +629 -0
- build/lib/build/lib/siat/fund_china.py +3307 -0
- build/lib/build/lib/siat/future_china.py +551 -0
- build/lib/build/lib/siat/google_authenticator.py +47 -0
- build/lib/build/lib/siat/grafix.py +3636 -0
- build/lib/build/lib/siat/holding_risk.py +867 -0
- build/lib/build/lib/siat/luchy_draw.py +638 -0
- build/lib/build/lib/siat/market_china.py +1168 -0
- build/lib/build/lib/siat/markowitz.py +2363 -0
- build/lib/build/lib/siat/markowitz2.py +3150 -0
- build/lib/build/lib/siat/markowitz2_20250704.py +2969 -0
- build/lib/build/lib/siat/markowitz2_20250705.py +3158 -0
- build/lib/build/lib/siat/markowitz_simple.py +373 -0
- build/lib/build/lib/siat/ml_cases.py +2291 -0
- build/lib/build/lib/siat/ml_cases_example.py +60 -0
- build/lib/build/lib/siat/option_china.py +3069 -0
- build/lib/build/lib/siat/option_pricing.py +1925 -0
- build/lib/build/lib/siat/other_indexes.py +409 -0
- build/lib/build/lib/siat/risk_adjusted_return.py +1576 -0
- build/lib/build/lib/siat/risk_adjusted_return2.py +1900 -0
- build/lib/build/lib/siat/risk_evaluation.py +2218 -0
- build/lib/build/lib/siat/risk_free_rate.py +351 -0
- build/lib/build/lib/siat/sector_china.py +4140 -0
- build/lib/build/lib/siat/security_price2.py +727 -0
- build/lib/build/lib/siat/security_prices.py +3408 -0
- build/lib/build/lib/siat/security_trend.py +402 -0
- build/lib/build/lib/siat/security_trend2.py +646 -0
- build/lib/build/lib/siat/stock.py +4284 -0
- build/lib/build/lib/siat/stock_advice_linear.py +934 -0
- build/lib/build/lib/siat/stock_base.py +26 -0
- build/lib/build/lib/siat/stock_china.py +2095 -0
- build/lib/build/lib/siat/stock_prices_kneighbors.py +910 -0
- build/lib/build/lib/siat/stock_prices_linear.py +386 -0
- build/lib/build/lib/siat/stock_profile.py +707 -0
- build/lib/build/lib/siat/stock_technical.py +3305 -0
- build/lib/build/lib/siat/stooq.py +74 -0
- build/lib/build/lib/siat/transaction.py +347 -0
- build/lib/build/lib/siat/translate.py +5183 -0
- build/lib/build/lib/siat/valuation.py +1378 -0
- build/lib/build/lib/siat/valuation_china.py +2076 -0
- build/lib/build/lib/siat/var_model_validation.py +444 -0
- build/lib/build/lib/siat/yf_name.py +811 -0
- build/lib/siat/__init__.py +75 -0
- build/lib/siat/allin.py +137 -0
- build/lib/siat/assets_liquidity.py +915 -0
- build/lib/siat/beta_adjustment.py +1058 -0
- build/lib/siat/beta_adjustment_china.py +548 -0
- build/lib/siat/blockchain.py +143 -0
- build/lib/siat/bond.py +2900 -0
- build/lib/siat/bond_base.py +992 -0
- build/lib/siat/bond_china.py +100 -0
- build/lib/siat/bond_zh_sina.py +143 -0
- build/lib/siat/capm_beta.py +783 -0
- build/lib/siat/capm_beta2.py +887 -0
- build/lib/siat/common.py +5360 -0
- build/lib/siat/compare_cross.py +642 -0
- build/lib/siat/copyrights.py +18 -0
- build/lib/siat/cryptocurrency.py +667 -0
- build/lib/siat/economy.py +1471 -0
- build/lib/siat/economy2.py +1853 -0
- build/lib/siat/esg.py +536 -0
- build/lib/siat/event_study.py +815 -0
- build/lib/siat/fama_french.py +1521 -0
- build/lib/siat/fin_stmt2_yahoo.py +982 -0
- build/lib/siat/financial_base.py +1160 -0
- build/lib/siat/financial_statements.py +598 -0
- build/lib/siat/financials.py +2339 -0
- build/lib/siat/financials2.py +1278 -0
- build/lib/siat/financials_china.py +4433 -0
- build/lib/siat/financials_china2.py +2212 -0
- build/lib/siat/fund.py +629 -0
- build/lib/siat/fund_china.py +3307 -0
- build/lib/siat/future_china.py +551 -0
- build/lib/siat/google_authenticator.py +47 -0
- build/lib/siat/grafix.py +3636 -0
- build/lib/siat/holding_risk.py +867 -0
- build/lib/siat/luchy_draw.py +638 -0
- build/lib/siat/market_china.py +1168 -0
- build/lib/siat/markowitz.py +2363 -0
- build/lib/siat/markowitz2.py +3150 -0
- build/lib/siat/markowitz2_20250704.py +2969 -0
- build/lib/siat/markowitz2_20250705.py +3158 -0
- build/lib/siat/markowitz_simple.py +373 -0
- build/lib/siat/ml_cases.py +2291 -0
- build/lib/siat/ml_cases_example.py +60 -0
- build/lib/siat/option_china.py +3069 -0
- build/lib/siat/option_pricing.py +1925 -0
- build/lib/siat/other_indexes.py +409 -0
- build/lib/siat/risk_adjusted_return.py +1576 -0
- build/lib/siat/risk_adjusted_return2.py +1900 -0
- build/lib/siat/risk_evaluation.py +2218 -0
- build/lib/siat/risk_free_rate.py +351 -0
- build/lib/siat/sector_china.py +4140 -0
- build/lib/siat/security_price2.py +727 -0
- build/lib/siat/security_prices.py +3408 -0
- build/lib/siat/security_trend.py +402 -0
- build/lib/siat/security_trend2.py +646 -0
- build/lib/siat/stock.py +4284 -0
- build/lib/siat/stock_advice_linear.py +934 -0
- build/lib/siat/stock_base.py +26 -0
- build/lib/siat/stock_china.py +2095 -0
- build/lib/siat/stock_prices_kneighbors.py +910 -0
- build/lib/siat/stock_prices_linear.py +386 -0
- build/lib/siat/stock_profile.py +707 -0
- build/lib/siat/stock_technical.py +3305 -0
- build/lib/siat/stooq.py +74 -0
- build/lib/siat/transaction.py +347 -0
- build/lib/siat/translate.py +5183 -0
- build/lib/siat/valuation.py +1378 -0
- build/lib/siat/valuation_china.py +2076 -0
- build/lib/siat/var_model_validation.py +444 -0
- build/lib/siat/yf_name.py +811 -0
- siat/__init__.py +0 -0
- siat/allin.py +0 -0
- siat/assets_liquidity.py +0 -0
- siat/beta_adjustment.py +0 -0
- siat/beta_adjustment_china.py +0 -0
- siat/blockchain.py +0 -0
- siat/bond.py +0 -0
- siat/bond_base.py +0 -0
- siat/bond_china.py +0 -0
- siat/bond_zh_sina.py +0 -0
- siat/capm_beta.py +0 -0
- siat/capm_beta2.py +0 -0
- siat/common.py +136 -3
- siat/compare_cross.py +0 -0
- siat/copyrights.py +0 -0
- siat/cryptocurrency.py +0 -0
- siat/economy.py +0 -0
- siat/economy2.py +0 -0
- siat/esg.py +0 -0
- siat/event_study.py +0 -0
- siat/exchange_bond_china.pickle +0 -0
- siat/fama_french.py +0 -0
- siat/fin_stmt2_yahoo.py +0 -0
- siat/financial_base.py +0 -0
- siat/financial_statements.py +0 -0
- siat/financials.py +0 -0
- siat/financials2.py +0 -0
- siat/financials_china.py +0 -0
- siat/financials_china2.py +0 -0
- siat/fund.py +0 -0
- siat/fund_china.pickle +0 -0
- siat/fund_china.py +0 -0
- siat/future_china.py +0 -0
- siat/google_authenticator.py +0 -0
- siat/grafix.py +1 -1
- siat/holding_risk.py +0 -0
- siat/luchy_draw.py +0 -0
- siat/market_china.py +1 -1
- siat/markowitz.py +0 -0
- siat/markowitz2.py +240 -39
- siat/markowitz2_20250704.py +2969 -0
- siat/markowitz2_20250705.py +3158 -0
- siat/markowitz_simple.py +0 -0
- siat/ml_cases.py +0 -0
- siat/ml_cases_example.py +0 -0
- siat/option_china.py +0 -0
- siat/option_pricing.py +0 -0
- siat/other_indexes.py +0 -0
- siat/risk_adjusted_return.py +0 -0
- siat/risk_adjusted_return2.py +0 -0
- siat/risk_evaluation.py +0 -0
- siat/risk_free_rate.py +0 -0
- siat/sector_china.py +0 -0
- siat/security_price2.py +0 -0
- siat/security_prices.py +3 -1
- siat/security_trend.py +0 -0
- siat/security_trend2.py +1 -1
- siat/stock.py +4 -2
- siat/stock_advice_linear.py +0 -0
- siat/stock_base.py +0 -0
- siat/stock_china.py +0 -0
- siat/stock_info.pickle +0 -0
- siat/stock_prices_kneighbors.py +0 -0
- siat/stock_prices_linear.py +0 -0
- siat/stock_profile.py +0 -0
- siat/stock_technical.py +0 -0
- siat/stooq.py +0 -0
- siat/transaction.py +0 -0
- siat/translate.py +11 -11
- siat/valuation.py +0 -0
- siat/valuation_china.py +0 -0
- siat/var_model_validation.py +0 -0
- siat/yf_name.py +0 -0
- {siat-3.10.131.dist-info → siat-3.10.132.dist-info}/METADATA +235 -227
- siat-3.10.132.dist-info/RECORD +218 -0
- {siat-3.10.131.dist-info → siat-3.10.132.dist-info}/WHEEL +1 -1
- {siat-3.10.131.dist-info → siat-3.10.132.dist-info/licenses}/LICENSE +0 -0
- siat-3.10.132.dist-info/top_level.txt +4 -0
- siat-3.10.131.dist-info/RECORD +0 -76
- siat-3.10.131.dist-info/top_level.txt +0 -1
build/lib/siat/grafix.py
ADDED
@@ -0,0 +1,3636 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
本模块功能:绘制折线图,单线,双线,多线
|
4
|
+
所属工具包:证券投资分析工具SIAT
|
5
|
+
SIAT:Security Investment Analysis Tool
|
6
|
+
创建日期:2020年9月16日
|
7
|
+
最新修订日期:2020年9月16日
|
8
|
+
作者:王德宏 (WANG Dehong, Peter)
|
9
|
+
作者单位:北京外国语大学国际商学院
|
10
|
+
作者邮件:wdehong2000@163.com
|
11
|
+
版权所有:王德宏
|
12
|
+
用途限制:仅限研究与教学使用,不可商用!商用需要额外授权。
|
13
|
+
特别声明:作者不对使用本工具进行证券投资导致的任何损益负责!
|
14
|
+
"""
|
15
|
+
|
16
|
+
#==============================================================================
|
17
|
+
#关闭所有警告
|
18
|
+
import warnings; warnings.filterwarnings('ignore')
|
19
|
+
from siat.common import *
|
20
|
+
from siat.translate import *
|
21
|
+
import pandas as pd
|
22
|
+
#==============================================================================
|
23
|
+
import matplotlib.pyplot as plt
|
24
|
+
import matplotlib.dates as mdate
|
25
|
+
#import matplotlib.font_manager as fm
|
26
|
+
#==============================================================================
|
27
|
+
|
28
|
+
#设置刻度线风格:in,out,inout
|
29
|
+
plt.rcParams['xtick.direction'] = 'inout' # 将x轴的刻度线方向设置向内
|
30
|
+
plt.rcParams['ytick.direction'] = 'inout' # 将y轴的刻度方向设置向内内
|
31
|
+
|
32
|
+
#统一设定绘制的图片大小:数值为英寸,1英寸=100像素
|
33
|
+
#plt.rcParams['figure.figsize']=(12.8,7.2)
|
34
|
+
plt.rcParams['figure.figsize']=(12.8,6.4)
|
35
|
+
plt.rcParams['figure.dpi']=300
|
36
|
+
plt.rcParams['font.size'] = 13
|
37
|
+
plt.rcParams['xtick.labelsize']=11 #横轴字体大小
|
38
|
+
plt.rcParams['ytick.labelsize']=11 #纵轴字体大小
|
39
|
+
|
40
|
+
plt.rcParams['figure.facecolor']='whitesmoke' #背景颜色
|
41
|
+
#plt.rcParams['axes.facecolor']='whitesmoke' #背景颜色
|
42
|
+
#plt.figure(facecolor='whitesmoke')
|
43
|
+
|
44
|
+
title_txt_size=16
|
45
|
+
ylabel_txt_size=12
|
46
|
+
xlabel_txt_size=12
|
47
|
+
legend_txt_size=12
|
48
|
+
annotate_size=11
|
49
|
+
|
50
|
+
if check_language() == "English":
|
51
|
+
title_txt_size=20
|
52
|
+
ylabel_txt_size=16
|
53
|
+
xlabel_txt_size=16
|
54
|
+
legend_txt_size=16
|
55
|
+
annotate_size=13
|
56
|
+
|
57
|
+
#设置绘图风格:网格虚线
|
58
|
+
plt.rcParams['axes.grid']=False
|
59
|
+
#plt.rcParams['grid.color']='steelblue'
|
60
|
+
#plt.rcParams['grid.linestyle']='dashed'
|
61
|
+
#plt.rcParams['grid.linewidth']=0.5
|
62
|
+
|
63
|
+
|
64
|
+
#设置x,y 的主刻度定位器
|
65
|
+
#from matplotlib.pyplot import MultipleLocator
|
66
|
+
|
67
|
+
|
68
|
+
#处理绘图汉字乱码问题
|
69
|
+
import sys; czxt=sys.platform
|
70
|
+
if czxt in ['win32','win64']:
|
71
|
+
#设置中文字体
|
72
|
+
"""
|
73
|
+
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置默认字体
|
74
|
+
mpfrc={'font.family': 'SimHei'}
|
75
|
+
"""
|
76
|
+
plt.rcParams['font.sans-serif'] = ['SimHei'] # 设置默认字体
|
77
|
+
mpfrc={'font.family': 'SimHei'}
|
78
|
+
|
79
|
+
if check_language() == "English":
|
80
|
+
#设置英文字体
|
81
|
+
plt.rcParams['font.sans-serif'] = ['Times New Roman'] # 设置默认字体
|
82
|
+
mpfrc={'font.family': 'Times New Roman'}
|
83
|
+
|
84
|
+
if czxt in ['darwin']: #MacOSX
|
85
|
+
plt.rcParams['font.family']= ['Heiti TC']
|
86
|
+
mpfrc={'font.family': 'Heiti TC'}
|
87
|
+
|
88
|
+
if czxt in ['linux']: #website Jupyter
|
89
|
+
plt.rcParams['font.family']= ['Heiti TC']
|
90
|
+
mpfrc={'font.family':'Heiti TC'}
|
91
|
+
|
92
|
+
# 解决保存图像时'-'显示为方块的问题
|
93
|
+
plt.rcParams['axes.unicode_minus'] = False
|
94
|
+
#==============================================================================
|
95
|
+
if __name__ =="__main__":
|
96
|
+
df0=get_price('000001.SS','2023-1-1','2024-3-22')
|
97
|
+
df0=get_price('sz149995','2020-1-1','2024-3-31')
|
98
|
+
df0,_=get_price_1ticker('sz149976',fromdate='2024-1-1',todate='2024-4-6',fill=True)
|
99
|
+
|
100
|
+
colname='Close'
|
101
|
+
collabel='Close'
|
102
|
+
ylabeltxt='Close'
|
103
|
+
titletxt='Title'
|
104
|
+
footnote='footnote'
|
105
|
+
datatag=False
|
106
|
+
power=0
|
107
|
+
zeroline=False
|
108
|
+
average_value=False
|
109
|
+
resample_freq='D'
|
110
|
+
loc='best'
|
111
|
+
date_range=False
|
112
|
+
date_freq=False
|
113
|
+
date_fmt='%Y-%m-%d'
|
114
|
+
mark_top=True
|
115
|
+
mark_bottom=True
|
116
|
+
|
117
|
+
plot_line(df0,colname,collabel,ylabeltxt,titletxt,footnote,mark_top=True,mark_bottom=True)
|
118
|
+
|
119
|
+
def plot_line(df0,colname,collabel,ylabeltxt,titletxt,footnote,datatag=False, \
|
120
|
+
power=0,zeroline=False, \
|
121
|
+
attention_value='',attention_value_area='', \
|
122
|
+
attention_point='',attention_point_area='', \
|
123
|
+
average_value=False, \
|
124
|
+
|
125
|
+
resample_freq='D',loc='best', \
|
126
|
+
date_range=False,date_freq=False,date_fmt='%Y-%m-%d', \
|
127
|
+
mark_start=False,mark_top=True,mark_bottom=True,mark_end=True, \
|
128
|
+
facecolor='whitesmoke',maxticks=15,translate=False):
|
129
|
+
"""
|
130
|
+
功能:绘制折线图。如果power=0不绘制趋势图,否则绘制多项式趋势图
|
131
|
+
假定:数据表有索引,且已经按照索引排序
|
132
|
+
输入:数据表df,数据表中的列名colname,列名的标签collabel;y轴标签ylabeltxt;
|
133
|
+
标题titletxt,脚注footnote;是否在图中标记数据datatag;趋势图的多项式次数power
|
134
|
+
mark_top,mark_bottom:是否标记最高最低点
|
135
|
+
输出:折线图
|
136
|
+
返回值:无
|
137
|
+
注意1:需要日期类型作为df索引
|
138
|
+
注意2:date_freq不为False时,必须设置date_range=True,否则无法完成日期设置!
|
139
|
+
"""
|
140
|
+
DEBUG=False
|
141
|
+
|
142
|
+
import pandas as pd
|
143
|
+
|
144
|
+
#空值判断
|
145
|
+
if len(df0) ==0:
|
146
|
+
print (" #Warning(plot_line): no data to plot.")
|
147
|
+
return
|
148
|
+
|
149
|
+
#插值平滑:未填充时
|
150
|
+
#if 'filled' not in list(df0):
|
151
|
+
try:
|
152
|
+
df0x=df0[[colname]].astype('float')
|
153
|
+
df=df_smooth_manual(df0x,resample_freq=resample_freq)
|
154
|
+
except:
|
155
|
+
df=df0
|
156
|
+
#else: df=df0
|
157
|
+
|
158
|
+
print('')
|
159
|
+
#先绘制折线图
|
160
|
+
date_start=df.index[0]
|
161
|
+
date_end=df.index[-1]
|
162
|
+
ax=plt.gca()
|
163
|
+
|
164
|
+
if date_range and not date_freq:
|
165
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
166
|
+
plt.xticks(pd.date_range(date_start,date_end))
|
167
|
+
if not date_range and date_freq:
|
168
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
169
|
+
plt.xticks(pd.date_range(freq=date_freq))
|
170
|
+
if date_range and date_freq:
|
171
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
172
|
+
plt.xticks(pd.date_range(date_start,date_end,freq=date_freq))
|
173
|
+
|
174
|
+
if ylabeltxt != '' or ylabeltxt == "stooq_MB":
|
175
|
+
collabel=''
|
176
|
+
if ylabeltxt == "stooq_MB":
|
177
|
+
ylabeltxt=''
|
178
|
+
|
179
|
+
lwadjust=linewidth_adjust(df)
|
180
|
+
|
181
|
+
#if 'filled' not in list(df):
|
182
|
+
if translate:
|
183
|
+
collabel=lang_auto2(collabel)
|
184
|
+
|
185
|
+
if DEBUG: print(f"df.index: {df.index}")
|
186
|
+
plt.plot(df.index,df[colname],'-',label=collabel, \
|
187
|
+
linestyle='-',color='blue', linewidth=lwadjust)
|
188
|
+
"""
|
189
|
+
else:
|
190
|
+
#区分实际有数据部分和填充部分
|
191
|
+
df_raw=df[df['filled'] == False] #原有数据
|
192
|
+
df_filled=df[df['filled'] != False] #填充的数据
|
193
|
+
|
194
|
+
plt.plot(df_filled.index,df_filled[colname],'-',label=collabel, \
|
195
|
+
linestyle=':',color='black', linewidth=lwadjust)
|
196
|
+
|
197
|
+
plt.plot(df_raw.index,df_raw[colname],'-',label=collabel, \
|
198
|
+
linestyle='-',color='blue', linewidth=lwadjust)
|
199
|
+
"""
|
200
|
+
haveLegend=True
|
201
|
+
if collabel == '':
|
202
|
+
haveLegend=False
|
203
|
+
|
204
|
+
#绘制数据标签
|
205
|
+
if datatag:
|
206
|
+
mark_start=False; mark_top=False; mark_bottom=False; mark_end=False
|
207
|
+
for x, y in zip(df.index, df[colname]):
|
208
|
+
plt.text(x,y*1.001,'%.2f' % y,ha='center',va='bottom',color='black')
|
209
|
+
|
210
|
+
#标记最高点/最低点
|
211
|
+
if mark_top or mark_bottom:
|
212
|
+
df_mark=df[[colname]].copy() #避免影响原df
|
213
|
+
df_mark.sort_values(by=colname,ascending=False,inplace=True)
|
214
|
+
|
215
|
+
high_poit=df_mark[colname].head(1).values[0]
|
216
|
+
low_poit=df_mark[colname].tail(1).values[0]
|
217
|
+
high_low=high_poit - low_poit
|
218
|
+
if mark_top:
|
219
|
+
df_mark_top=df_mark[:1]
|
220
|
+
for x, y in zip(df_mark_top.index, df_mark_top[colname]):
|
221
|
+
#plt.text(x,y+0.1,'%.2f' % y,ha='center',va='bottom',color='red')
|
222
|
+
#y1=round(y+high_low*0.01,2)
|
223
|
+
y1=y+high_low*0.01
|
224
|
+
#s='%.0f' if y >= 100 else '%.2f'
|
225
|
+
s='%.1f' if abs(y) >= 100 else '%.2f' if abs(y) >= 1 else '%.4f'
|
226
|
+
plt.text(x,y1,s % y,ha='right',va='bottom',color='red')
|
227
|
+
"""
|
228
|
+
s='%.0f' if y >= 100 else '%.2f'
|
229
|
+
plt.text(x,y,s % y,ha='right',va='bottom',color='red')
|
230
|
+
"""
|
231
|
+
plt.scatter(x,y, color='red',marker='8',s=70)
|
232
|
+
|
233
|
+
if mark_bottom:
|
234
|
+
df_mark_bottom=df_mark[-1:]
|
235
|
+
for x, y in zip(df_mark_bottom.index, df_mark_bottom[colname]):
|
236
|
+
#plt.text(x,y-0.1,'%.2f' % y,ha='center',va='bottom',color='black')
|
237
|
+
#y1=round(y-high_low*0.055,2) #标记位置对应y1的底部
|
238
|
+
y1=y-high_low*0.050 #标记位置对应y1的底部
|
239
|
+
#s='%.0f' if y >= 100 else '%.2f'
|
240
|
+
#s='%.2f' if abs(y) >= 100 else '%.2f' if abs(y) >= 10 else '%.4f'
|
241
|
+
s='%.1f' if abs(y) >= 100 else '%.2f' if abs(y) >= 1 else '%.4f'
|
242
|
+
#plt.text(x,y1,s % y,ha='center',va='bottom',color='seagreen')
|
243
|
+
plt.text(x,y1,s % y,ha='right',va='bottom',color='seagreen')
|
244
|
+
plt.scatter(x,y, color='seagreen',marker='8',s=70)
|
245
|
+
|
246
|
+
#标记曲线开始数值
|
247
|
+
if mark_start:
|
248
|
+
df_start=df.head(1)
|
249
|
+
y_start = df_start[colname].min() # 开始的y坐标
|
250
|
+
x_start = df_start[colname].idxmin() # 开始值的x坐标
|
251
|
+
|
252
|
+
#y1=str(int(y_end)) if y_end >= 100 else str(round(y_end,2))
|
253
|
+
y1=str(round(y_start,1)) if abs(y_start) >= 100 else str(round(y_start,2)) if abs(y_start) >= 1 else str(round(y_start,3))
|
254
|
+
plt.annotate(text=y1,
|
255
|
+
xy=(x_start, y_start),
|
256
|
+
xytext=(x_start, y_start*0.998),fontsize=annotate_size,ha='right',va='center')
|
257
|
+
# 特别注意:这里的left/right与实际图示的方向正好相反!!!
|
258
|
+
|
259
|
+
#标记曲线末端数值
|
260
|
+
if mark_end:
|
261
|
+
df_end=df.tail(1)
|
262
|
+
y_end = df_end[colname].min() # 末端的y坐标
|
263
|
+
x_end = df_end[colname].idxmin() # 末端值的x坐标
|
264
|
+
|
265
|
+
#y1=str(int(y_end)) if y_end >= 100 else str(round(y_end,2))
|
266
|
+
y1=str(round(y_end,1)) if abs(y_end) >= 100 else str(round(y_end,2)) if abs(y_end) >= 1 else str(round(y_end,3))
|
267
|
+
plt.annotate(text=' '+y1,
|
268
|
+
xy=(x_end, y_end),
|
269
|
+
xytext=(x_end, y_end*0.998),fontsize=annotate_size,ha='left',va='center')
|
270
|
+
|
271
|
+
#是否绘制水平线
|
272
|
+
if isinstance(zeroline,bool):#若zeroline为True
|
273
|
+
if zeroline:
|
274
|
+
hline=0
|
275
|
+
#plt.axhline(y=hline,ls=":",c="green",linewidth=2,label="零线")
|
276
|
+
plt.axhline(y=hline,ls=":",c="black",linewidth=2,label='')
|
277
|
+
haveLegend=False
|
278
|
+
else:
|
279
|
+
#不在必要,被attention_value的逻辑替代
|
280
|
+
if isinstance(zeroline,float) or isinstance(zeroline,int):
|
281
|
+
hline=zeroline
|
282
|
+
plt.axhline(y=hline,ls=":",c="darkorange",linewidth=3,label=text_lang("关注值","Attention"))
|
283
|
+
haveLegend=True
|
284
|
+
footnote=footnote + text_lang(",关注值",", Attention ")+str(hline)
|
285
|
+
|
286
|
+
#用于关注值的颜色列表
|
287
|
+
atv_color_list=["lightgray","paleturquoise","wheat","khaki","lightsage"]
|
288
|
+
#用于关注点的颜色列表
|
289
|
+
atp_color_list=["crimson","dodgerblue","magenta","lightseagreen","chocolate"]
|
290
|
+
|
291
|
+
if not attention_value=='':
|
292
|
+
if isinstance(attention_value,int) or isinstance(attention_value,float):
|
293
|
+
atv_list=[attention_value]
|
294
|
+
elif isinstance(attention_value,list):
|
295
|
+
atv_list=attention_value
|
296
|
+
else:
|
297
|
+
atv_list=[]
|
298
|
+
if not atv_list==[] and not atv_list==['']:
|
299
|
+
for at in atv_list:
|
300
|
+
pos=atv_list.index(at)
|
301
|
+
color=atv_color_list[pos]
|
302
|
+
plt.axhline(y=at,ls=":",c=color,linewidth=2,label=text_lang("关注值","Attention value ")+str(at))
|
303
|
+
|
304
|
+
if not attention_value_area=='':
|
305
|
+
if isinstance(attention_value_area,list) and len(attention_value_area)>=2:
|
306
|
+
plt.fill_between(df.index,attention_value_area[0],attention_value_area[1],color='lightgray',alpha=0.5)
|
307
|
+
|
308
|
+
import pandas as pd
|
309
|
+
from datetime import datetime; date_format="%Y-%m-%d"
|
310
|
+
if not attention_point=='':
|
311
|
+
if isinstance(attention_point,str) or isinstance(attention_point,int) or isinstance(attention_point,float):
|
312
|
+
atp_list=[attention_point]
|
313
|
+
elif isinstance(attention_point,list):
|
314
|
+
atp_list=attention_point
|
315
|
+
else:
|
316
|
+
atp_list=[]
|
317
|
+
#去重,不打乱原来的顺序
|
318
|
+
atp_list=list(dict.fromkeys(atp_list))
|
319
|
+
|
320
|
+
if not atp_list==[] and not atp_list==['']:
|
321
|
+
|
322
|
+
for at in atp_list:
|
323
|
+
pos=atp_list.index(at)
|
324
|
+
color=atp_color_list[pos]
|
325
|
+
|
326
|
+
#判断是否日期字符串
|
327
|
+
try:
|
328
|
+
#at=datetime.strptime(at, date_format)
|
329
|
+
atpd=pd.to_datetime(at)
|
330
|
+
except:
|
331
|
+
atpd=at
|
332
|
+
|
333
|
+
if DEBUG: print(f"atpd={atpd}")
|
334
|
+
|
335
|
+
try:
|
336
|
+
at_str=atpd.strftime('%Y-%m-%d')
|
337
|
+
except:
|
338
|
+
at_str=atpd
|
339
|
+
#plt.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+str(at))
|
340
|
+
plt.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+at_str)
|
341
|
+
|
342
|
+
if not attention_point_area=='':
|
343
|
+
if isinstance(attention_point_area,list) and len(attention_point_area)>=2:
|
344
|
+
apa_list=[]
|
345
|
+
for ap in attention_point_area:
|
346
|
+
try:
|
347
|
+
#ap=datetime.strptime(ap, date_format)
|
348
|
+
appd=pd.to_datetime(ap)
|
349
|
+
except:
|
350
|
+
appd=ap
|
351
|
+
apa_list=apa_list+[appd]
|
352
|
+
|
353
|
+
yaxis_data=plt.ylim()
|
354
|
+
|
355
|
+
if DEBUG:
|
356
|
+
print(f"yaxis_data={yaxis_data}")
|
357
|
+
print(f"apa_list[0]={apa_list[0]}")
|
358
|
+
print(f"apa_list[1]={apa_list[1]}")
|
359
|
+
plt.fill_betweenx(yaxis_data,apa_list[0],apa_list[1],color='powderblue',alpha=0.5)
|
360
|
+
|
361
|
+
if average_value:
|
362
|
+
haveLegend=True
|
363
|
+
|
364
|
+
av=df[colname].mean()
|
365
|
+
#av=str(round(av,2)) if av < 100 else str(int(av))
|
366
|
+
#av=str(int(av)) if abs(av) >= 100 else str(round(av,2)) if abs(av) >= 10 else str(round(av,3))
|
367
|
+
avstr=str(int(av)) if abs(av) >= 100 else str(round(av,2)) if abs(av) >= 10 else str(round(av,3))
|
368
|
+
plt.axhline(y=av,ls="dashed",c="blueviolet",linewidth=2,label=text_lang("本期间均值","Periodic mean ")+avstr)
|
369
|
+
#footnote=footnote + ",均值"+av
|
370
|
+
#footnote=text_lang("注:期间均值","Note: Periodic mean ")+av+"; "+footnote
|
371
|
+
|
372
|
+
#绘制趋势线
|
373
|
+
#print("--Debug(plot_line): power=",power)
|
374
|
+
if power > 0:
|
375
|
+
trend_txt=text_lang('趋势线','Trend line')
|
376
|
+
|
377
|
+
try:
|
378
|
+
#生成行号,借此将横轴的日期数量化,以便拟合
|
379
|
+
df['id']=range(len(df))
|
380
|
+
|
381
|
+
#设定多项式拟合,power为多项式次数
|
382
|
+
import numpy as np
|
383
|
+
parameter = np.polyfit(df.id, df[colname], power)
|
384
|
+
f = np.poly1d(parameter)
|
385
|
+
plt.plot(df.index, f(df.id),"r--", label=trend_txt,linewidth=1)
|
386
|
+
haveLegend=True
|
387
|
+
except:
|
388
|
+
print(" #Warning(plot_line): failed to converge trend line, try a smaller power.")
|
389
|
+
|
390
|
+
if ylabeltxt != '' or average_value or isinstance(zeroline,bool):
|
391
|
+
if haveLegend:
|
392
|
+
plt.legend(loc=loc,fontsize=legend_txt_size)
|
393
|
+
|
394
|
+
# 使用 AutoDateLocator 自动选择最佳间隔,目的是显示最后一个日期,亲测有效!!!
|
395
|
+
import matplotlib.dates as mdates
|
396
|
+
ax.xaxis.set_major_locator(mdates.AutoDateLocator(maxticks=maxticks))
|
397
|
+
|
398
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
399
|
+
try:
|
400
|
+
plt.gca().set_facecolor(facecolor) #设置画布背景颜色
|
401
|
+
except:
|
402
|
+
print(" #Warning(plot_line): color",facecolor,"is unsupported, changed to default setting")
|
403
|
+
plt.gca().set_facecolor("whitesmoke")
|
404
|
+
|
405
|
+
if '基金' in titletxt and '收盘价' in ylabeltxt and not ('基金指数' in titletxt):
|
406
|
+
ylabeltxt=ylabeltxt.replace('收盘价','单位净值')
|
407
|
+
|
408
|
+
if translate:
|
409
|
+
ylabeltxt=lang_auto2(ylabeltxt)
|
410
|
+
footnote=lang_auto2(footnote)
|
411
|
+
titletxt=lang_auto2(titletxt)
|
412
|
+
|
413
|
+
plt.ylabel(ylabeltxt,fontsize=ylabel_txt_size)
|
414
|
+
plt.xlabel(footnote,fontsize=xlabel_txt_size,ha='center')
|
415
|
+
plt.title(titletxt,fontweight='bold',fontsize=title_txt_size)
|
416
|
+
|
417
|
+
if haveLegend:
|
418
|
+
plt.legend(loc=loc,fontsize=legend_txt_size)
|
419
|
+
|
420
|
+
plt.show()
|
421
|
+
plt.close()
|
422
|
+
|
423
|
+
return
|
424
|
+
|
425
|
+
|
426
|
+
#==============================================================================
|
427
|
+
if __name__ =="__main__":
|
428
|
+
df1=df2=option_comm_china(symbol='黄金期权',contract='au2508',printout=False,graph=False)
|
429
|
+
|
430
|
+
ticker1='看涨期权'; ticker2='看跌期权'
|
431
|
+
colname1='最新价C'; colname2='最新价P'
|
432
|
+
label1=label2=ylabeltxt='价格'
|
433
|
+
twinx=True
|
434
|
+
|
435
|
+
power=0
|
436
|
+
datatag1=False
|
437
|
+
datatag2=False
|
438
|
+
yscalemax=5
|
439
|
+
zeroline=False
|
440
|
+
twinx=False
|
441
|
+
yline=999
|
442
|
+
xline=999
|
443
|
+
resample_freq='D'
|
444
|
+
|
445
|
+
attention_value_area=''; attention_point_area=''
|
446
|
+
loc1='best'; loc2='best'
|
447
|
+
color1='red'; color2='blue'; facecolor='whitesmoke'; maxticks=20
|
448
|
+
|
449
|
+
def plot_line2(df1,ticker1,colname1,label1, \
|
450
|
+
df2,ticker2,colname2,label2, \
|
451
|
+
ylabeltxt,titletxt,footnote, \
|
452
|
+
power=0,datatag1=False,datatag2=False,yscalemax=5, \
|
453
|
+
zeroline=False, \
|
454
|
+
twinx=False, \
|
455
|
+
yline=999,attention_value_area='', \
|
456
|
+
xline=999,attention_point_area='', \
|
457
|
+
resample_freq='D',loc1='best',loc2='best', \
|
458
|
+
color1='red',color2='blue',facecolor='whitesmoke', \
|
459
|
+
maxticks=20):
|
460
|
+
"""
|
461
|
+
功能:绘制两个证券的折线图。如果power=0不绘制趋势图,否则绘制多项式趋势图
|
462
|
+
假定:数据表有索引,且已经按照索引排序
|
463
|
+
输入:
|
464
|
+
证券1:数据表df1,证券代码ticker1,列名1,列名标签1;
|
465
|
+
证券2:数据表df2,证券代码ticker2,列名2,列名标签2;
|
466
|
+
标题titletxt,脚注footnote;是否在图中标记数据datatag;趋势图的多项式次数power
|
467
|
+
输出:默认绘制同轴折线图,若twinx=True则绘制双轴折线图
|
468
|
+
返回值:无
|
469
|
+
注意:需要日期类型作为df索引
|
470
|
+
"""
|
471
|
+
DEBUG=False
|
472
|
+
|
473
|
+
#空值判断
|
474
|
+
if len(df1) ==0:
|
475
|
+
print (" #Warning(plot_line2): no data to plot df1.")
|
476
|
+
if len(df2) ==0:
|
477
|
+
print (" #Warning(plot_line2): no data to plot df2.")
|
478
|
+
if (len(df1) ==0) and (len(df2) ==0):
|
479
|
+
pass
|
480
|
+
return
|
481
|
+
|
482
|
+
if DEBUG:
|
483
|
+
print("In plot_line2")
|
484
|
+
print("Going to plot_line2_coaxial")
|
485
|
+
print("yline=",yline,"; xline=",xline)
|
486
|
+
|
487
|
+
#if not twinx:
|
488
|
+
if twinx == True: # 双轴会图
|
489
|
+
plot_line2_twinx(df1,ticker1,colname1,label1, \
|
490
|
+
df2,ticker2,colname2,label2, \
|
491
|
+
titletxt,footnote,power,datatag1,datatag2, \
|
492
|
+
resample_freq=resample_freq, \
|
493
|
+
xline=xline,attention_point_area=attention_point_area, \
|
494
|
+
loc1=loc1,loc2=loc2, \
|
495
|
+
color1=color1,color2=color2,facecolor=facecolor, \
|
496
|
+
maxticks=maxticks)
|
497
|
+
elif twinx == False: # twinx == False # 正常绘图
|
498
|
+
plot_line2_coaxial(df1,ticker1,colname1,label1, \
|
499
|
+
df2,ticker2,colname2,label2, \
|
500
|
+
ylabeltxt,titletxt,footnote,power,datatag1,datatag2,zeroline, \
|
501
|
+
yline=yline,attention_value_area=attention_value_area, \
|
502
|
+
xline=xline,attention_point_area=attention_point_area, \
|
503
|
+
resample_freq=resample_freq, \
|
504
|
+
loc1=loc1,loc2=loc2, \
|
505
|
+
color1=color1,color2=color2,facecolor=facecolor, \
|
506
|
+
maxticks=maxticks)
|
507
|
+
|
508
|
+
elif 'LR' in twinx.upper(): # 左右双图
|
509
|
+
plot_line2_LR(df1,ticker1,colname1,label1, \
|
510
|
+
df2,ticker2,colname2,label2, \
|
511
|
+
titletxt,footnote,power,datatag1,datatag2, \
|
512
|
+
resample_freq=resample_freq, \
|
513
|
+
xline=xline,attention_point_area=attention_point_area, \
|
514
|
+
loc1=loc1,loc2=loc2, \
|
515
|
+
color1=color1,color2=color2,facecolor=facecolor, \
|
516
|
+
maxticks=maxticks)
|
517
|
+
elif 'UD' in twinx.upper(): # 上下双图
|
518
|
+
plot_line2_UD(df1,ticker1,colname1,label1, \
|
519
|
+
df2,ticker2,colname2,label2, \
|
520
|
+
titletxt,footnote,power,datatag1,datatag2, \
|
521
|
+
resample_freq=resample_freq, \
|
522
|
+
xline=xline,attention_point_area=attention_point_area, \
|
523
|
+
loc1=loc1,loc2=loc2, \
|
524
|
+
color1=color1,color2=color2,facecolor=facecolor, \
|
525
|
+
maxticks=maxticks)
|
526
|
+
|
527
|
+
else: # twinx == False # 正常绘图
|
528
|
+
plot_line2_coaxial(df1,ticker1,colname1,label1, \
|
529
|
+
df2,ticker2,colname2,label2, \
|
530
|
+
ylabeltxt,titletxt,footnote,power,datatag1,datatag2,zeroline, \
|
531
|
+
yline=yline,attention_value_area=attention_value_area, \
|
532
|
+
xline=xline,attention_point_area=attention_point_area, \
|
533
|
+
resample_freq=resample_freq, \
|
534
|
+
loc1=loc1,loc2=loc2, \
|
535
|
+
color1=color1,color2=color2,facecolor=facecolor, \
|
536
|
+
maxticks=maxticks)
|
537
|
+
|
538
|
+
return
|
539
|
+
|
540
|
+
|
541
|
+
#==============================================================================
|
542
|
+
def plot2_line2(df1,ticker1,colname1,label1, \
|
543
|
+
df2,ticker2,colname2,label2, \
|
544
|
+
ylabeltxt,titletxt,footnote, \
|
545
|
+
power=0,datatag1=False,datatag2=False,yscalemax=5, \
|
546
|
+
zeroline=False,twinx=False, \
|
547
|
+
yline=999,attention_value_area='', \
|
548
|
+
xline=999,attention_point_area='', \
|
549
|
+
resample_freq='D',loc1='best',loc2='best', \
|
550
|
+
date_range=False,date_freq=False,date_fmt='%Y-%m-%d', \
|
551
|
+
color1='red',color2='blue',facecolor='whitesmoke', \
|
552
|
+
maxticks=20):
|
553
|
+
"""
|
554
|
+
注意:可能有bug,twinx=True时左纵坐标轴和横坐标轴标记可能发生重叠!!!暂不建议使用
|
555
|
+
facecolor不起作用
|
556
|
+
目前的解决方案:改用函数plot_line2
|
557
|
+
|
558
|
+
功能:绘制两个证券的折线图。如果power=0不绘制趋势图,否则绘制多项式趋势图
|
559
|
+
假定:数据表有索引,且已经按照索引排序
|
560
|
+
输入:
|
561
|
+
证券1:数据表df1,证券代码ticker1,列名1,列名标签1;
|
562
|
+
证券2:数据表df2,证券代码ticker2,列名2,列名标签2;
|
563
|
+
标题titletxt,脚注footnote;是否在图中标记数据datatag;趋势图的多项式次数power
|
564
|
+
输出:默认绘制同轴折线图,若twinx=True则绘制双轴折线图
|
565
|
+
返回值:无
|
566
|
+
注意:需要日期类型作为df索引
|
567
|
+
|
568
|
+
date_range:表示绘图横轴是否需要尽量绘制开始和结束日期
|
569
|
+
date_freq:定义绘图横轴的日期间隔,False表示自动间隔,'1Y'表示以1年为单位间隔,
|
570
|
+
'1M'表示间隔一个月,'3M'表示间隔3个月等
|
571
|
+
date_fmt:定义绘图横轴日期的格式,'%Y-%m-%d'表示YYYY-mm-dd,'%Y-%m'表示YYYY-mm,
|
572
|
+
'%Y'表示YYYY
|
573
|
+
"""
|
574
|
+
#空值判断
|
575
|
+
if len(df1) ==0:
|
576
|
+
print (" #Warning(plot2_line2): no data to plot df1.")
|
577
|
+
if len(df2) ==0:
|
578
|
+
print (" #Warning(plot2_line2): no data to plot df2.")
|
579
|
+
if (len(df1) ==0) and (len(df2) ==0):
|
580
|
+
return
|
581
|
+
|
582
|
+
if not twinx:
|
583
|
+
plot_line2_coaxial2(df1,ticker1,colname1,label1, \
|
584
|
+
df2,ticker2,colname2,label2, \
|
585
|
+
ylabeltxt,titletxt,footnote,power,datatag1,datatag2,zeroline, \
|
586
|
+
yline=yline,attention_value_area=attention_value_area, \
|
587
|
+
xline=xline,attention_point_area=attention_point_area, \
|
588
|
+
resample_freq=resample_freq, \
|
589
|
+
loc1=loc1,loc2=loc2, \
|
590
|
+
date_range=date_range,date_freq=date_freq,date_fmt=date_fmt, \
|
591
|
+
color1=color1,color2=color2,facecolor=facecolor, \
|
592
|
+
maxticks=maxticks)
|
593
|
+
else:
|
594
|
+
plot_line2_twinx2(df1,ticker1,colname1,label1, \
|
595
|
+
df2,ticker2,colname2,label2, \
|
596
|
+
titletxt,footnote,power,datatag1,datatag2, \
|
597
|
+
xline,attention_point_area=attention_point_area, \
|
598
|
+
resample_freq=resample_freq, \
|
599
|
+
loc1=loc1,loc2=loc2, \
|
600
|
+
date_range=date_range,date_freq=date_freq,date_fmt=date_fmt, \
|
601
|
+
color1=color1,color2=color2,facecolor=facecolor, \
|
602
|
+
maxticks=maxticks)
|
603
|
+
|
604
|
+
return
|
605
|
+
|
606
|
+
|
607
|
+
#==============================================================================
|
608
|
+
|
609
|
+
|
610
|
+
def plot_line2_coaxial(df01,ticker1,colname1,label1, \
|
611
|
+
df02,ticker2,colname2,label2, \
|
612
|
+
ylabeltxt,titletxt,footnote, \
|
613
|
+
power=0,datatag1=False,datatag2=False,zeroline=False, \
|
614
|
+
yline=999,attention_value_area='', \
|
615
|
+
xline=999,attention_point_area='', \
|
616
|
+
resample_freq='D', \
|
617
|
+
loc1='best',loc2='best', \
|
618
|
+
color1='red',color2='blue',facecolor='whitesmoke', \
|
619
|
+
ticker_type='auto',maxticks=15):
|
620
|
+
"""
|
621
|
+
功能:绘制两个证券的折线图。如果power=0不绘制趋势图,否则绘制多项式趋势图
|
622
|
+
假定:数据表有索引,且已经按照索引排序
|
623
|
+
输入:
|
624
|
+
证券1:数据表df1,证券代码ticker1,列名1,列名标签1;
|
625
|
+
证券2:数据表df2,证券代码ticker2,列名2,列名标签2;
|
626
|
+
标题titletxt,脚注footnote;是否在图中标记数据datatag;趋势图的多项式次数power
|
627
|
+
输出:绘制同轴折线图
|
628
|
+
返回值:无
|
629
|
+
注意:需要日期类型作为df索引
|
630
|
+
"""
|
631
|
+
DEBUG=False
|
632
|
+
|
633
|
+
#插值平滑:如果横轴不为日期型时不可平滑,否则数据会丢失!
|
634
|
+
if not isinstance(maxticks,bool):
|
635
|
+
try:
|
636
|
+
df01x=df01[[colname1]].astype('float')
|
637
|
+
df1=df_smooth_manual(df01x,resample_freq=resample_freq)
|
638
|
+
except:
|
639
|
+
df1=df01
|
640
|
+
try:
|
641
|
+
df02x=df02[[colname2]].astype('float')
|
642
|
+
df2=df_smooth_manual(df02x,resample_freq=resample_freq)
|
643
|
+
except:
|
644
|
+
df2=df02
|
645
|
+
else:
|
646
|
+
df1=df01; df2=df02
|
647
|
+
|
648
|
+
#预处理ticker_type
|
649
|
+
ticker_type_list=ticker_type_preprocess_mticker_mixed([ticker1,ticker2],ticker_type)
|
650
|
+
|
651
|
+
#证券1:先绘制折线图
|
652
|
+
|
653
|
+
if ticker1 == '':
|
654
|
+
label1txt=label1
|
655
|
+
else:
|
656
|
+
if label1 == '':
|
657
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])
|
658
|
+
else:
|
659
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])+'('+label1+')'
|
660
|
+
|
661
|
+
lwadjust=linewidth_adjust(df1)
|
662
|
+
plt.plot(df1.index,df1[colname1],'-',label=label1txt, \
|
663
|
+
linestyle='-',linewidth=lwadjust,color=color1)
|
664
|
+
|
665
|
+
#证券1:绘制数据标签
|
666
|
+
if datatag1:
|
667
|
+
for x, y in zip(df1.index, df1[colname1]):
|
668
|
+
plt.text(x,y+0.1,'%.2f' % y,ha='center',va='bottom',color='black')
|
669
|
+
|
670
|
+
#是否绘制水平0线
|
671
|
+
df_max=max([df1[colname1].max(),df2[colname2].max()])
|
672
|
+
df_min=min([df1[colname1].min(),df2[colname2].min()])
|
673
|
+
if df_max * df_min >=0: #同正同负
|
674
|
+
zeroline=False
|
675
|
+
else:
|
676
|
+
zeroline=True
|
677
|
+
|
678
|
+
#if zeroline and ((min(df1[colname1]) < 0) or (min(df2[colname2]) < 0)):
|
679
|
+
if zeroline:
|
680
|
+
plt.axhline(y=0,ls=":",c="black",linewidth=2)
|
681
|
+
|
682
|
+
if DEBUG:
|
683
|
+
print("In plot_line2_coaxial:")
|
684
|
+
print("yline=",yline,"; xline=",xline)
|
685
|
+
|
686
|
+
#是否绘制水平线
|
687
|
+
if yline != 999:
|
688
|
+
attention_value=yline
|
689
|
+
|
690
|
+
#用于关注值的颜色列表
|
691
|
+
atv_color_list=["lightgray","paleturquoise","wheat","khaki","lightsage"]
|
692
|
+
|
693
|
+
if isinstance(attention_value,int) or isinstance(attention_value,float):
|
694
|
+
atv_list=[attention_value]
|
695
|
+
elif isinstance(attention_value,list):
|
696
|
+
atv_list=attention_value
|
697
|
+
else:
|
698
|
+
atv_list=[]
|
699
|
+
|
700
|
+
if DEBUG:
|
701
|
+
print("atv_list=",atv_list)
|
702
|
+
|
703
|
+
if not atv_list==[] and not atv_list==['']:
|
704
|
+
for at in atv_list:
|
705
|
+
pos=atv_list.index(at)
|
706
|
+
color=atv_color_list[pos]
|
707
|
+
plt.axhline(y=at,ls=":",c=color,linewidth=2,label=text_lang("关注值","Attention value ")+str(at))
|
708
|
+
|
709
|
+
if not attention_value_area=='':
|
710
|
+
if isinstance(attention_value_area,list) and len(attention_value_area)>=2:
|
711
|
+
plt.fill_between(df1.index,attention_value_area[0],attention_value_area[1],color='lightgray',alpha=0.5)
|
712
|
+
|
713
|
+
#是否绘制垂直线
|
714
|
+
import pandas as pd
|
715
|
+
from datetime import datetime; date_format="%Y-%m-%d"
|
716
|
+
if xline != 999:
|
717
|
+
attention_point=xline
|
718
|
+
#用于关注点的颜色列表
|
719
|
+
atp_color_list=["crimson","dodgerblue","magenta","lightseagreen","chocolate"]
|
720
|
+
|
721
|
+
if isinstance(attention_point,str) or isinstance(attention_point,int) or isinstance(attention_point,float):
|
722
|
+
atp_list=[attention_point]
|
723
|
+
elif isinstance(attention_point,list):
|
724
|
+
atp_list=attention_point
|
725
|
+
else:
|
726
|
+
atp_list=[]
|
727
|
+
|
728
|
+
#去重,不打乱原来的顺序
|
729
|
+
atp_list=list(dict.fromkeys(atp_list))
|
730
|
+
|
731
|
+
if not atp_list==[] and not atp_list==['']:
|
732
|
+
|
733
|
+
for at in atp_list:
|
734
|
+
pos=atp_list.index(at)
|
735
|
+
color=atp_color_list[pos]
|
736
|
+
|
737
|
+
#判断是否日期字符串
|
738
|
+
try:
|
739
|
+
at=datetime.strptime(at, date_format)
|
740
|
+
atpd=pd.to_datetime(at)
|
741
|
+
except:
|
742
|
+
atpd=at
|
743
|
+
|
744
|
+
if DEBUG:
|
745
|
+
print("atpd=",atpd)
|
746
|
+
|
747
|
+
try:
|
748
|
+
at_str=atpd.strftime('%Y-%m-%d')
|
749
|
+
except:
|
750
|
+
at_str=atpd
|
751
|
+
plt.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+at_str)
|
752
|
+
|
753
|
+
if not attention_point_area=='':
|
754
|
+
if isinstance(attention_point_area,list) and len(attention_point_area)>=2:
|
755
|
+
apa_list=[]
|
756
|
+
for ap in attention_point_area:
|
757
|
+
try:
|
758
|
+
ap=datetime.strptime(ap, date_format)
|
759
|
+
appd=pd.to_datetime(ap)
|
760
|
+
except:
|
761
|
+
appd=ap
|
762
|
+
apa_list=apa_list+[appd]
|
763
|
+
|
764
|
+
yaxis_data=plt.ylim()
|
765
|
+
plt.fill_betweenx(yaxis_data,apa_list[0],apa_list[1],color='powderblue',alpha=0.5)
|
766
|
+
|
767
|
+
#绘证券1:制趋势线
|
768
|
+
if power > 0:
|
769
|
+
trend_txt=text_lang('趋势线','Trend line')
|
770
|
+
|
771
|
+
try:
|
772
|
+
#生成行号,借此将横轴的日期数量化,以便拟合
|
773
|
+
df1['id']=range(len(df1))
|
774
|
+
|
775
|
+
#设定多项式拟合,power为多项式次数
|
776
|
+
import numpy as np
|
777
|
+
parameter = np.polyfit(df1.id, df1[colname1], power)
|
778
|
+
f = np.poly1d(parameter)
|
779
|
+
|
780
|
+
if ticker1 == '':
|
781
|
+
label1txt=''
|
782
|
+
else:
|
783
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])+"("+trend_txt+")"
|
784
|
+
|
785
|
+
#plt.plot(df1.index, f(df1.id),"g--", label=label1txt,linewidth=1)
|
786
|
+
plt.plot(df1.index, f(df1.id),"g--", label='',linewidth=1)
|
787
|
+
except: pass
|
788
|
+
|
789
|
+
#证券2:先绘制折线图
|
790
|
+
if ticker2 == '':
|
791
|
+
label2txt=label2
|
792
|
+
else:
|
793
|
+
if label2 == '':
|
794
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])
|
795
|
+
else:
|
796
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])+'('+label2+')'
|
797
|
+
|
798
|
+
lwadjust=linewidth_adjust(df2)
|
799
|
+
plt.plot(df2.index,df2[colname2],'-',label=label2txt, \
|
800
|
+
linestyle='-.',linewidth=lwadjust,color=color2)
|
801
|
+
#证券2:绘制数据标签
|
802
|
+
if datatag2:
|
803
|
+
for x, y in zip(df2.index, df2[colname2]):
|
804
|
+
plt.text(x,y+0.1,'%.2f' % y,ha='center',va='bottom',color='black')
|
805
|
+
|
806
|
+
#绘证券2:制趋势线
|
807
|
+
if power > 0:
|
808
|
+
lang=check_language()
|
809
|
+
trend_txt='趋势线'
|
810
|
+
if lang == 'English':
|
811
|
+
trend_txt='Trend line'
|
812
|
+
|
813
|
+
try:
|
814
|
+
#生成行号,借此将横轴的日期数量化,以便拟合
|
815
|
+
df2['id']=range(len(df2))
|
816
|
+
|
817
|
+
#设定多项式拟合,power为多项式次数
|
818
|
+
import numpy as np
|
819
|
+
parameter = np.polyfit(df2.id, df2[colname2], power)
|
820
|
+
f = np.poly1d(parameter)
|
821
|
+
|
822
|
+
if ticker2 == '':
|
823
|
+
label2txt=''
|
824
|
+
else:
|
825
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])+"("+trend_txt+")"
|
826
|
+
|
827
|
+
#plt.plot(df2.index, f(df2.id),"r--", label=label2txt,linewidth=1)
|
828
|
+
plt.plot(df2.index, f(df2.id),"r--", label='',linewidth=1)
|
829
|
+
except: pass
|
830
|
+
|
831
|
+
# 同轴绘图时,loc2未用上!
|
832
|
+
plt.legend(loc=loc1,fontsize=legend_txt_size)
|
833
|
+
|
834
|
+
# 使用 AutoDateLocator 自动选择最佳间隔,目的是显示最后一个日期,亲测有效!!!
|
835
|
+
if not isinstance(maxticks,bool):
|
836
|
+
import matplotlib.dates as mdates
|
837
|
+
try:
|
838
|
+
ax=plt.gca()
|
839
|
+
ax.xaxis.set_major_locator(mdates.AutoDateLocator(maxticks=maxticks))
|
840
|
+
except:
|
841
|
+
pass
|
842
|
+
|
843
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
844
|
+
|
845
|
+
try:
|
846
|
+
plt.gca().set_facecolor(facecolor)
|
847
|
+
except:
|
848
|
+
print(" #Warning(plot_line2_coaxial): color",facecolor,"is unsupported, changed to default setting")
|
849
|
+
plt.gca().set_facecolor("whitesmoke")
|
850
|
+
|
851
|
+
plt.ylabel(ylabeltxt,fontsize=ylabel_txt_size)
|
852
|
+
plt.xlabel(footnote,fontsize=xlabel_txt_size,ha='center')
|
853
|
+
plt.title(titletxt,fontweight='bold',fontsize=title_txt_size)
|
854
|
+
plt.show()
|
855
|
+
plt.close()
|
856
|
+
|
857
|
+
return
|
858
|
+
|
859
|
+
if __name__ =="__main__":
|
860
|
+
df1 = get_price('000002.SZ', '2020-1-1', '2020-3-16')
|
861
|
+
df2 = get_price('600266.SS', '2020-1-1', '2020-3-16')
|
862
|
+
ticker1='000002.SZ'; ticker2='600266.SS'
|
863
|
+
colname1='Close'; colname2='Close'
|
864
|
+
label1="收盘价"; label2="收盘价"
|
865
|
+
ylabeltxt="价格"
|
866
|
+
plot_line2_coaxial(df1,'000002.SZ','High','最高价', \
|
867
|
+
df1,'000002.SZ','Low','最低价',"价格", \
|
868
|
+
"证券价格走势对比图","数据来源:新浪/stooq")
|
869
|
+
plot_line2_coaxial(df1,'000002.SZ','Open','开盘价', \
|
870
|
+
df1,'000002.SZ','Close','收盘价',"价格", \
|
871
|
+
"证券价格走势对比图","数据来源:新浪/stooq")
|
872
|
+
|
873
|
+
plot_line2_coaxial(df2,'600266.SS','Open','开盘价', \
|
874
|
+
df2,'600266.SS','Close','收盘价',"价格", \
|
875
|
+
"证券价格走势对比图","数据来源:新浪/stooq")
|
876
|
+
|
877
|
+
#==============================================================================
|
878
|
+
def plot_line2_coaxial2(df01,ticker1,colname1,label1, \
|
879
|
+
df02,ticker2,colname2,label2, \
|
880
|
+
ylabeltxt,titletxt,footnote, \
|
881
|
+
power=0,datatag1=False,datatag2=False,zeroline=False, \
|
882
|
+
yline=999,attention_value_area='', \
|
883
|
+
xline=999,attention_point_area='', \
|
884
|
+
resample_freq='D', \
|
885
|
+
loc1='best',loc2='best', \
|
886
|
+
date_range=False,date_freq=False,date_fmt='%Y-%m-%d', \
|
887
|
+
color1='red',color2='blue',facecolor='whitesmoke', \
|
888
|
+
ticker_type='auto', \
|
889
|
+
maxticks=15):
|
890
|
+
"""
|
891
|
+
功能:绘制两个证券的折线图。如果power=0不绘制趋势图,否则绘制多项式趋势图
|
892
|
+
假定:数据表有索引,且已经按照索引排序
|
893
|
+
输入:
|
894
|
+
证券1:数据表df1,证券代码ticker1,列名1,列名标签1;
|
895
|
+
证券2:数据表df2,证券代码ticker2,列名2,列名标签2;
|
896
|
+
标题titletxt,脚注footnote;是否在图中标记数据datatag;趋势图的多项式次数power
|
897
|
+
输出:绘制同轴折线图
|
898
|
+
返回值:无
|
899
|
+
注意:需要日期类型作为df索引
|
900
|
+
"""
|
901
|
+
import pandas as pd
|
902
|
+
DEBUG=False
|
903
|
+
|
904
|
+
#插值平滑:对于df索引为非日期型的不能进行插值平滑,否则会丢失数据!!!
|
905
|
+
if not isinstance(maxticks,bool):
|
906
|
+
try:
|
907
|
+
df01x=df01[[colname1]].astype('float')
|
908
|
+
df1=df_smooth_manual(df01x,resample_freq=resample_freq)
|
909
|
+
except:
|
910
|
+
df1=df01
|
911
|
+
try:
|
912
|
+
df02x=df02[[colname2]].astype('float')
|
913
|
+
df2=df_smooth_manual(df02x,resample_freq=resample_freq)
|
914
|
+
except:
|
915
|
+
df2=df02
|
916
|
+
else:
|
917
|
+
df1=df01; df2=df02
|
918
|
+
|
919
|
+
#预处理ticker_type
|
920
|
+
ticker_type_list=ticker_type_preprocess_mticker_mixed([ticker1,ticker2],ticker_type)
|
921
|
+
|
922
|
+
#证券1:先绘制折线图
|
923
|
+
if ticker1 == '':
|
924
|
+
label1txt=label1
|
925
|
+
else:
|
926
|
+
if label1 == '':
|
927
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])
|
928
|
+
else:
|
929
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])+'('+label1+')'
|
930
|
+
|
931
|
+
ax=plt.gca()
|
932
|
+
date_start=df1.index[0]
|
933
|
+
date_end=df1.index[-1]
|
934
|
+
import pandas as pd
|
935
|
+
|
936
|
+
if date_range and not date_freq:
|
937
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
938
|
+
plt.xticks(pd.date_range(date_start,date_end))
|
939
|
+
if not date_range and date_freq:
|
940
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
941
|
+
plt.xticks(pd.date_range(freq=date_freq))
|
942
|
+
if date_range and date_freq:
|
943
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
944
|
+
plt.xticks(pd.date_range(date_start,date_end,freq=date_freq))
|
945
|
+
|
946
|
+
lwadjust=linewidth_adjust(df1)
|
947
|
+
plt.plot(df1.index,df1[colname1],'-',label=label1txt, \
|
948
|
+
linestyle='-',linewidth=lwadjust,color=color1)
|
949
|
+
#证券1:绘制数据标签
|
950
|
+
if datatag1:
|
951
|
+
for x, y in zip(df1.index, df1[colname1]):
|
952
|
+
plt.text(x,y+0.1,'%.2f' % y,ha='center',va='bottom',color='black')
|
953
|
+
|
954
|
+
#是否绘制水平0线
|
955
|
+
if zeroline and ((min(df1[colname1]) < 0) or (min(df2[colname2]) < 0)):
|
956
|
+
plt.axhline(y=0,ls=":",c="black",linewidth=2.5)
|
957
|
+
|
958
|
+
"""
|
959
|
+
#是否绘制水平线
|
960
|
+
if yline != 999:
|
961
|
+
plt.axhline(y=yline,ls=":",c="black",linewidth=2.5)
|
962
|
+
|
963
|
+
#是否绘制垂直线
|
964
|
+
if xline != 999:
|
965
|
+
plt.axvline(x=xline,ls=":",c="black",linewidth=2.5)
|
966
|
+
"""
|
967
|
+
#是否绘制水平线
|
968
|
+
if yline != 999:
|
969
|
+
attention_value=yline
|
970
|
+
|
971
|
+
#用于关注值的颜色列表
|
972
|
+
atv_color_list=["lightgray","paleturquoise","wheat","khaki","lightsage"]
|
973
|
+
|
974
|
+
if isinstance(attention_value,int) or isinstance(attention_value,float):
|
975
|
+
atv_list=[attention_value]
|
976
|
+
elif isinstance(attention_value,list):
|
977
|
+
atv_list=attention_value
|
978
|
+
else:
|
979
|
+
atv_list=[]
|
980
|
+
|
981
|
+
if DEBUG:
|
982
|
+
print("atv_list=",atv_list)
|
983
|
+
|
984
|
+
if not atv_list==[] and not atv_list==['']:
|
985
|
+
for at in atv_list:
|
986
|
+
pos=atv_list.index(at)
|
987
|
+
color=atv_color_list[pos]
|
988
|
+
plt.axhline(y=at,ls=":",c=color,linewidth=2,label=text_lang("关注值","Attention value ")+str(at))
|
989
|
+
|
990
|
+
if not attention_value_area=='':
|
991
|
+
if isinstance(attention_value_area,list) and len(attention_value_area)>=2:
|
992
|
+
plt.fill_between(df1.index,attention_value_area[0],attention_value_area[1],color='lightgray',alpha=0.5)
|
993
|
+
|
994
|
+
#是否绘制垂直线
|
995
|
+
import pandas as pd
|
996
|
+
from datetime import datetime; date_format="%Y-%m-%d"
|
997
|
+
if xline != 999:
|
998
|
+
attention_point=xline
|
999
|
+
#用于关注点的颜色列表
|
1000
|
+
atp_color_list=["crimson","dodgerblue","magenta","lightseagreen","chocolate"]
|
1001
|
+
|
1002
|
+
if isinstance(attention_point,str) or isinstance(attention_point,int) or isinstance(attention_point,float):
|
1003
|
+
atp_list=[attention_point]
|
1004
|
+
elif isinstance(attention_point,list):
|
1005
|
+
atp_list=attention_point
|
1006
|
+
else:
|
1007
|
+
atp_list=[]
|
1008
|
+
#去重,不打乱原来的顺序
|
1009
|
+
atp_list=list(dict.fromkeys(atp_list))
|
1010
|
+
|
1011
|
+
if not atp_list==[] and not atp_list==['']:
|
1012
|
+
|
1013
|
+
for at in atp_list:
|
1014
|
+
pos=atp_list.index(at)
|
1015
|
+
color=atp_color_list[pos]
|
1016
|
+
|
1017
|
+
#判断是否日期字符串
|
1018
|
+
try:
|
1019
|
+
at=datetime.strptime(at, date_format)
|
1020
|
+
atpd=pd.to_datetime(at)
|
1021
|
+
except:
|
1022
|
+
atpd=at
|
1023
|
+
|
1024
|
+
if DEBUG:
|
1025
|
+
print("atpd=",atpd)
|
1026
|
+
|
1027
|
+
try:
|
1028
|
+
at_str=atpd.strftime('%Y-%m-%d')
|
1029
|
+
except:
|
1030
|
+
at_str=atpd
|
1031
|
+
#plt.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+str(at))
|
1032
|
+
plt.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+at_str)
|
1033
|
+
|
1034
|
+
if not attention_point_area=='':
|
1035
|
+
if isinstance(attention_point_area,list) and len(attention_point_area)>=2:
|
1036
|
+
apa_list=[]
|
1037
|
+
for ap in attention_point_area:
|
1038
|
+
try:
|
1039
|
+
ap=datetime.strptime(ap, date_format)
|
1040
|
+
appd=pd.to_datetime(ap)
|
1041
|
+
except:
|
1042
|
+
appd=ap
|
1043
|
+
apa_list=apa_list+[appd]
|
1044
|
+
|
1045
|
+
yaxis_data=plt.ylim()
|
1046
|
+
plt.fill_betweenx(yaxis_data,apa_list[0],apa_list[1],color='powderblue',alpha=0.5)
|
1047
|
+
|
1048
|
+
|
1049
|
+
#绘证券1:制趋势线
|
1050
|
+
if power > 0:
|
1051
|
+
lang=check_language()
|
1052
|
+
trend_txt='趋势线'
|
1053
|
+
if lang == 'English':
|
1054
|
+
trend_txt='Trend line'
|
1055
|
+
|
1056
|
+
try:
|
1057
|
+
#生成行号,借此将横轴的日期数量化,以便拟合
|
1058
|
+
df1['id']=range(len(df1))
|
1059
|
+
|
1060
|
+
#设定多项式拟合,power为多项式次数
|
1061
|
+
import numpy as np
|
1062
|
+
parameter = np.polyfit(df1.id, df1[colname1], power)
|
1063
|
+
f = np.poly1d(parameter)
|
1064
|
+
|
1065
|
+
if ticker1 == '':
|
1066
|
+
label1txt=''
|
1067
|
+
else:
|
1068
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])+"("+trend_txt+")"
|
1069
|
+
plt.plot(df1.index, f(df1.id),"g--", label=label1txt,linewidth=1)
|
1070
|
+
except: pass
|
1071
|
+
|
1072
|
+
#证券2:先绘制折线图
|
1073
|
+
if ticker2 == '':
|
1074
|
+
label2txt=label2
|
1075
|
+
else:
|
1076
|
+
if label2 == '':
|
1077
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])
|
1078
|
+
else:
|
1079
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])+'('+label2+')'
|
1080
|
+
|
1081
|
+
date_start=df2.index[0]
|
1082
|
+
date_end=df2.index[-1]
|
1083
|
+
if date_range and not date_freq:
|
1084
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
1085
|
+
plt.xticks(pd.date_range(date_start,date_end))
|
1086
|
+
if not date_range and date_freq:
|
1087
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
1088
|
+
plt.xticks(pd.date_range(freq=date_freq))
|
1089
|
+
if date_range and date_freq:
|
1090
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
1091
|
+
plt.xticks(pd.date_range(date_start,date_end,freq=date_freq))
|
1092
|
+
|
1093
|
+
lwadjust=linewidth_adjust(df2)
|
1094
|
+
plt.plot(df2.index,df2[colname2],'-',label=label2txt, \
|
1095
|
+
linestyle='-.',linewidth=lwadjust,color=color2)
|
1096
|
+
#证券2:绘制数据标签
|
1097
|
+
if datatag2:
|
1098
|
+
for x, y in zip(df2.index, df2[colname2]):
|
1099
|
+
plt.text(x,y+0.1,'%.2f' % y,ha='center',va='bottom',color='black')
|
1100
|
+
|
1101
|
+
#绘证券2:制趋势线
|
1102
|
+
if power > 0:
|
1103
|
+
lang=check_language()
|
1104
|
+
trend_txt='趋势线'
|
1105
|
+
if lang == 'English':
|
1106
|
+
trend_txt='Trend line'
|
1107
|
+
|
1108
|
+
try:
|
1109
|
+
#生成行号,借此将横轴的日期数量化,以便拟合
|
1110
|
+
df2['id']=range(len(df2))
|
1111
|
+
|
1112
|
+
#设定多项式拟合,power为多项式次数
|
1113
|
+
import numpy as np
|
1114
|
+
parameter = np.polyfit(df2.id, df2[colname2], power)
|
1115
|
+
f = np.poly1d(parameter)
|
1116
|
+
|
1117
|
+
if ticker2 == '':
|
1118
|
+
label2txt=''
|
1119
|
+
else:
|
1120
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])+"("+trend_txt+")"
|
1121
|
+
plt.plot(df2.index, f(df2.id),"r--", label=label2txt,linewidth=1)
|
1122
|
+
except: pass
|
1123
|
+
|
1124
|
+
# 同轴绘图时,loc1/loc2未用上!
|
1125
|
+
plt.legend(loc=loc1,fontsize=legend_txt_size)
|
1126
|
+
|
1127
|
+
# 使用 AutoDateLocator 自动选择最佳间隔,目的是显示最后一个日期,亲测有效!!!
|
1128
|
+
if not isinstance(maxticks,bool):
|
1129
|
+
import matplotlib.dates as mdates
|
1130
|
+
ax.xaxis.set_major_locator(mdates.AutoDateLocator(maxticks=maxticks))
|
1131
|
+
|
1132
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
1133
|
+
try:
|
1134
|
+
plt.gca().set_facecolor(facecolor)
|
1135
|
+
except:
|
1136
|
+
print(" #Warning(plot_line2_coaxial2): color",facecolor,"is unsupported, changed to default setting")
|
1137
|
+
plt.gca().set_facecolor("whitesmoke")
|
1138
|
+
|
1139
|
+
plt.ylabel(ylabeltxt,fontsize=ylabel_txt_size)
|
1140
|
+
plt.xlabel(footnote,fontsize=xlabel_txt_size,ha='center')
|
1141
|
+
plt.title(titletxt,fontweight='bold',fontsize=title_txt_size)
|
1142
|
+
plt.show()
|
1143
|
+
plt.close()
|
1144
|
+
|
1145
|
+
return
|
1146
|
+
|
1147
|
+
#==============================================================================
|
1148
|
+
if __name__ =="__main__":
|
1149
|
+
df01=df1; df02=df2
|
1150
|
+
maxticks=False
|
1151
|
+
|
1152
|
+
def plot_line2_twinx(df01,ticker1,colname1,label1, \
|
1153
|
+
df02,ticker2,colname2,label2, \
|
1154
|
+
titletxt,footnote,power=0,datatag1=False,datatag2=False, \
|
1155
|
+
resample_freq='D', \
|
1156
|
+
xline=999,attention_point_area='', \
|
1157
|
+
loc1='upper left',loc2='lower left', \
|
1158
|
+
color1='red',color2='blue',facecolor='whitesmoke', \
|
1159
|
+
ticker_type='auto', \
|
1160
|
+
maxticks=15):
|
1161
|
+
"""
|
1162
|
+
功能:绘制两个证券的折线图。如果power=0不绘制趋势图,否则绘制多项式趋势图
|
1163
|
+
假定:数据表有索引,且已经按照索引排序
|
1164
|
+
输入:
|
1165
|
+
证券1:数据表df1,证券代码ticker1,列名1,列名标签1;
|
1166
|
+
证券2:数据表df2,证券代码ticker2,列名2,列名标签2;
|
1167
|
+
标题titletxt,脚注footnote;是否在图中标记数据datatag;趋势图的多项式次数power
|
1168
|
+
输出:绘制双轴折线图
|
1169
|
+
返回值:无
|
1170
|
+
注意:需要日期类型作为df索引
|
1171
|
+
"""
|
1172
|
+
DEBUG=False
|
1173
|
+
|
1174
|
+
#plt.rcParams['axes.grid']=False
|
1175
|
+
|
1176
|
+
#插值平滑
|
1177
|
+
if not isinstance(maxticks,bool):
|
1178
|
+
try:
|
1179
|
+
df01x=df01[[colname1]].astype('float')
|
1180
|
+
df1=df_smooth_manual(df01x,resample_freq=resample_freq)
|
1181
|
+
except:
|
1182
|
+
df1=df01
|
1183
|
+
try:
|
1184
|
+
df02x=df02[[colname2]].astype('float')
|
1185
|
+
df2=df_smooth_manual(df02x,resample_freq=resample_freq)
|
1186
|
+
except:
|
1187
|
+
df2=df02
|
1188
|
+
else:
|
1189
|
+
df1=df01; df2=df02
|
1190
|
+
|
1191
|
+
#预处理ticker_type
|
1192
|
+
ticker_type_list=ticker_type_preprocess_mticker_mixed([ticker1,ticker2],ticker_type)
|
1193
|
+
|
1194
|
+
#证券1:绘制折线图,双坐标轴
|
1195
|
+
fig = plt.figure()
|
1196
|
+
ax = fig.add_subplot(111)
|
1197
|
+
try:
|
1198
|
+
fig.gca().set_facecolor(facecolor)
|
1199
|
+
except:
|
1200
|
+
print(" #Warning(plot_line2_twinx): color",facecolor,"is unsupported, changed to default setting")
|
1201
|
+
fig.gca().set_facecolor("whitesmoke")
|
1202
|
+
|
1203
|
+
if ticker1 == '':
|
1204
|
+
label1txt=label1
|
1205
|
+
else:
|
1206
|
+
if label1 == '':
|
1207
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])
|
1208
|
+
else:
|
1209
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])+'('+label1+')'
|
1210
|
+
|
1211
|
+
lwadjust=linewidth_adjust(df1)
|
1212
|
+
ax.plot(df1.index,df1[colname1],'-',label=label1txt, \
|
1213
|
+
linestyle='-',color=color1,linewidth=lwadjust)
|
1214
|
+
|
1215
|
+
#证券1:绘制数据标签
|
1216
|
+
if datatag1:
|
1217
|
+
for x, y in zip(df1.index, df1[colname1]):
|
1218
|
+
ax.text(x,y+0.1,'%.2f' % y,ha='center',va='bottom',color='black')
|
1219
|
+
|
1220
|
+
#绘制关注点
|
1221
|
+
import pandas as pd
|
1222
|
+
from datetime import datetime; date_format="%Y-%m-%d"
|
1223
|
+
if xline != 999:
|
1224
|
+
attention_point=xline
|
1225
|
+
|
1226
|
+
#用于关注点的颜色列表
|
1227
|
+
atp_color_list=["crimson","dodgerblue","magenta","lightseagreen","chocolate"]
|
1228
|
+
|
1229
|
+
if isinstance(attention_point,str) or isinstance(attention_point,int) or isinstance(attention_point,float):
|
1230
|
+
atp_list=[attention_point]
|
1231
|
+
elif isinstance(attention_point,list):
|
1232
|
+
atp_list=attention_point
|
1233
|
+
else:
|
1234
|
+
atp_list=[]
|
1235
|
+
#去重,不打乱原来的顺序
|
1236
|
+
atp_list=list(dict.fromkeys(atp_list))
|
1237
|
+
|
1238
|
+
if DEBUG:
|
1239
|
+
print("In plot_line2_twinx")
|
1240
|
+
print("atp_list=",atp_list)
|
1241
|
+
|
1242
|
+
if not atp_list==[] and not atp_list==['']:
|
1243
|
+
|
1244
|
+
for at in atp_list:
|
1245
|
+
pos=atp_list.index(at)
|
1246
|
+
color=atp_color_list[pos]
|
1247
|
+
|
1248
|
+
#判断是否日期字符串
|
1249
|
+
try:
|
1250
|
+
at=datetime.strptime(at, date_format)
|
1251
|
+
atpd=pd.to_datetime(at)
|
1252
|
+
except:
|
1253
|
+
atpd=at
|
1254
|
+
|
1255
|
+
try:
|
1256
|
+
at_str=atpd.strftime('%Y-%m-%d')
|
1257
|
+
except:
|
1258
|
+
at_str=atpd
|
1259
|
+
plt.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+at_str)
|
1260
|
+
|
1261
|
+
if not attention_point_area=='':
|
1262
|
+
if isinstance(attention_point_area,list) and len(attention_point_area)>=2:
|
1263
|
+
apa_list=[]
|
1264
|
+
for ap in attention_point_area:
|
1265
|
+
try:
|
1266
|
+
ap=datetime.strptime(ap, date_format)
|
1267
|
+
appd=pd.to_datetime(ap)
|
1268
|
+
except:
|
1269
|
+
appd=ap
|
1270
|
+
apa_list=apa_list+[appd]
|
1271
|
+
|
1272
|
+
yaxis_data=plt.ylim()
|
1273
|
+
plt.fill_betweenx(yaxis_data,apa_list[0],apa_list[1],color='powderblue',alpha=0.5)
|
1274
|
+
|
1275
|
+
#绘证券1:制趋势线
|
1276
|
+
if power > 0:
|
1277
|
+
lang=check_language()
|
1278
|
+
trend_txt='趋势线'
|
1279
|
+
if lang == 'English':
|
1280
|
+
trend_txt='Trend line'
|
1281
|
+
|
1282
|
+
#生成行号,借此将横轴的日期数量化,以便拟合
|
1283
|
+
df1['id']=range(len(df1))
|
1284
|
+
|
1285
|
+
#设定多项式拟合,power为多项式次数
|
1286
|
+
import numpy as np
|
1287
|
+
parameter = np.polyfit(df1.id, df1[colname1], power)
|
1288
|
+
f = np.poly1d(parameter)
|
1289
|
+
|
1290
|
+
if ticker1 == '':
|
1291
|
+
label1txt=''
|
1292
|
+
else:
|
1293
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])+"("+trend_txt+")"
|
1294
|
+
ax.plot(df1.index, f(df1.id),"r--", label=label1txt,linewidth=1)
|
1295
|
+
|
1296
|
+
#绘证券2:建立第二y轴
|
1297
|
+
ax2 = ax.twinx()
|
1298
|
+
|
1299
|
+
if ticker2 == '':
|
1300
|
+
label2txt=label2
|
1301
|
+
else:
|
1302
|
+
if label2 == '':
|
1303
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])
|
1304
|
+
else:
|
1305
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])+'('+label2+')'
|
1306
|
+
|
1307
|
+
lwadjust=linewidth_adjust(df2)
|
1308
|
+
ax2.plot(df2.index,df2[colname2],'-',label=label2txt, \
|
1309
|
+
linestyle='-.',color=color2,linewidth=lwadjust)
|
1310
|
+
#证券2:绘制数据标签
|
1311
|
+
if datatag2:
|
1312
|
+
for x, y in zip(df2.index, df2[colname2]):
|
1313
|
+
ax2.text(x,y+0.1,'%.2f' % y,ha='center',va='bottom',color='black')
|
1314
|
+
|
1315
|
+
#绘证券2:制趋势线
|
1316
|
+
if power > 0:
|
1317
|
+
lang=check_language()
|
1318
|
+
trend_txt='趋势线'
|
1319
|
+
if lang == 'English':
|
1320
|
+
trend_txt='Trend line'
|
1321
|
+
|
1322
|
+
#生成行号,借此将横轴的日期数量化,以便拟合
|
1323
|
+
df2['id']=range(len(df2))
|
1324
|
+
|
1325
|
+
#设定多项式拟合,power为多项式次数
|
1326
|
+
import numpy as np
|
1327
|
+
parameter = np.polyfit(df2.id, df2[colname2], power)
|
1328
|
+
f = np.poly1d(parameter)
|
1329
|
+
|
1330
|
+
if ticker2 == '':
|
1331
|
+
label2txt=''
|
1332
|
+
else:
|
1333
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])+"("+trend_txt+")"
|
1334
|
+
ax2.plot(df2.index, f(df2.id),"c--", label=label2txt,linewidth=1)
|
1335
|
+
|
1336
|
+
ax.set_xlabel(footnote,fontsize=xlabel_txt_size,ha='center')
|
1337
|
+
|
1338
|
+
if ticker1 == '':
|
1339
|
+
label1txt=label1
|
1340
|
+
else:
|
1341
|
+
if label1 == "":
|
1342
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])
|
1343
|
+
else:
|
1344
|
+
label1txt=label1+'('+ticker_name(ticker1,ticker_type_list[0])+')'
|
1345
|
+
ax.set_ylabel(label1txt,fontsize=ylabel_txt_size)
|
1346
|
+
ax.legend(loc=loc1,fontsize=legend_txt_size)
|
1347
|
+
|
1348
|
+
if ticker2 == '':
|
1349
|
+
label2txt=label2
|
1350
|
+
else:
|
1351
|
+
if label2 == "":
|
1352
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])
|
1353
|
+
else:
|
1354
|
+
label2txt=label2+'('+ticker_name(ticker2,ticker_type_list[1])+')'
|
1355
|
+
ax2.set_ylabel(label2txt,fontsize=ylabel_txt_size)
|
1356
|
+
ax2.legend(loc=loc2,fontsize=legend_txt_size)
|
1357
|
+
|
1358
|
+
# 使用 AutoDateLocator 自动选择最佳间隔,目的是显示最后一个日期,亲测有效!!!
|
1359
|
+
if not isinstance(maxticks,bool):
|
1360
|
+
import matplotlib.dates as mdates
|
1361
|
+
ax.xaxis.set_major_locator(mdates.AutoDateLocator(maxticks=maxticks))
|
1362
|
+
ax2.xaxis.set_major_locator(mdates.AutoDateLocator(maxticks=maxticks))
|
1363
|
+
|
1364
|
+
#自动优化x轴标签
|
1365
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
1366
|
+
|
1367
|
+
plt.title(titletxt,fontweight='bold',fontsize=title_txt_size)
|
1368
|
+
plt.show()
|
1369
|
+
|
1370
|
+
return
|
1371
|
+
|
1372
|
+
|
1373
|
+
if __name__ =="__main__":
|
1374
|
+
df1 = get_price('000002.SZ', '2020-1-1', '2020-3-16')
|
1375
|
+
df2 = get_price('600266.SS', '2020-1-1', '2020-3-16')
|
1376
|
+
ticker1='000002.SZ'; ticker2='600266.SS'
|
1377
|
+
colname1='Close'; colname2='Close'
|
1378
|
+
label1="收盘价"; label2="收盘价"
|
1379
|
+
ylabeltxt="价格"
|
1380
|
+
plot_line2_twinx(df1,'000002.SZ','Close','收盘价', \
|
1381
|
+
df2,'600266.SS','Close','收盘价', \
|
1382
|
+
"证券价格走势对比图","数据来源:新浪/stooq")
|
1383
|
+
|
1384
|
+
plot_line2_LR(df1,'000002.SZ','Close','收盘价', \
|
1385
|
+
df2,'600266.SS','Close','收盘价', \
|
1386
|
+
"证券价格走势对比图","数据来源:新浪/stooq",power=3)
|
1387
|
+
|
1388
|
+
#==============================================================================
|
1389
|
+
def plot_line2_LR(df01,ticker1,colname1,label1, \
|
1390
|
+
df02,ticker2,colname2,label2, \
|
1391
|
+
titletxt,footnote,power=0, \
|
1392
|
+
datatag1=False,datatag2=False, \
|
1393
|
+
resample_freq='D', \
|
1394
|
+
xline=999,attention_point_area='', \
|
1395
|
+
loc1='upper left',loc2='lower left', \
|
1396
|
+
color1='red',color2='blue',facecolor='whitesmoke', \
|
1397
|
+
ticker_type='auto', \
|
1398
|
+
maxticks=15):
|
1399
|
+
"""
|
1400
|
+
功能:绘制两个证券或指标的左右折线图。如果power=0不绘制趋势图,否则绘制多项式趋势图
|
1401
|
+
假定:数据表有索引,且已经按照索引排序
|
1402
|
+
输入:
|
1403
|
+
证券1:数据表df1,证券代码ticker1,列名1,列名标签1;
|
1404
|
+
证券2:数据表df2,证券代码ticker2,列名2,列名标签2;
|
1405
|
+
标题titletxt,脚注footnote;是否在图中标记数据datatag;趋势图的多项式次数power
|
1406
|
+
输出:绘制左右并列折线图
|
1407
|
+
返回值:无
|
1408
|
+
注意:需要日期类型作为df索引
|
1409
|
+
"""
|
1410
|
+
DEBUG=False
|
1411
|
+
|
1412
|
+
#plt.rcParams['axes.grid']=False
|
1413
|
+
|
1414
|
+
#插值平滑:若df索引为非日期型,不能进行插值平滑
|
1415
|
+
if not isinstance(maxticks,bool):
|
1416
|
+
try:
|
1417
|
+
df01x=df01[[colname1]].astype('float')
|
1418
|
+
df1=df_smooth_manual(df01x,resample_freq=resample_freq)
|
1419
|
+
except:
|
1420
|
+
df1=df01
|
1421
|
+
try:
|
1422
|
+
df02x=df02[[colname2]].astype('float')
|
1423
|
+
df2=df_smooth_manual(df02x,resample_freq=resample_freq)
|
1424
|
+
except:
|
1425
|
+
df2=df02
|
1426
|
+
else:
|
1427
|
+
df1=df01; df2=df02
|
1428
|
+
|
1429
|
+
#预处理ticker_type
|
1430
|
+
ticker_type_list=ticker_type_preprocess_mticker_mixed([ticker1,ticker2],ticker_type)
|
1431
|
+
|
1432
|
+
#创建画布:绘制折线图,左右双子图
|
1433
|
+
fig, (ax, ax2) = plt.subplots(1, 2)
|
1434
|
+
|
1435
|
+
#设置主标题
|
1436
|
+
#plt.suptitle(titletxt,fontweight='bold',fontsize=title_txt_size, y=1.01) # y参数调整垂直位置
|
1437
|
+
plt.suptitle(titletxt,fontweight='bold',fontsize=title_txt_size)
|
1438
|
+
|
1439
|
+
# 绘制左子图================================================================
|
1440
|
+
#设置画布背景颜色
|
1441
|
+
try:
|
1442
|
+
ax.set_facecolor(facecolor)
|
1443
|
+
except:
|
1444
|
+
print(" #Warning(plot_line2_twinx): color",facecolor,"is unsupported")
|
1445
|
+
facecolor="whitesmoke"
|
1446
|
+
ax.set_facecolor(facecolor)
|
1447
|
+
|
1448
|
+
# 设置折线标签
|
1449
|
+
if ticker1 == '':
|
1450
|
+
label1txt=label1
|
1451
|
+
else:
|
1452
|
+
if label1 == '':
|
1453
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])
|
1454
|
+
else:
|
1455
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])+'('+label1+')'
|
1456
|
+
|
1457
|
+
# 设置子图标题
|
1458
|
+
if ticker1 != ticker2:
|
1459
|
+
ax.set_title(label1txt,fontsize=xlabel_txt_size)
|
1460
|
+
else:
|
1461
|
+
ax.set_title(ectranslate(colname1),fontsize=xlabel_txt_size)
|
1462
|
+
|
1463
|
+
# 绘图
|
1464
|
+
lwadjust=linewidth_adjust(df1)
|
1465
|
+
"""
|
1466
|
+
ax.plot(df1.index,df1[colname1],'-',label=label1txt, \
|
1467
|
+
linestyle='-',color=color1,linewidth=lwadjust)
|
1468
|
+
"""
|
1469
|
+
ax.plot(df1.index,df1[colname1],'-', \
|
1470
|
+
linestyle='-',color=color1,linewidth=lwadjust)
|
1471
|
+
|
1472
|
+
#证券1:绘制数据标签
|
1473
|
+
if datatag1:
|
1474
|
+
for x, y in zip(df1.index, df1[colname1]):
|
1475
|
+
ax.text(x,y+0.1,'%.2f' % y,ha='center',va='bottom',color='black')
|
1476
|
+
|
1477
|
+
#绘制关注点
|
1478
|
+
import pandas as pd
|
1479
|
+
from datetime import datetime; date_format="%Y-%m-%d"
|
1480
|
+
if xline != 999:
|
1481
|
+
attention_point=xline
|
1482
|
+
|
1483
|
+
#用于关注点的颜色列表
|
1484
|
+
atp_color_list=["crimson","dodgerblue","magenta","lightseagreen","chocolate"]
|
1485
|
+
|
1486
|
+
if isinstance(attention_point,str) or isinstance(attention_point,int) or isinstance(attention_point,float):
|
1487
|
+
atp_list=[attention_point]
|
1488
|
+
elif isinstance(attention_point,list):
|
1489
|
+
atp_list=attention_point
|
1490
|
+
else:
|
1491
|
+
atp_list=[]
|
1492
|
+
#去重,不打乱原来的顺序
|
1493
|
+
atp_list=list(dict.fromkeys(atp_list))
|
1494
|
+
|
1495
|
+
if DEBUG:
|
1496
|
+
print("In plot_line2_twinx")
|
1497
|
+
print("atp_list=",atp_list)
|
1498
|
+
|
1499
|
+
if not atp_list==[] and not atp_list==['']:
|
1500
|
+
|
1501
|
+
for at in atp_list:
|
1502
|
+
pos=atp_list.index(at)
|
1503
|
+
color=atp_color_list[pos]
|
1504
|
+
|
1505
|
+
#判断是否日期字符串
|
1506
|
+
try:
|
1507
|
+
at=datetime.strptime(at, date_format)
|
1508
|
+
atpd=pd.to_datetime(at)
|
1509
|
+
except:
|
1510
|
+
atpd=at
|
1511
|
+
|
1512
|
+
try:
|
1513
|
+
at_str=atpd.strftime('%Y-%m-%d')
|
1514
|
+
except:
|
1515
|
+
at_str=atpd
|
1516
|
+
#ax.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+str(at))
|
1517
|
+
ax.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+at_str)
|
1518
|
+
|
1519
|
+
if not attention_point_area=='':
|
1520
|
+
if isinstance(attention_point_area,list) and len(attention_point_area)>=2:
|
1521
|
+
apa_list=[]
|
1522
|
+
for ap in attention_point_area:
|
1523
|
+
try:
|
1524
|
+
ap=datetime.strptime(ap, date_format)
|
1525
|
+
appd=pd.to_datetime(ap)
|
1526
|
+
except:
|
1527
|
+
appd=ap
|
1528
|
+
apa_list=apa_list+[appd]
|
1529
|
+
|
1530
|
+
yaxis_data=plt.ylim()
|
1531
|
+
ax.fill_betweenx(yaxis_data,apa_list[0],apa_list[1],color='powderblue',alpha=0.5)
|
1532
|
+
|
1533
|
+
#绘证券1:制趋势线
|
1534
|
+
if power > 0:
|
1535
|
+
lang=check_language()
|
1536
|
+
trend_txt='趋势线'
|
1537
|
+
if lang == 'English':
|
1538
|
+
trend_txt='Trend line'
|
1539
|
+
|
1540
|
+
#生成行号,借此将横轴的日期数量化,以便拟合
|
1541
|
+
df1['id']=range(len(df1))
|
1542
|
+
|
1543
|
+
#设定多项式拟合,power为多项式次数
|
1544
|
+
import numpy as np
|
1545
|
+
parameter = np.polyfit(df1.id, df1[colname1], power)
|
1546
|
+
f = np.poly1d(parameter)
|
1547
|
+
|
1548
|
+
if ticker1 == '':
|
1549
|
+
label1txt=''
|
1550
|
+
else:
|
1551
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])+"("+trend_txt+")"
|
1552
|
+
ax.plot(df1.index, f(df1.id),"r--", label=label1txt,linewidth=1)
|
1553
|
+
|
1554
|
+
# 纵轴标签
|
1555
|
+
#ax.set_ylabel(label1txt,fontsize=ylabel_txt_size)
|
1556
|
+
#ax.legend(loc=loc1,fontsize=legend_txt_size)
|
1557
|
+
|
1558
|
+
# 横轴标签
|
1559
|
+
#ax.set_xlabel(text_lang("日期","Date"),fontsize=xlabel_txt_size,ha='center')
|
1560
|
+
|
1561
|
+
#绘图右图===================================================================
|
1562
|
+
#设置画布背景颜色
|
1563
|
+
ax2.set_facecolor(facecolor)
|
1564
|
+
|
1565
|
+
# 折线标签
|
1566
|
+
if ticker2 == '':
|
1567
|
+
label2txt=label2
|
1568
|
+
else:
|
1569
|
+
if label2 == '':
|
1570
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])
|
1571
|
+
else:
|
1572
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])+'('+label2+')'
|
1573
|
+
|
1574
|
+
# 设置子图标题
|
1575
|
+
if ticker1 != ticker2:
|
1576
|
+
ax2.set_title(label2txt,fontsize=xlabel_txt_size)
|
1577
|
+
else:
|
1578
|
+
ax2.set_title(ectranslate(colname2),fontsize=xlabel_txt_size)
|
1579
|
+
|
1580
|
+
# 绘图
|
1581
|
+
lwadjust=linewidth_adjust(df2)
|
1582
|
+
"""
|
1583
|
+
ax2.plot(df2.index,df2[colname2],'-',label=label2txt, \
|
1584
|
+
linestyle='-.',color=color2,linewidth=lwadjust)
|
1585
|
+
"""
|
1586
|
+
ax2.plot(df2.index,df2[colname2],'-', \
|
1587
|
+
linestyle='-.',color=color2,linewidth=lwadjust)
|
1588
|
+
|
1589
|
+
#证券2:绘制数据标签
|
1590
|
+
if datatag2:
|
1591
|
+
for x, y in zip(df2.index, df2[colname2]):
|
1592
|
+
ax2.text(x,y+0.1,'%.2f' % y,ha='center',va='bottom',color='black')
|
1593
|
+
|
1594
|
+
#绘证券2:制趋势线
|
1595
|
+
if power > 0:
|
1596
|
+
lang=check_language()
|
1597
|
+
trend_txt='趋势线'
|
1598
|
+
if lang == 'English':
|
1599
|
+
trend_txt='Trend line'
|
1600
|
+
|
1601
|
+
#生成行号,借此将横轴的日期数量化,以便拟合
|
1602
|
+
df2['id']=range(len(df2))
|
1603
|
+
|
1604
|
+
#设定多项式拟合,power为多项式次数
|
1605
|
+
import numpy as np
|
1606
|
+
parameter = np.polyfit(df2.id, df2[colname2], power)
|
1607
|
+
f = np.poly1d(parameter)
|
1608
|
+
|
1609
|
+
if ticker2 == '':
|
1610
|
+
label2txt=''
|
1611
|
+
else:
|
1612
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])+"("+trend_txt+")"
|
1613
|
+
ax2.plot(df2.index, f(df2.id),"c--", label=label2txt,linewidth=1)
|
1614
|
+
|
1615
|
+
# 纵轴标签
|
1616
|
+
#ax2.set_ylabel(label2txt,fontsize=ylabel_txt_size)
|
1617
|
+
#ax2.legend(loc=loc2,fontsize=legend_txt_size)
|
1618
|
+
|
1619
|
+
# 横轴标签
|
1620
|
+
#ax2.set_xlabel(text_lang("日期","Date"),fontsize=xlabel_txt_size,ha='center')
|
1621
|
+
|
1622
|
+
#自动优化x轴标签
|
1623
|
+
#ax2.autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
1624
|
+
|
1625
|
+
# 共同脚注==================================================================
|
1626
|
+
fig.text(0.5,-0.01,footnote,ha='center',fontsize=xlabel_txt_size,color='gray')
|
1627
|
+
#plt.xlabel(footnote,ha='center',fontsize=xlabel_txt_size,color='gray')
|
1628
|
+
|
1629
|
+
# 自动倾斜横轴日期
|
1630
|
+
if not isinstance(maxticks,bool):
|
1631
|
+
fig.autofmt_xdate(ha="center")
|
1632
|
+
|
1633
|
+
# 调整布局防止重叠
|
1634
|
+
plt.tight_layout()
|
1635
|
+
plt.show()
|
1636
|
+
|
1637
|
+
return
|
1638
|
+
|
1639
|
+
|
1640
|
+
#==============================================================================
|
1641
|
+
def plot_line2_UD(df01,ticker1,colname1,label1, \
|
1642
|
+
df02,ticker2,colname2,label2, \
|
1643
|
+
titletxt,footnote,power=0, \
|
1644
|
+
datatag1=False,datatag2=False, \
|
1645
|
+
resample_freq='D', \
|
1646
|
+
xline=999,attention_point_area='', \
|
1647
|
+
loc1='upper left',loc2='lower left', \
|
1648
|
+
color1='red',color2='blue',facecolor='whitesmoke', \
|
1649
|
+
ticker_type='auto', \
|
1650
|
+
maxticks=15):
|
1651
|
+
"""
|
1652
|
+
功能:绘制两个证券或指标的上下折线图。如果power=0不绘制趋势图,否则绘制多项式趋势图
|
1653
|
+
假定:数据表有索引,且已经按照索引排序
|
1654
|
+
输入:
|
1655
|
+
证券1:数据表df1,证券代码ticker1,列名1,列名标签1;
|
1656
|
+
证券2:数据表df2,证券代码ticker2,列名2,列名标签2;
|
1657
|
+
标题titletxt,脚注footnote;是否在图中标记数据datatag;趋势图的多项式次数power
|
1658
|
+
输出:绘制上下并列折线图
|
1659
|
+
返回值:无
|
1660
|
+
注意:需要日期类型作为df索引
|
1661
|
+
"""
|
1662
|
+
DEBUG=False
|
1663
|
+
|
1664
|
+
#plt.rcParams['axes.grid']=False
|
1665
|
+
|
1666
|
+
#插值平滑
|
1667
|
+
if not isinstance(maxticks,bool):
|
1668
|
+
try:
|
1669
|
+
df01x=df01[[colname1]].astype('float')
|
1670
|
+
df1=df_smooth_manual(df01x,resample_freq=resample_freq)
|
1671
|
+
except:
|
1672
|
+
df1=df01
|
1673
|
+
try:
|
1674
|
+
df02x=df02[[colname2]].astype('float')
|
1675
|
+
df2=df_smooth_manual(df02x,resample_freq=resample_freq)
|
1676
|
+
except:
|
1677
|
+
df2=df02
|
1678
|
+
else:
|
1679
|
+
df1=df01; df2=df02
|
1680
|
+
|
1681
|
+
#预处理ticker_type
|
1682
|
+
ticker_type_list=ticker_type_preprocess_mticker_mixed([ticker1,ticker2],ticker_type)
|
1683
|
+
|
1684
|
+
#创建画布:绘制折线图,上下双子图
|
1685
|
+
fig, (ax, ax2) = plt.subplots(2, 1)
|
1686
|
+
|
1687
|
+
#设置主标题
|
1688
|
+
#plt.suptitle(titletxt,fontweight='bold',fontsize=title_txt_size, y=1.01) # y参数调整垂直位置
|
1689
|
+
plt.suptitle(titletxt,fontweight='bold',fontsize=title_txt_size)
|
1690
|
+
|
1691
|
+
# 绘制左子图================================================================
|
1692
|
+
#设置画布背景颜色
|
1693
|
+
try:
|
1694
|
+
ax.set_facecolor(facecolor)
|
1695
|
+
except:
|
1696
|
+
print(" #Warning(plot_line2_twinx): color",facecolor,"is unsupported")
|
1697
|
+
facecolor="whitesmoke"
|
1698
|
+
ax.set_facecolor(facecolor)
|
1699
|
+
|
1700
|
+
# 设置折线标签
|
1701
|
+
if ticker1 == '':
|
1702
|
+
label1txt=label1
|
1703
|
+
else:
|
1704
|
+
if label1 == '':
|
1705
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])
|
1706
|
+
else:
|
1707
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])+'('+label1+')'
|
1708
|
+
|
1709
|
+
# 设置子图标题
|
1710
|
+
"""
|
1711
|
+
if ticker1 != ticker2:
|
1712
|
+
ax.set_title(label1txt,fontsize=xlabel_txt_size)
|
1713
|
+
else:
|
1714
|
+
ax.set_title(ectranslate(colname1),fontsize=xlabel_txt_size)
|
1715
|
+
"""
|
1716
|
+
# 绘图
|
1717
|
+
lwadjust=linewidth_adjust(df1)
|
1718
|
+
"""
|
1719
|
+
ax.plot(df1.index,df1[colname1],'-',label=label1txt, \
|
1720
|
+
linestyle='-',color=color1,linewidth=lwadjust)
|
1721
|
+
"""
|
1722
|
+
ax.plot(df1.index,df1[colname1],'-', \
|
1723
|
+
linestyle='-',color=color1,linewidth=lwadjust)
|
1724
|
+
|
1725
|
+
#证券1:绘制数据标签
|
1726
|
+
if datatag1:
|
1727
|
+
for x, y in zip(df1.index, df1[colname1]):
|
1728
|
+
ax.text(x,y+0.1,'%.2f' % y,ha='center',va='bottom',color='black')
|
1729
|
+
|
1730
|
+
#绘制关注点
|
1731
|
+
import pandas as pd
|
1732
|
+
from datetime import datetime; date_format="%Y-%m-%d"
|
1733
|
+
if xline != 999:
|
1734
|
+
attention_point=xline
|
1735
|
+
|
1736
|
+
#用于关注点的颜色列表
|
1737
|
+
atp_color_list=["crimson","dodgerblue","magenta","lightseagreen","chocolate"]
|
1738
|
+
|
1739
|
+
if isinstance(attention_point,str) or isinstance(attention_point,int) or isinstance(attention_point,float):
|
1740
|
+
atp_list=[attention_point]
|
1741
|
+
elif isinstance(attention_point,list):
|
1742
|
+
atp_list=attention_point
|
1743
|
+
else:
|
1744
|
+
atp_list=[]
|
1745
|
+
#去重,不打乱原来的顺序
|
1746
|
+
atp_list=list(dict.fromkeys(atp_list))
|
1747
|
+
|
1748
|
+
if DEBUG:
|
1749
|
+
print("In plot_line2_twinx")
|
1750
|
+
print("atp_list=",atp_list)
|
1751
|
+
|
1752
|
+
if not atp_list==[] and not atp_list==['']:
|
1753
|
+
|
1754
|
+
for at in atp_list:
|
1755
|
+
pos=atp_list.index(at)
|
1756
|
+
color=atp_color_list[pos]
|
1757
|
+
|
1758
|
+
#判断是否日期字符串
|
1759
|
+
try:
|
1760
|
+
at=datetime.strptime(at, date_format)
|
1761
|
+
atpd=pd.to_datetime(at)
|
1762
|
+
except:
|
1763
|
+
atpd=at
|
1764
|
+
|
1765
|
+
try:
|
1766
|
+
at_str=atpd.strftime('%Y-%m-%d')
|
1767
|
+
except:
|
1768
|
+
at_str=atpd
|
1769
|
+
#ax.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+str(at))
|
1770
|
+
ax.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+at_str)
|
1771
|
+
|
1772
|
+
if not attention_point_area=='':
|
1773
|
+
if isinstance(attention_point_area,list) and len(attention_point_area)>=2:
|
1774
|
+
apa_list=[]
|
1775
|
+
for ap in attention_point_area:
|
1776
|
+
try:
|
1777
|
+
ap=datetime.strptime(ap, date_format)
|
1778
|
+
appd=pd.to_datetime(ap)
|
1779
|
+
except:
|
1780
|
+
appd=ap
|
1781
|
+
apa_list=apa_list+[appd]
|
1782
|
+
|
1783
|
+
yaxis_data=plt.ylim()
|
1784
|
+
ax.fill_betweenx(yaxis_data,apa_list[0],apa_list[1],color='powderblue',alpha=0.5)
|
1785
|
+
|
1786
|
+
#绘证券1:制趋势线
|
1787
|
+
if power > 0:
|
1788
|
+
lang=check_language()
|
1789
|
+
trend_txt='趋势线'
|
1790
|
+
if lang == 'English':
|
1791
|
+
trend_txt='Trend line'
|
1792
|
+
|
1793
|
+
#生成行号,借此将横轴的日期数量化,以便拟合
|
1794
|
+
df1['id']=range(len(df1))
|
1795
|
+
|
1796
|
+
#设定多项式拟合,power为多项式次数
|
1797
|
+
import numpy as np
|
1798
|
+
parameter = np.polyfit(df1.id, df1[colname1], power)
|
1799
|
+
f = np.poly1d(parameter)
|
1800
|
+
|
1801
|
+
if ticker1 == '':
|
1802
|
+
label1txt=''
|
1803
|
+
else:
|
1804
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])+"("+trend_txt+")"
|
1805
|
+
ax.plot(df1.index, f(df1.id),"r--", label=label1txt,linewidth=1)
|
1806
|
+
|
1807
|
+
# 纵轴标签
|
1808
|
+
if ticker1 != ticker2:
|
1809
|
+
ax.set_ylabel(label1txt,fontsize=ylabel_txt_size)
|
1810
|
+
else:
|
1811
|
+
ax.set_ylabel(ectranslate(colname1),fontsize=ylabel_txt_size)
|
1812
|
+
#ax.legend(loc=loc1,fontsize=legend_txt_size)
|
1813
|
+
|
1814
|
+
# 横轴标签
|
1815
|
+
#ax.set_xlabel(text_lang("日期","Date"),fontsize=xlabel_txt_size,ha='center')
|
1816
|
+
|
1817
|
+
#绘图右图===================================================================
|
1818
|
+
#设置画布背景颜色
|
1819
|
+
ax2.set_facecolor(facecolor)
|
1820
|
+
|
1821
|
+
# 折线标签
|
1822
|
+
if ticker2 == '':
|
1823
|
+
label2txt=label2
|
1824
|
+
else:
|
1825
|
+
if label2 == '':
|
1826
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])
|
1827
|
+
else:
|
1828
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])+'('+label2+')'
|
1829
|
+
|
1830
|
+
# 设置子图标题
|
1831
|
+
"""
|
1832
|
+
if ticker1 != ticker2:
|
1833
|
+
ax2.set_title(label2txt,fontsize=xlabel_txt_size)
|
1834
|
+
else:
|
1835
|
+
ax2.set_title(ectranslate(colname2),fontsize=xlabel_txt_size)
|
1836
|
+
"""
|
1837
|
+
# 绘图
|
1838
|
+
lwadjust=linewidth_adjust(df2)
|
1839
|
+
"""
|
1840
|
+
ax2.plot(df2.index,df2[colname2],'-',label=label2txt, \
|
1841
|
+
linestyle='-.',color=color2,linewidth=lwadjust)
|
1842
|
+
"""
|
1843
|
+
ax2.plot(df2.index,df2[colname2],'-', \
|
1844
|
+
linestyle='-.',color=color2,linewidth=lwadjust)
|
1845
|
+
|
1846
|
+
#证券2:绘制数据标签
|
1847
|
+
if datatag2:
|
1848
|
+
for x, y in zip(df2.index, df2[colname2]):
|
1849
|
+
ax2.text(x,y+0.1,'%.2f' % y,ha='center',va='bottom',color='black')
|
1850
|
+
|
1851
|
+
#绘证券2:制趋势线
|
1852
|
+
if power > 0:
|
1853
|
+
lang=check_language()
|
1854
|
+
trend_txt='趋势线'
|
1855
|
+
if lang == 'English':
|
1856
|
+
trend_txt='Trend line'
|
1857
|
+
|
1858
|
+
#生成行号,借此将横轴的日期数量化,以便拟合
|
1859
|
+
df2['id']=range(len(df2))
|
1860
|
+
|
1861
|
+
#设定多项式拟合,power为多项式次数
|
1862
|
+
import numpy as np
|
1863
|
+
parameter = np.polyfit(df2.id, df2[colname2], power)
|
1864
|
+
f = np.poly1d(parameter)
|
1865
|
+
|
1866
|
+
if ticker2 == '':
|
1867
|
+
label2txt=''
|
1868
|
+
else:
|
1869
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])+"("+trend_txt+")"
|
1870
|
+
ax2.plot(df2.index, f(df2.id),"c--", label=label2txt,linewidth=1)
|
1871
|
+
|
1872
|
+
# 纵轴标签
|
1873
|
+
if ticker1 != ticker2:
|
1874
|
+
ax2.set_ylabel(label2txt,fontsize=ylabel_txt_size)
|
1875
|
+
else:
|
1876
|
+
ax2.set_ylabel(ectranslate(colname2),fontsize=ylabel_txt_size)
|
1877
|
+
#ax2.legend(loc=loc2,fontsize=legend_txt_size)
|
1878
|
+
|
1879
|
+
# 横轴标签
|
1880
|
+
#ax2.set_xlabel(text_lang("日期","Date"),fontsize=xlabel_txt_size,ha='center')
|
1881
|
+
|
1882
|
+
#自动优化x轴标签
|
1883
|
+
#ax2.autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
1884
|
+
|
1885
|
+
# 共同脚注==================================================================
|
1886
|
+
fig.text(0.5,-0.01,footnote,ha='center',fontsize=xlabel_txt_size,color='gray')
|
1887
|
+
#plt.xlabel(footnote,ha='center',fontsize=xlabel_txt_size,color='gray')
|
1888
|
+
|
1889
|
+
# 自动倾斜横轴日期
|
1890
|
+
if not isinstance(maxticks,bool):
|
1891
|
+
fig.autofmt_xdate(ha="center")
|
1892
|
+
|
1893
|
+
# 调整布局防止重叠
|
1894
|
+
plt.tight_layout()
|
1895
|
+
plt.show()
|
1896
|
+
|
1897
|
+
return
|
1898
|
+
|
1899
|
+
#==============================================================================
|
1900
|
+
def plot_line2_twinx2(df01,ticker1,colname1,label1, \
|
1901
|
+
df02,ticker2,colname2,label2, \
|
1902
|
+
titletxt,footnote,power=0,datatag1=False,datatag2=False, \
|
1903
|
+
xline=999,attention_point_area='', \
|
1904
|
+
resample_freq='D',loc1='upper left',loc2='lower left', \
|
1905
|
+
date_range=False,date_freq=False,date_fmt='%Y-%m-%d', \
|
1906
|
+
color1='red',color2='blue',facecolor='whitesmoke', \
|
1907
|
+
ticker_type='auto', \
|
1908
|
+
maxticks=15):
|
1909
|
+
"""
|
1910
|
+
功能:绘制两个证券的折线图。如果power=0不绘制趋势图,否则绘制多项式趋势图
|
1911
|
+
假定:数据表有索引,且已经按照索引排序
|
1912
|
+
输入:
|
1913
|
+
证券1:数据表df1,证券代码ticker1,列名1,列名标签1;
|
1914
|
+
证券2:数据表df2,证券代码ticker2,列名2,列名标签2;
|
1915
|
+
标题titletxt,脚注footnote;是否在图中标记数据datatag;趋势图的多项式次数power
|
1916
|
+
输出:绘制双轴折线图
|
1917
|
+
返回值:无
|
1918
|
+
注意:需要日期类型作为df索引
|
1919
|
+
"""
|
1920
|
+
import pandas as pd
|
1921
|
+
#plt.rcParams['axes.grid']=False
|
1922
|
+
|
1923
|
+
#插值平滑
|
1924
|
+
if not isinstance(maxticks,bool):
|
1925
|
+
try:
|
1926
|
+
df01x=df01[[colname1]].astype('float')
|
1927
|
+
df1=df_smooth_manual(df01x,resample_freq=resample_freq)
|
1928
|
+
except:
|
1929
|
+
df1=df01
|
1930
|
+
try:
|
1931
|
+
df02x=df02[[colname2]].astype('float')
|
1932
|
+
df2=df_smooth_manual(df02x,resample_freq=resample_freq)
|
1933
|
+
except:
|
1934
|
+
df2=df02
|
1935
|
+
else:
|
1936
|
+
df1=df01; df2=df02
|
1937
|
+
|
1938
|
+
#预处理ticker_type
|
1939
|
+
ticker_type_list=ticker_type_preprocess_mticker_mixed([ticker1,ticker2],ticker_type)
|
1940
|
+
|
1941
|
+
#证券1:绘制折线图,双坐标轴
|
1942
|
+
fig = plt.figure()
|
1943
|
+
try:
|
1944
|
+
fig.gca().set_facecolor(facecolor)
|
1945
|
+
except:
|
1946
|
+
print(" #Warning(plot_line2_twinx2): color",facecolor,"is unsupported, changed to default setting")
|
1947
|
+
fig.gca().set_facecolor("whitesmoke")
|
1948
|
+
|
1949
|
+
ax = fig.add_subplot(111)
|
1950
|
+
|
1951
|
+
if ticker1 == '':
|
1952
|
+
label1txt=label1
|
1953
|
+
else:
|
1954
|
+
if label1 == '':
|
1955
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])
|
1956
|
+
else:
|
1957
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])+'('+label1+')'
|
1958
|
+
|
1959
|
+
date_start=df1.index[0]
|
1960
|
+
date_end=df1.index[-1]
|
1961
|
+
|
1962
|
+
if date_range and not date_freq:
|
1963
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
1964
|
+
plt.xticks(pd.date_range(date_start,date_end))
|
1965
|
+
if not date_range and date_freq:
|
1966
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
1967
|
+
plt.xticks(pd.date_range(freq=date_freq))
|
1968
|
+
if date_range and date_freq:
|
1969
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
1970
|
+
plt.xticks(pd.date_range(date_start,date_end,freq=date_freq))
|
1971
|
+
|
1972
|
+
lwadjust=linewidth_adjust(df1)
|
1973
|
+
ax.plot(df1.index,df1[colname1],'-',label=label1txt, \
|
1974
|
+
linestyle='-',color=color1,linewidth=lwadjust)
|
1975
|
+
#证券1:绘制数据标签
|
1976
|
+
if datatag1:
|
1977
|
+
for x, y in zip(df1.index, df1[colname1]):
|
1978
|
+
ax.text(x,y+0.1,'%.2f' % y,ha='center',va='bottom',color='black')
|
1979
|
+
|
1980
|
+
#绘制关注点
|
1981
|
+
import pandas as pd
|
1982
|
+
from datetime import datetime; date_format="%Y-%m-%d"
|
1983
|
+
if xline != 999:
|
1984
|
+
attention_point=xline
|
1985
|
+
|
1986
|
+
#用于关注点的颜色列表
|
1987
|
+
atp_color_list=["crimson","dodgerblue","magenta","lightseagreen","chocolate"]
|
1988
|
+
|
1989
|
+
if isinstance(attention_point,str) or isinstance(attention_point,int) or isinstance(attention_point,float):
|
1990
|
+
atp_list=[attention_point]
|
1991
|
+
elif isinstance(attention_point,list):
|
1992
|
+
atp_list=attention_point
|
1993
|
+
else:
|
1994
|
+
atp_list=[]
|
1995
|
+
#去重,不打乱原来的顺序
|
1996
|
+
atp_list=list(dict.fromkeys(atp_list))
|
1997
|
+
|
1998
|
+
if DEBUG:
|
1999
|
+
print("In plot_line2_twinx")
|
2000
|
+
print("atp_list=",atp_list)
|
2001
|
+
|
2002
|
+
if not atp_list==[] and not atp_list==['']:
|
2003
|
+
|
2004
|
+
for at in atp_list:
|
2005
|
+
pos=atp_list.index(at)
|
2006
|
+
color=atp_color_list[pos]
|
2007
|
+
|
2008
|
+
#判断是否日期字符串
|
2009
|
+
try:
|
2010
|
+
at=datetime.strptime(at, date_format)
|
2011
|
+
atpd=pd.to_datetime(at)
|
2012
|
+
except:
|
2013
|
+
atpd=at
|
2014
|
+
|
2015
|
+
try:
|
2016
|
+
at_str=atpd.strftime('%Y-%m-%d')
|
2017
|
+
except:
|
2018
|
+
at_str=atpd
|
2019
|
+
#plt.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+str(at))
|
2020
|
+
plt.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+at_str)
|
2021
|
+
|
2022
|
+
if not attention_point_area=='':
|
2023
|
+
if isinstance(attention_point_area,list) and len(attention_point_area)>=2:
|
2024
|
+
apa_list=[]
|
2025
|
+
for ap in attention_point_area:
|
2026
|
+
try:
|
2027
|
+
ap=datetime.strptime(ap, date_format)
|
2028
|
+
appd=pd.to_datetime(ap)
|
2029
|
+
except:
|
2030
|
+
appd=ap
|
2031
|
+
apa_list=apa_list+[appd]
|
2032
|
+
|
2033
|
+
yaxis_data=plt.ylim()
|
2034
|
+
plt.fill_betweenx(yaxis_data,apa_list[0],apa_list[1],color='powderblue',alpha=0.5)
|
2035
|
+
|
2036
|
+
|
2037
|
+
#绘证券1:制趋势线
|
2038
|
+
if power > 0:
|
2039
|
+
lang=check_language()
|
2040
|
+
trend_txt='趋势线'
|
2041
|
+
if lang == 'English':
|
2042
|
+
trend_txt='Trend line'
|
2043
|
+
|
2044
|
+
#生成行号,借此将横轴的日期数量化,以便拟合
|
2045
|
+
df1['id']=range(len(df1))
|
2046
|
+
|
2047
|
+
#设定多项式拟合,power为多项式次数
|
2048
|
+
import numpy as np
|
2049
|
+
parameter = np.polyfit(df1.id, df1[colname1], power)
|
2050
|
+
f = np.poly1d(parameter)
|
2051
|
+
|
2052
|
+
if ticker1 == '':
|
2053
|
+
label1txt=''
|
2054
|
+
else:
|
2055
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])+"("+trend_txt+")"
|
2056
|
+
ax.plot(df1.index, f(df1.id),"r--", label=label1txt,linewidth=1)
|
2057
|
+
|
2058
|
+
ax.set_xlabel(footnote,fontsize=xlabel_txt_size,ha='center')
|
2059
|
+
|
2060
|
+
if ticker1 == '':
|
2061
|
+
label1txt=label1
|
2062
|
+
else:
|
2063
|
+
if label1 == "":
|
2064
|
+
label1txt=ticker_name(ticker1,ticker_type_list[0])
|
2065
|
+
else:
|
2066
|
+
label1txt=label1+'('+ticker_name(ticker1,ticker_type_list[0])+')'
|
2067
|
+
ax.set_ylabel(label1txt,fontsize=ylabel_txt_size)
|
2068
|
+
ax.legend(loc=loc1,fontsize=legend_txt_size)
|
2069
|
+
|
2070
|
+
#绘证券2:建立第二y轴
|
2071
|
+
ax2 = ax.twinx()
|
2072
|
+
|
2073
|
+
if ticker2 == '':
|
2074
|
+
label2txt=label2
|
2075
|
+
else:
|
2076
|
+
if label2 == '':
|
2077
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])
|
2078
|
+
else:
|
2079
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])+'('+label2+')'
|
2080
|
+
|
2081
|
+
date_start=df2.index[0]
|
2082
|
+
date_end=df2.index[-1]
|
2083
|
+
if date_range and not date_freq:
|
2084
|
+
ax2.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
2085
|
+
plt.xticks(pd.date_range(date_start,date_end))
|
2086
|
+
if not date_range and date_freq:
|
2087
|
+
ax2.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
2088
|
+
plt.xticks(pd.date_range(freq=date_freq))
|
2089
|
+
if date_range and date_freq:
|
2090
|
+
ax2.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
2091
|
+
plt.xticks(pd.date_range(date_start,date_end,freq=date_freq))
|
2092
|
+
|
2093
|
+
lwadjust=linewidth_adjust(df2)
|
2094
|
+
ax2.plot(df2.index,df2[colname2],'-',label=label2txt, \
|
2095
|
+
linestyle='-.',color=color2,linewidth=lwadjust)
|
2096
|
+
#证券2:绘制数据标签
|
2097
|
+
if datatag2:
|
2098
|
+
for x, y in zip(df2.index, df2[colname2]):
|
2099
|
+
ax2.text(x,y+0.1,'%.2f' % y,ha='center',va='bottom',color='black')
|
2100
|
+
|
2101
|
+
#绘证券2:制趋势线
|
2102
|
+
if power > 0:
|
2103
|
+
lang=check_language()
|
2104
|
+
trend_txt='趋势线'
|
2105
|
+
if lang == 'English':
|
2106
|
+
trend_txt='Trend line'
|
2107
|
+
|
2108
|
+
#生成行号,借此将横轴的日期数量化,以便拟合
|
2109
|
+
df2['id']=range(len(df2))
|
2110
|
+
|
2111
|
+
#设定多项式拟合,power为多项式次数
|
2112
|
+
import numpy as np
|
2113
|
+
parameter = np.polyfit(df2.id, df2[colname2], power)
|
2114
|
+
f = np.poly1d(parameter)
|
2115
|
+
|
2116
|
+
if ticker2 == '':
|
2117
|
+
label2txt=''
|
2118
|
+
else:
|
2119
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])+"("+trend_txt+")"
|
2120
|
+
|
2121
|
+
ax2.plot(df2.index, f(df2.id),"c--", label=label2txt,linewidth=1)
|
2122
|
+
|
2123
|
+
if ticker2 == '':
|
2124
|
+
label2txt=label2
|
2125
|
+
else:
|
2126
|
+
if label2 == "":
|
2127
|
+
label2txt=ticker_name(ticker2,ticker_type_list[1])
|
2128
|
+
else:
|
2129
|
+
label2txt=label2+'('+ticker_name(ticker2,ticker_type_list[1])+')'
|
2130
|
+
ax2.set_ylabel(label2txt,fontsize=ylabel_txt_size)
|
2131
|
+
ax2.legend(loc=loc2,fontsize=legend_txt_size)
|
2132
|
+
|
2133
|
+
# 使用 AutoDateLocator 自动选择最佳间隔,目的是显示最后一个日期,亲测有效!!!
|
2134
|
+
import matplotlib.dates as mdates
|
2135
|
+
ax.xaxis.set_major_locator(mdates.AutoDateLocator(maxticks=maxticks))
|
2136
|
+
ax2.xaxis.set_major_locator(mdates.AutoDateLocator(maxticks=maxticks))
|
2137
|
+
|
2138
|
+
#自动优化x轴标签
|
2139
|
+
#格式化时间轴标注
|
2140
|
+
#plt.gca().xaxis.set_major_formatter(mdate.DateFormatter('%y-%m-%d'))
|
2141
|
+
if not isinstance(maxticks,bool):
|
2142
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
2143
|
+
|
2144
|
+
plt.title(titletxt,fontweight='bold',fontsize=title_txt_size)
|
2145
|
+
plt.show()
|
2146
|
+
|
2147
|
+
return
|
2148
|
+
|
2149
|
+
#==============================================================================
|
2150
|
+
if __name__ =="__main__":
|
2151
|
+
df0=security_trend(['PDD','JD'],annotate=True,graph=False)
|
2152
|
+
|
2153
|
+
y_label=''; x_label='Footnote'
|
2154
|
+
axhline_value=0; axhline_label=''
|
2155
|
+
title_txt='Title'
|
2156
|
+
data_label=False
|
2157
|
+
resample_freq='D'; smooth=True
|
2158
|
+
linewidth=1.5
|
2159
|
+
loc='best'
|
2160
|
+
annotate=True; annotate_value=True
|
2161
|
+
plus_sign=False
|
2162
|
+
attention_value=''; attention_value_area=''
|
2163
|
+
attention_point=''; attention_point_area=''
|
2164
|
+
mark_top=False; mark_bottom=False; mark_end=False
|
2165
|
+
ticker_type='auto'
|
2166
|
+
facecolor='whitesmoke'
|
2167
|
+
|
2168
|
+
|
2169
|
+
def draw_lines(df0,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
2170
|
+
data_label=False,resample_freq='D',smooth=False,linewidth=1.5, \
|
2171
|
+
band_area='',loc='best', \
|
2172
|
+
annotate=False,annotate_value=False,plus_sign=False, \
|
2173
|
+
attention_value='',attention_value_area='', \
|
2174
|
+
attention_point='',attention_point_area='', \
|
2175
|
+
mark_start=False,mark_top=False,mark_bottom=False,mark_end=False, \
|
2176
|
+
ticker_type='auto',facecolor='whitesmoke', \
|
2177
|
+
maxticks_enable=True,maxticks=15, \
|
2178
|
+
translate=False, \
|
2179
|
+
precision=2):
|
2180
|
+
"""
|
2181
|
+
函数功能:根据df的内容绘制折线图
|
2182
|
+
输入参数:
|
2183
|
+
df:数据框。有几个字段就绘制几条折现。必须索引,索引值将作为X轴标记点
|
2184
|
+
要求:df的索引为pandas的datetime日期型
|
2185
|
+
axhline_label: 水平辅助线标记。如果为空值则不绘制水平辅助线
|
2186
|
+
axhline_value: 水平辅助线的y轴位置
|
2187
|
+
y_label:y轴标记
|
2188
|
+
x_label:x轴标记
|
2189
|
+
title_txt:标题。如需多行,中间用\n分割
|
2190
|
+
|
2191
|
+
输出:
|
2192
|
+
绘制折线图
|
2193
|
+
无返回数据
|
2194
|
+
注意:需要日期类型作为df索引
|
2195
|
+
"""
|
2196
|
+
DEBUG=False
|
2197
|
+
if DEBUG:
|
2198
|
+
print(f"band_area={band_area}")
|
2199
|
+
print(f"df0={df0}")
|
2200
|
+
|
2201
|
+
|
2202
|
+
if DEBUG:
|
2203
|
+
print("annotate=",annotate,"annotate_value=",annotate_value)
|
2204
|
+
print("mark_top=",mark_top,"mark_bottom=",mark_bottom)
|
2205
|
+
print(df0)
|
2206
|
+
|
2207
|
+
#空值判断
|
2208
|
+
if len(df0) ==0:
|
2209
|
+
print (" #Warning(draw_lines): no data to plot.")
|
2210
|
+
return
|
2211
|
+
|
2212
|
+
#插值平滑
|
2213
|
+
if smooth and not isinstance(maxticks,bool):
|
2214
|
+
#print(" Rendering graphics ...")
|
2215
|
+
try:
|
2216
|
+
df=df_smooth_manual(df0,resample_freq=resample_freq)
|
2217
|
+
except:
|
2218
|
+
df=df0
|
2219
|
+
else:
|
2220
|
+
df=df0
|
2221
|
+
|
2222
|
+
#取得df字段名列表
|
2223
|
+
collist=df.columns.values.tolist()
|
2224
|
+
if len(collist) > 16:
|
2225
|
+
print (" #Warning(draw_lines): too many columns to draw lines, max 16 lines")
|
2226
|
+
return
|
2227
|
+
|
2228
|
+
lslist=['-','--','-.',':','-','--','-.',':','-','--','-.',':','-','--','-.',':',]
|
2229
|
+
#mklist=[',','d','_','.','o','v','^','<','>','1','2','3','4','s','p','*','h','H','+','x','D']
|
2230
|
+
mklist=[',',',',',',',',',',',',',',',',',',',',',',',',',',',',',',',',',',',',',',',',',']
|
2231
|
+
|
2232
|
+
# 所有字段转换为数值类型,以防万一
|
2233
|
+
for c in collist:
|
2234
|
+
try:
|
2235
|
+
df[c]=df[c].astype('float')
|
2236
|
+
except:
|
2237
|
+
del df[c]
|
2238
|
+
|
2239
|
+
# 计算所有列中的最大最小差距,所有列必须为数值型!
|
2240
|
+
dfmax=df.max().max(); dfmin=df.min().min()
|
2241
|
+
high_low=dfmax - dfmin
|
2242
|
+
|
2243
|
+
# 将末端值最大的排在第一列,优先绘图
|
2244
|
+
dftt=df.T
|
2245
|
+
lastrow=list(dftt)[-1]
|
2246
|
+
dftt.sort_values(lastrow,ascending=False,inplace=True)
|
2247
|
+
df2=dftt.T
|
2248
|
+
|
2249
|
+
# 最上层线标志
|
2250
|
+
firstline=True
|
2251
|
+
|
2252
|
+
#绘制折线图
|
2253
|
+
ax=plt.gca()
|
2254
|
+
print('')
|
2255
|
+
y_end_list=[]
|
2256
|
+
for c in collist:
|
2257
|
+
pos=collist.index(c)
|
2258
|
+
try:
|
2259
|
+
lsc=lslist[pos]
|
2260
|
+
except:
|
2261
|
+
print(" #Bug(draw_lines): lslist=",lslist,",pos=",pos)
|
2262
|
+
mkc=mklist[pos]
|
2263
|
+
|
2264
|
+
# 连接折线中非交易日的断开区间
|
2265
|
+
import pandas as pd
|
2266
|
+
dfg=pd.DataFrame(df2[c]).copy(deep=True)
|
2267
|
+
|
2268
|
+
# 慎用dropna
|
2269
|
+
#dfg=dfg.dropna(inplace=True)
|
2270
|
+
if dfg is None:
|
2271
|
+
print(" #Error(draw_lines): null dataframe for graphics in column",c)
|
2272
|
+
continue
|
2273
|
+
if len(dfg)==0:
|
2274
|
+
print(" #Error(draw_lines): no data for graphics in column",c)
|
2275
|
+
continue
|
2276
|
+
|
2277
|
+
#plt.plot(dfg,label=c,linewidth=linewidth,ls=lsc,marker=mkc,markersize=3)
|
2278
|
+
lwadjust=linewidth_adjust(dfg)
|
2279
|
+
|
2280
|
+
"""
|
2281
|
+
if not annotate:
|
2282
|
+
|
2283
|
+
#注意:许多传入的df字段名已经不是证券代码,此处调用codetranslate将会导致
|
2284
|
+
#股票名称字典重新下载,耗费时间,且出现黄条。
|
2285
|
+
#建议:在调用draw_lines之前,先调用codetranslate,将证券代码翻译为证券名称。
|
2286
|
+
#本函数仅负责绘图,不负责翻译证券名称。
|
2287
|
+
|
2288
|
+
#plt.plot(dfg,label=codetranslate(c),linewidth=linewidth,ls=lsc,marker=mkc,markersize=3)
|
2289
|
+
plt.plot(dfg,label=c,linewidth=lwadjust,ls=lsc,marker=mkc,markersize=3)
|
2290
|
+
else:
|
2291
|
+
#plt.plot(dfg[c],label=codetranslate(c),linewidth=linewidth,ls=lsc,marker=mkc,markersize=3)
|
2292
|
+
#plt.plot(dfg,label=codetranslate(c),linewidth=linewidth,ls=lsc,marker=mkc,markersize=3)
|
2293
|
+
plt.plot(dfg,label=c,linewidth=lwadjust,ls=lsc,marker=mkc,markersize=3)
|
2294
|
+
"""
|
2295
|
+
if not annotate or c in ["平均值","中位数"]:
|
2296
|
+
if c in ["平均值","中位数"]:
|
2297
|
+
clabel=c+str(round(dfg[c].values[0],2))
|
2298
|
+
else:
|
2299
|
+
clabel=c
|
2300
|
+
plt.plot(dfg,label=auto_translate2(clabel,translate),linewidth=lwadjust,ls=lsc,marker=mkc,markersize=3)
|
2301
|
+
else:
|
2302
|
+
plt.plot(dfg,linewidth=lwadjust,ls=lsc,marker=mkc,markersize=3)
|
2303
|
+
|
2304
|
+
lines = plt.gca().lines
|
2305
|
+
last_line_color = lines[-1].get_color()
|
2306
|
+
|
2307
|
+
if annotate and (c not in ["平均值","中位数"]):
|
2308
|
+
mark_end=False
|
2309
|
+
df_end=dfg.tail(1)
|
2310
|
+
# df_end[c]必须为数值类型,否则可能出错
|
2311
|
+
y_end = df_end[c].min() # 末端的y坐标
|
2312
|
+
x_end = df_end[c].idxmin() # 末端值的x坐标
|
2313
|
+
|
2314
|
+
if annotate_value: #在标记曲线名称的同时标记其末端数值
|
2315
|
+
#y1=str(int(y_end)) if y_end >= 100 else str(round(y_end,2))
|
2316
|
+
y1=str(round(y_end,2)) if abs(y_end) >= 100 else str(round(y_end,precision)) if abs(y_end) >= 1 else str(round(y_end,precision))
|
2317
|
+
plt.annotate(text=' '+auto_translate2(c,translate)+': '+y1,
|
2318
|
+
xy=(x_end, y_end),
|
2319
|
+
xytext=(x_end, y_end),fontsize=annotate_size,
|
2320
|
+
color=last_line_color)
|
2321
|
+
else:
|
2322
|
+
plt.annotate(text=' '+auto_translate2(c,translate),
|
2323
|
+
xy=(x_end, y_end),
|
2324
|
+
#xytext=(x_end, y_end),fontsize=9)
|
2325
|
+
xytext=(x_end, y_end),fontsize=annotate_size,
|
2326
|
+
color=last_line_color)
|
2327
|
+
|
2328
|
+
#plt.plot(df[c],label=c,linewidth=1.5,marker=mkc,markersize=3)
|
2329
|
+
#为折线加数据标签
|
2330
|
+
if data_label==True:
|
2331
|
+
mark_top=False; mark_bottom=False; mark_end=False
|
2332
|
+
for a,b in zip(dfg.index,df2[c]):
|
2333
|
+
plt.text(a,b+0.02,str(round(b,2)), \
|
2334
|
+
ha='center',va='bottom',fontsize=7)
|
2335
|
+
|
2336
|
+
#标记最高点/最低点
|
2337
|
+
if (mark_top or mark_bottom) and (c not in ["平均值","中位数"]):
|
2338
|
+
df_mark=dfg[[c]].copy() #避免影响原df
|
2339
|
+
df_mark.dropna(inplace=True)
|
2340
|
+
df_mark.sort_values(by=c,ascending=False,inplace=True)
|
2341
|
+
|
2342
|
+
if mark_top:
|
2343
|
+
df_mark_top=df_mark[:1]
|
2344
|
+
for x, y in zip(df_mark_top.index, df_mark_top[c]):
|
2345
|
+
#y1=round(y+high_low*0.01,2)
|
2346
|
+
y1=y+high_low*0.01
|
2347
|
+
#s='%.0f' if y >= 100 else '%.2f'
|
2348
|
+
s='%.1f' if abs(y) >= 100 else '%.2f' if abs(y) >= 1 else '%.4f'
|
2349
|
+
#plt.text(x,y1,s % y,ha='center',va='bottom',color='red')
|
2350
|
+
plt.text(x,y1,s % y,ha='right',va='bottom',color=last_line_color)
|
2351
|
+
plt.scatter(x,y, color='red',marker='8',s=70)
|
2352
|
+
|
2353
|
+
if mark_bottom:
|
2354
|
+
df_mark_bottom=df_mark[-1:]
|
2355
|
+
for x, y in zip(df_mark_bottom.index, df_mark_bottom[c]):
|
2356
|
+
#y1=round(y-high_low*0.055,2) #标记位置对应y1的底部
|
2357
|
+
y1=y-high_low*0.050 #标记位置对应y1的底部
|
2358
|
+
#s='%.0f' if y >= 100 else '%.2f'
|
2359
|
+
s='%.1f' if abs(y) >= 100 else '%.2f' if abs(y) >= 1 else '%.4f'
|
2360
|
+
#plt.text(x,y1,s % y,ha='center',va='bottom',color='seagreen')
|
2361
|
+
plt.text(x,y1,s % y,ha='right',va='bottom',color=last_line_color)
|
2362
|
+
plt.scatter(x,y, color='seagreen',marker='8',s=70)
|
2363
|
+
|
2364
|
+
#标记曲线开始数值
|
2365
|
+
if mark_start and (c not in ["平均值","中位数"]):
|
2366
|
+
df_end=dfg.head(1)
|
2367
|
+
y_end = df_end[c].min() # 开始的y坐标
|
2368
|
+
x_end = df_end[c].idxmin() # 开始值的x坐标
|
2369
|
+
|
2370
|
+
#y1=str(int(y_end)) if y_end >= 100 else str(round(y_end,2))
|
2371
|
+
y1=str(round(y_end,1)) if abs(y_end) >= 100 else str(round(y_end,precision)) if abs(y_end) >= 1 else str(round(y_end,precision))
|
2372
|
+
plt.annotate(text=' '+y1,
|
2373
|
+
xy=(x_end, y_end),
|
2374
|
+
xytext=(x_end, y_end*0.998),fontsize=annotate_size,
|
2375
|
+
color=last_line_color,ha='right',va='center')
|
2376
|
+
|
2377
|
+
#标记曲线末端数值
|
2378
|
+
if mark_end and (c not in ["平均值","中位数"]):
|
2379
|
+
df_end=dfg.tail(1)
|
2380
|
+
y_end = df_end[c].min() # 末端的y坐标
|
2381
|
+
x_end = df_end[c].idxmin() # 末端值的x坐标
|
2382
|
+
|
2383
|
+
#y1=str(int(y_end)) if y_end >= 100 else str(round(y_end,2))
|
2384
|
+
y1=str(round(y_end,1)) if abs(y_end) >= 100 else str(round(y_end,precision)) if abs(y_end) >= 1 else str(round(y_end,precision))
|
2385
|
+
plt.annotate(text=' '+y1,
|
2386
|
+
xy=(x_end, y_end),
|
2387
|
+
xytext=(x_end, y_end*0.998),fontsize=annotate_size,
|
2388
|
+
color=last_line_color,ha='left',va='center')
|
2389
|
+
|
2390
|
+
#用于关注值的颜色列表
|
2391
|
+
atv_color_list=["lightgray","paleturquoise","wheat","khaki","lightsage","hotpink","mediumslateblue"]
|
2392
|
+
#用于关注点的颜色列表
|
2393
|
+
atp_color_list=["crimson","dodgerblue","magenta","lightseagreen","chocolate","hotpink","mediumslateblue"]
|
2394
|
+
|
2395
|
+
if not attention_value=='':
|
2396
|
+
if isinstance(attention_value,int) or isinstance(attention_value,float):
|
2397
|
+
atv_list=[attention_value]
|
2398
|
+
elif isinstance(attention_value,list):
|
2399
|
+
atv_list=attention_value
|
2400
|
+
else:
|
2401
|
+
atv_list=[]
|
2402
|
+
if not atv_list==[] and not atv_list==['']:
|
2403
|
+
for at in atv_list:
|
2404
|
+
pos=atv_list.index(at)
|
2405
|
+
color=atv_color_list[pos]
|
2406
|
+
plt.axhline(y=at,ls=":",c=color,linewidth=2,label=text_lang("关注值","Attention value ")+str(at))
|
2407
|
+
|
2408
|
+
if not attention_value_area=='':
|
2409
|
+
if isinstance(attention_value_area,list) and len(attention_value_area)>=2:
|
2410
|
+
plt.fill_between(dfg.index,attention_value_area[0],attention_value_area[1],color='lightgray',alpha=0.5)
|
2411
|
+
|
2412
|
+
#绘制曲线之间的带状区域
|
2413
|
+
if DEBUG:
|
2414
|
+
print(f"dfg={dfg}")
|
2415
|
+
|
2416
|
+
if band_area != '' and isinstance(band_area,list) and len(band_area)>=2:
|
2417
|
+
upper_line=band_area[0]; lower_line=band_area[1]
|
2418
|
+
if upper_line not in collist:
|
2419
|
+
upper_line=ectranslate(upper_line)
|
2420
|
+
lower_line=ectranslate(lower_line)
|
2421
|
+
|
2422
|
+
if upper_line not in collist:
|
2423
|
+
upper_line=ticker_name(upper_line)
|
2424
|
+
lower_line=ticker_name(lower_line)
|
2425
|
+
|
2426
|
+
if upper_line not in collist:
|
2427
|
+
upper_line=None
|
2428
|
+
lower_line=None
|
2429
|
+
|
2430
|
+
if not (upper_line is None) and not (lower_line is None):
|
2431
|
+
try:
|
2432
|
+
plt.fill_between(df2.index,df2[upper_line],df2[lower_line],df2[upper_line] > df2[lower_line],color='aquamarine',alpha=0.5,label='',interpolate=True)
|
2433
|
+
plt.fill_between(df2.index,df2[upper_line],df2[lower_line],df2[upper_line] < df2[lower_line],color='lightcoral',alpha=0.5,label='',interpolate=True)
|
2434
|
+
except:
|
2435
|
+
print(f" #Warning(draw_lines): band area elements {upper_line} or {lower_line} not found")
|
2436
|
+
|
2437
|
+
import pandas as pd
|
2438
|
+
from datetime import datetime; date_format="%Y-%m-%d"
|
2439
|
+
from datetime import datetime; date_format="%Y-%m-%d"
|
2440
|
+
if not attention_point=='':
|
2441
|
+
if isinstance(attention_point,str) or isinstance(attention_point,int) or isinstance(attention_point,float):
|
2442
|
+
atp_list=[attention_point]
|
2443
|
+
elif isinstance(attention_point,list):
|
2444
|
+
atp_list=attention_point
|
2445
|
+
else:
|
2446
|
+
atp_list=[]
|
2447
|
+
#去重,不打乱原来的顺序
|
2448
|
+
atp_list=list(dict.fromkeys(atp_list))
|
2449
|
+
|
2450
|
+
if not atp_list==[] and not atp_list==['']:
|
2451
|
+
|
2452
|
+
for at in atp_list:
|
2453
|
+
try:
|
2454
|
+
pos=atp_list.index(at)
|
2455
|
+
color=atp_color_list[pos]
|
2456
|
+
except:
|
2457
|
+
print("*** in draw_lines:")
|
2458
|
+
print("atp_list={0},at={1},pos={2}".format(atp_list,at,pos))
|
2459
|
+
print("atp_color_list={0}".format(atp_color_list))
|
2460
|
+
|
2461
|
+
color=atp_color_list[-1]
|
2462
|
+
|
2463
|
+
try:
|
2464
|
+
#判断是否日期字符串
|
2465
|
+
at=datetime.strptime(at, date_format)
|
2466
|
+
#若是日期字符串
|
2467
|
+
atpd=pd.to_datetime(at)
|
2468
|
+
except:
|
2469
|
+
#不是日期字符串
|
2470
|
+
atpd=at
|
2471
|
+
|
2472
|
+
"""
|
2473
|
+
#纵轴最小值
|
2474
|
+
yaxis_min=plt.ylim()[0]
|
2475
|
+
arrow_dx=0
|
2476
|
+
#各曲线在关注点的最大值
|
2477
|
+
point_max=dfg.loc[atpd].max()
|
2478
|
+
arrow_dy=point_max - yaxis_min
|
2479
|
+
|
2480
|
+
if DEBUG:
|
2481
|
+
print("*** In draw_lines:")
|
2482
|
+
print("dfg.loc[atpd]",dfg.loc[atpd].values)
|
2483
|
+
print("yaxis_min=",yaxis_min)
|
2484
|
+
print("point_max=",point_max)
|
2485
|
+
print("arrow_dy=",arrow_dy)
|
2486
|
+
|
2487
|
+
plt.arrow(atpd,yaxis_min,arrow_dx,arrow_dy,ls=':',lw=2,fc=color,ec=color,alpha=0.5,shape='full', \
|
2488
|
+
width=0.05,length_includes_head=True)
|
2489
|
+
"""
|
2490
|
+
|
2491
|
+
try:
|
2492
|
+
at_str=atpd.strftime('%Y-%m-%d')
|
2493
|
+
except:
|
2494
|
+
at_str=atpd
|
2495
|
+
#plt.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+str(at))
|
2496
|
+
plt.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+at_str)
|
2497
|
+
|
2498
|
+
if not attention_point_area=='':
|
2499
|
+
if isinstance(attention_point_area,list) and len(attention_point_area)>=2:
|
2500
|
+
apa_list=[]
|
2501
|
+
for ap in attention_point_area:
|
2502
|
+
try:
|
2503
|
+
ap=datetime.strptime(ap, date_format)
|
2504
|
+
appd=pd.to_datetime(ap)
|
2505
|
+
except:
|
2506
|
+
appd=ap
|
2507
|
+
apa_list=apa_list+[appd]
|
2508
|
+
|
2509
|
+
yaxis_data=plt.ylim()
|
2510
|
+
plt.fill_betweenx(yaxis_data,apa_list[0],apa_list[1],color='powderblue',alpha=0.5)
|
2511
|
+
|
2512
|
+
#绘制水平辅助线
|
2513
|
+
if axhline_label !="":
|
2514
|
+
if '零线' in axhline_label:
|
2515
|
+
axhline_label=''
|
2516
|
+
|
2517
|
+
max_values = df2.max(numeric_only=True); global_max_values=max_values.max()
|
2518
|
+
min_values = df2.min(numeric_only=True); global_min_values=min_values.min()
|
2519
|
+
if global_max_values >= axhline_value and global_min_values <= axhline_value:
|
2520
|
+
plt.axhline(y=axhline_value,label=auto_translate2(axhline_label,translate),color='black',linestyle=':',linewidth=2)
|
2521
|
+
#plt.axhline(y=axhline_value,color='purple',linestyle=':',linewidth=1.5)
|
2522
|
+
|
2523
|
+
#坐标轴标记
|
2524
|
+
y_label_t=ectranslate(y_label)
|
2525
|
+
plt.ylabel(auto_translate2(y_label_t,translate),fontweight='bold',fontsize=ylabel_txt_size)
|
2526
|
+
|
2527
|
+
x_label_t=ectranslate(x_label)
|
2528
|
+
if x_label != "":
|
2529
|
+
plt.xlabel(auto_translate2(x_label_t,translate),fontweight='bold',fontsize=xlabel_txt_size,ha='center')
|
2530
|
+
#图示标题
|
2531
|
+
plt.title(auto_translate2(title_txt,translate),fontweight='bold',fontsize=title_txt_size)
|
2532
|
+
|
2533
|
+
if not isinstance(maxticks,bool):
|
2534
|
+
if maxticks_enable:
|
2535
|
+
# 使用 AutoDateLocator 自动选择最佳间隔,目的是显示最后一个日期,亲测有效!!!
|
2536
|
+
import matplotlib.dates as mdates
|
2537
|
+
ax.xaxis.set_major_locator(mdates.AutoDateLocator(maxticks=maxticks))
|
2538
|
+
|
2539
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
2540
|
+
try:
|
2541
|
+
plt.gca().set_facecolor(facecolor)
|
2542
|
+
except:
|
2543
|
+
print(" #Warning(draw_lines): color",facecolor,"is unsupported, changed to default setting")
|
2544
|
+
plt.gca().set_facecolor("whitesmoke")
|
2545
|
+
|
2546
|
+
# 若不绘制annotate,则绘制图例
|
2547
|
+
#if not annotate:
|
2548
|
+
#检查是否存在可绘制的标签,若有则绘制
|
2549
|
+
if DEBUG:
|
2550
|
+
have_label=False
|
2551
|
+
for line in plt.gca().lines:
|
2552
|
+
print(f" DEBUG: plt.gca().lines, line={line.get_label()}")
|
2553
|
+
if line.get_label() != '':
|
2554
|
+
have_label=True
|
2555
|
+
|
2556
|
+
#plt.legend没有图例标签时会提示信息No artists...
|
2557
|
+
if not annotate or attention_value !='' or attention_point !='':
|
2558
|
+
plt.legend(loc=loc,fontsize=legend_txt_size)
|
2559
|
+
|
2560
|
+
"""
|
2561
|
+
if any([line.get_label() != '' for line in plt.gca().lines]):
|
2562
|
+
tuli=plt.legend(loc=loc,fontsize=legend_txt_size)
|
2563
|
+
if DEBUG:
|
2564
|
+
print(f" DEBUG: result of plt.legend is {tuli}")
|
2565
|
+
"""
|
2566
|
+
if plus_sign:
|
2567
|
+
# 尝试改变纵轴刻度格式:给正数添加正号+,以便突出显示增减幅度
|
2568
|
+
import matplotlib.ticker as ticker
|
2569
|
+
ax = plt.gca()
|
2570
|
+
bare0 = lambda y, pos: ('%+g' if y>0 else '%g')%y
|
2571
|
+
#bare0 = lambda y, pos: ('%+g%' if y>0 else '%g%')%y #此处%为全角,无法适配英文环境,放弃!
|
2572
|
+
ax.yaxis.set_major_formatter(ticker.FuncFormatter(bare0))
|
2573
|
+
|
2574
|
+
plt.show()
|
2575
|
+
|
2576
|
+
return
|
2577
|
+
|
2578
|
+
if __name__=='__main__':
|
2579
|
+
title_txt="Stock Risk \nCAPM Beta Trends"
|
2580
|
+
draw_lines(df,"market line",1.0,"Beta coefficient","",title_txt)
|
2581
|
+
|
2582
|
+
#==============================================================================
|
2583
|
+
def draw_lines2(df0,y_label,x_label,axhline_value,axhline_label,title_txt, \
|
2584
|
+
data_label=False,resample_freq='1D',smooth=True, \
|
2585
|
+
date_range=False,date_freq=False,date_fmt='%Y-%m-%d', \
|
2586
|
+
colorlist=[],lslist=[],lwlist=[], \
|
2587
|
+
#指定纵轴两个变量之间的区域
|
2588
|
+
band_area='',loc='best', \
|
2589
|
+
attention_value='', \
|
2590
|
+
#指定纵轴两个数值之间的区域
|
2591
|
+
attention_value_area='', \
|
2592
|
+
attention_point='', \
|
2593
|
+
#指定两个横轴之间的区域
|
2594
|
+
attention_point_area='', \
|
2595
|
+
annotate=False,annotate_value=False, \
|
2596
|
+
mark_start=False,mark_top=False,mark_bottom=False,mark_end=False, \
|
2597
|
+
facecolor='whitesmoke',maxticks=20,translate=False):
|
2598
|
+
"""
|
2599
|
+
函数功能:根据df的内容绘制折线图
|
2600
|
+
输入参数:
|
2601
|
+
df:数据框。有几个字段就绘制几条折现。必须索引,索引值将作为X轴标记点
|
2602
|
+
要求:df的索引为pandas的datetime日期型
|
2603
|
+
axhline_label: 水平辅助线标记。如果为空值则不绘制水平辅助线
|
2604
|
+
axhline_value: 水平辅助线的y轴位置
|
2605
|
+
y_label:y轴标记
|
2606
|
+
x_label:x轴标记
|
2607
|
+
title_txt:标题。如需多行,中间用\n分割
|
2608
|
+
|
2609
|
+
smooth=True:默认进行曲线平滑处理,对于部分长期停牌的股票/债券,应选择不进行平滑处理False,否则曲线会严重失真。
|
2610
|
+
|
2611
|
+
输出:
|
2612
|
+
绘制折线图
|
2613
|
+
无返回数据
|
2614
|
+
注意:需要日期类型作为df索引
|
2615
|
+
|
2616
|
+
band_area='':默认为空,否则为列表,第1个值为带状区域上边沿字段,第2个值为带状区域下边沿字段
|
2617
|
+
"""
|
2618
|
+
import pandas as pd
|
2619
|
+
|
2620
|
+
DEBUG=False
|
2621
|
+
if DEBUG:
|
2622
|
+
print(f"band_area={band_area}")
|
2623
|
+
print(f"df0={df0}")
|
2624
|
+
|
2625
|
+
#空值判断
|
2626
|
+
if len(df0) ==0:
|
2627
|
+
print (" #Warning(draw_lines): no data to plot.")
|
2628
|
+
return
|
2629
|
+
|
2630
|
+
#插值平滑
|
2631
|
+
if smooth and not isinstance(maxticks,bool):
|
2632
|
+
print(" Smoothening curves ...")
|
2633
|
+
try:
|
2634
|
+
df=df_smooth_manual(df0,resample_freq=resample_freq)
|
2635
|
+
except:
|
2636
|
+
df=df0
|
2637
|
+
else:
|
2638
|
+
df=df0
|
2639
|
+
|
2640
|
+
# 所有字段转换为数值类型,以防万一
|
2641
|
+
for c in list(df):
|
2642
|
+
try:
|
2643
|
+
df[c]=df[c].astype('float')
|
2644
|
+
except:
|
2645
|
+
del df[c]
|
2646
|
+
|
2647
|
+
# 计算所有列中的最大最小差距,假设所有列均为数值型!
|
2648
|
+
dfmax=df.max().max(); dfmin=df.min().min()
|
2649
|
+
high_low=dfmax - dfmin
|
2650
|
+
|
2651
|
+
#定义横轴标签:显示完整开始、结束日期
|
2652
|
+
if not isinstance(maxticks,bool):
|
2653
|
+
ax=plt.gca()
|
2654
|
+
date_start=df.index[0]
|
2655
|
+
date_end=df.index[-1]
|
2656
|
+
if date_range and not date_freq:
|
2657
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
2658
|
+
plt.xticks(pd.date_range(date_start,date_end))
|
2659
|
+
if not date_range and date_freq:
|
2660
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
2661
|
+
plt.xticks(pd.date_range(freq=date_freq))
|
2662
|
+
if date_range and date_freq:
|
2663
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
2664
|
+
plt.xticks(pd.date_range(date_start,date_end,freq=date_freq))
|
2665
|
+
|
2666
|
+
#取得df字段名列表
|
2667
|
+
collist=df.columns.values.tolist()
|
2668
|
+
collist3=collist[:3] #专用于绘制布林带,取前3个字段
|
2669
|
+
|
2670
|
+
if lslist==[]:
|
2671
|
+
lslist=['-','--','-.',':','-','--','-.',':','-','--','-.',':','-','--','-.',':',]
|
2672
|
+
if colorlist==[]:
|
2673
|
+
colorlist=['blue','tomato','green','chocolate','darksage','cyan','blueviolet','violet','darkcyan','gold','wheat','silver','darkred','brown','coral','pink',]
|
2674
|
+
|
2675
|
+
print('')
|
2676
|
+
#绘制折线图
|
2677
|
+
for c in collist:
|
2678
|
+
pos=collist.index(c)
|
2679
|
+
try:
|
2680
|
+
lcolor=colorlist[pos]
|
2681
|
+
except:
|
2682
|
+
lcolor=''
|
2683
|
+
try:
|
2684
|
+
lls=lslist[pos]
|
2685
|
+
except:
|
2686
|
+
lls=''
|
2687
|
+
try:
|
2688
|
+
llw=lwlist[pos]
|
2689
|
+
except:
|
2690
|
+
llw=linewidth_adjust(df)
|
2691
|
+
|
2692
|
+
if (lcolor !='') and (lls !=''):
|
2693
|
+
if not annotate:
|
2694
|
+
plt.plot(df[c],label=auto_translate2(c,translate),linewidth=llw,ls=lls,color=lcolor)
|
2695
|
+
else:
|
2696
|
+
plt.plot(df[c],linewidth=llw,ls=lls,color=lcolor)
|
2697
|
+
elif (lcolor !=''):
|
2698
|
+
if not annotate:
|
2699
|
+
plt.plot(df[c],label=auto_translate2(c,translate),linewidth=llw,color=lcolor)
|
2700
|
+
else:
|
2701
|
+
plt.plot(df[c],linewidth=llw,color=lcolor)
|
2702
|
+
else:
|
2703
|
+
if not annotate:
|
2704
|
+
plt.plot(df[c],label=auto_translate2(c,translate),linewidth=llw)
|
2705
|
+
else:
|
2706
|
+
plt.plot(df[c],linewidth=llw)
|
2707
|
+
|
2708
|
+
lines = plt.gca().lines; last_line_color = lines[-1].get_color()
|
2709
|
+
|
2710
|
+
#为折线加数据标签
|
2711
|
+
if data_label==True:
|
2712
|
+
mark_top=False; mark_bottom=False; mark_end=False
|
2713
|
+
for a,b in zip(df.index,df[c]):
|
2714
|
+
plt.text(a,b+0.02,str(round(b,2)), \
|
2715
|
+
ha='center',va='bottom',fontsize=7)
|
2716
|
+
|
2717
|
+
#标记最高点/最低点
|
2718
|
+
if mark_top or mark_bottom:
|
2719
|
+
df_mark=df[[c]].copy() #避免影响原df
|
2720
|
+
df_mark.dropna(inplace=True)
|
2721
|
+
df_mark.sort_values(by=c,ascending=False,inplace=True)
|
2722
|
+
|
2723
|
+
if mark_top:
|
2724
|
+
df_mark_top=df_mark[:1]
|
2725
|
+
for x, y in zip(df_mark_top.index, df_mark_top[c]):
|
2726
|
+
#y1=round(y+high_low*0.01,2)
|
2727
|
+
y1=y+high_low*0.01
|
2728
|
+
#s='%.0f' if y >= 100 else '%.2f'
|
2729
|
+
s='%.1f' if abs(y) >= 100 else '%.2f' if abs(y) >= 1 else '%.4f'
|
2730
|
+
#plt.text(x,y1,s % y,ha='center',va='bottom',color='red')
|
2731
|
+
plt.text(x,y1,s % y,ha='right',va='bottom',color=last_line_color)
|
2732
|
+
plt.scatter(x,y, color='red',marker='8',s=70)
|
2733
|
+
|
2734
|
+
if mark_bottom:
|
2735
|
+
df_mark_bottom=df_mark[-1:]
|
2736
|
+
for x, y in zip(df_mark_bottom.index, df_mark_bottom[c]):
|
2737
|
+
#y1=round(y-high_low*0.055,2) #标记位置对应y1的底部
|
2738
|
+
y1=y-high_low*0.050 #标记位置对应y1的底部
|
2739
|
+
#s='%.0f' if y >= 100 else '%.2f'
|
2740
|
+
s='%.1f' if abs(y) >= 100 else '%.2f' if abs(y) >= 1 else '%.4f'
|
2741
|
+
#plt.text(x,y1,s % y,ha='center',va='bottom',color='seagreen')
|
2742
|
+
plt.text(x,y1,s % y,ha='right',va='bottom',color=last_line_color)
|
2743
|
+
plt.scatter(x,y, color='seagreen',marker='8',s=70)
|
2744
|
+
|
2745
|
+
#曲线末端标记:不建议用于布林带
|
2746
|
+
if annotate:
|
2747
|
+
df_end=df.tail(1)
|
2748
|
+
y_end = df_end[c].min() # 末端的y坐标
|
2749
|
+
x_end = df_end[c].idxmin() # 末端值的x坐标
|
2750
|
+
|
2751
|
+
if annotate_value: #在标记曲线名称的同时标记其末端数值
|
2752
|
+
#y1=str(int(y_end)) if y_end >= 100 else str(round(y_end,2))
|
2753
|
+
y1=str(int(y_end)) if abs(y_end) >= 100 else str(round(y_end,2)) if abs(y_end) >= 10 else str(round(y_end,4))
|
2754
|
+
plt.annotate(text=auto_translate2(c,translate)+': '+y1,
|
2755
|
+
xy=(x_end, y_end),
|
2756
|
+
xytext=(x_end, y_end),fontsize=annotate_size,
|
2757
|
+
color=last_line_color)
|
2758
|
+
else:
|
2759
|
+
plt.annotate(text=auto_translate2(c,translate),
|
2760
|
+
xy=(x_end, y_end),
|
2761
|
+
xytext=(x_end, y_end),fontsize=annotate_size,
|
2762
|
+
color=last_line_color)
|
2763
|
+
|
2764
|
+
#标记曲线开始数值
|
2765
|
+
if mark_start:
|
2766
|
+
df_start=df.head(1)
|
2767
|
+
y_start = df_start[c].min() # 开始的y坐标
|
2768
|
+
x_start = df_start[c].idxmin() # 开始值的x坐标
|
2769
|
+
|
2770
|
+
#y1=str(int(y_end)) if y_end >= 100 else str(round(y_end,2))
|
2771
|
+
y1=str(round(y_start,1)) if abs(y_start) >= 100 else str(round(y_start,2)) if abs(y_start) >= 1 else str(round(y_start,3))
|
2772
|
+
plt.annotate(text=y1,
|
2773
|
+
xy=(x_start, y_start),
|
2774
|
+
xytext=(x_start, y_start*0.998),fontsize=annotate_size,ha='right',va='center')
|
2775
|
+
# 特别注意:这里的left/right与实际图示的方向正好相反!!!
|
2776
|
+
|
2777
|
+
#处理布林带的mark_end,仅标记上中下线
|
2778
|
+
if mark_end & (c in collist3):
|
2779
|
+
df_end=df.tail(1)
|
2780
|
+
y_end = df_end[c].min() # 末端的y坐标
|
2781
|
+
x_end = df_end[c].idxmin() # 末端值的x坐标
|
2782
|
+
|
2783
|
+
#y1=str(int(y_end)) if y_end >= 100 else str(round(y_end,2))
|
2784
|
+
y1=str(round(y_end,1)) if abs(y_end) >= 100 else str(round(y_end,2)) if abs(y_end) >= 1 else str(round(y_end,3))
|
2785
|
+
plt.annotate(text=y1,
|
2786
|
+
xy=(x_end, y_end),
|
2787
|
+
xytext=(x_end, y_end),fontsize=annotate_size,
|
2788
|
+
color=last_line_color)
|
2789
|
+
|
2790
|
+
#绘制带状区域
|
2791
|
+
if band_area != '' and isinstance(band_area,list) and len(band_area)>=2:
|
2792
|
+
upper_line=band_area[0]; lower_line=band_area[1]
|
2793
|
+
if upper_line not in collist:
|
2794
|
+
upper_line=ectranslate(upper_line)
|
2795
|
+
lower_line=ectranslate(lower_line)
|
2796
|
+
|
2797
|
+
if upper_line not in collist:
|
2798
|
+
upper_line=ticker_name(upper_line)
|
2799
|
+
lower_line=ticker_name(lower_line)
|
2800
|
+
|
2801
|
+
if upper_line not in collist:
|
2802
|
+
upper_line=None
|
2803
|
+
lower_line=None
|
2804
|
+
|
2805
|
+
if not (upper_line is None) and not (lower_line is None):
|
2806
|
+
try:
|
2807
|
+
"""
|
2808
|
+
plt.fill_between(df.index,df[upper_line],df[lower_line],color='moccasin',alpha=0.5,label='',where=df[upper_line]>=df[lower_line])
|
2809
|
+
plt.fill_between(df.index,df[upper_line],df[lower_line],color='aquamarine',alpha=0.5,label='',where=df[upper_line]<=df[lower_line])
|
2810
|
+
"""
|
2811
|
+
#plt.fill_between(df.index,df[upper_line],df[lower_line],color='aquamarine',alpha=0.5,label='')
|
2812
|
+
plt.fill_between(df.index,df[upper_line],df[lower_line],df[upper_line] > df[lower_line],color='aquamarine',alpha=0.5,label='',interpolate=True)
|
2813
|
+
plt.fill_between(df.index,df[upper_line],df[lower_line],df[upper_line] < df[lower_line],color='lightcoral',alpha=0.5,label='',interpolate=True)
|
2814
|
+
#plt.fill_between(df.index,df[upper_line],df[lower_line],df[upper_line] == df[lower_line],color='lightgray',alpha=0.5,label='')
|
2815
|
+
except:
|
2816
|
+
print(f" #Warning(draw_lines2): lack of data for either {upper_line} or {lower_line}")
|
2817
|
+
#return None
|
2818
|
+
|
2819
|
+
#绘制水平辅助线
|
2820
|
+
if axhline_label !="":
|
2821
|
+
if DEBUG:
|
2822
|
+
print("DEBUG: draw axhline_label=",axhline_label)
|
2823
|
+
|
2824
|
+
if "零线" in axhline_label:
|
2825
|
+
plt.axhline(y=axhline_value,color='black',linestyle='--',linewidth=2)
|
2826
|
+
else:
|
2827
|
+
plt.axhline(y=axhline_value,label=axhline_label,color='black',linestyle='--',linewidth=2)
|
2828
|
+
|
2829
|
+
#用于关注值的颜色列表
|
2830
|
+
atv_color_list=["lightgray","paleturquoise","wheat","khaki","lightsage"]
|
2831
|
+
#用于关注点的颜色列表
|
2832
|
+
atp_color_list=["crimson","dodgerblue","magenta","lightseagreen","chocolate"]
|
2833
|
+
|
2834
|
+
if not attention_value=='':
|
2835
|
+
if isinstance(attention_value,int) or isinstance(attention_value,float):
|
2836
|
+
atv_list=[attention_value]
|
2837
|
+
elif isinstance(attention_value,list):
|
2838
|
+
atv_list=attention_value
|
2839
|
+
else:
|
2840
|
+
atv_list=[]
|
2841
|
+
if not atv_list==[] and not atv_list==['']:
|
2842
|
+
for at in atv_list:
|
2843
|
+
pos=atv_list.index(at)
|
2844
|
+
color=atv_color_list[pos]
|
2845
|
+
plt.axhline(y=at,ls=":",c=color,linewidth=2,label=text_lang("关注值","Attention value ")+str(at))
|
2846
|
+
|
2847
|
+
if not attention_value_area=='':
|
2848
|
+
if isinstance(attention_value_area,list) and len(attention_value_area)>=2:
|
2849
|
+
plt.fill_between(df.index,attention_value_area[0],attention_value_area[1],color='lightgray',alpha=0.5)
|
2850
|
+
|
2851
|
+
import pandas as pd
|
2852
|
+
from datetime import datetime; date_format="%Y-%m-%d"
|
2853
|
+
if not attention_point=='':
|
2854
|
+
if isinstance(attention_point,str) or isinstance(attention_point,int) or isinstance(attention_point,float):
|
2855
|
+
atp_list=[attention_point]
|
2856
|
+
elif isinstance(attention_point,list):
|
2857
|
+
atp_list=attention_point
|
2858
|
+
else:
|
2859
|
+
atp_list=[]
|
2860
|
+
#去重,不打乱原来的顺序
|
2861
|
+
atp_list=list(dict.fromkeys(atp_list))
|
2862
|
+
|
2863
|
+
if not atp_list==[] and not atp_list==['']:
|
2864
|
+
for at in atp_list:
|
2865
|
+
pos=atp_list.index(at)
|
2866
|
+
color=atp_color_list[pos]
|
2867
|
+
|
2868
|
+
#判断是否日期字符串
|
2869
|
+
try:
|
2870
|
+
at=datetime.strptime(at, date_format)
|
2871
|
+
atpd=pd.to_datetime(at)
|
2872
|
+
except:
|
2873
|
+
atpd=at
|
2874
|
+
|
2875
|
+
try:
|
2876
|
+
at_str=atpd.strftime('%Y-%m-%d')
|
2877
|
+
except:
|
2878
|
+
at_str=atpd
|
2879
|
+
|
2880
|
+
#plt.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+str(at))
|
2881
|
+
plt.axvline(x=atpd,ls=":",c=color,linewidth=1.5,label=text_lang("关注点","Attention point ")+at_str)
|
2882
|
+
|
2883
|
+
if not attention_point_area=='':
|
2884
|
+
if isinstance(attention_point_area,list) and len(attention_point_area)>=2:
|
2885
|
+
apa_list=[]
|
2886
|
+
for ap in attention_point_area:
|
2887
|
+
try:
|
2888
|
+
ap=datetime.strptime(ap, date_format)
|
2889
|
+
appd=pd.to_datetime(ap)
|
2890
|
+
except:
|
2891
|
+
appd=ap
|
2892
|
+
apa_list=apa_list+[appd]
|
2893
|
+
|
2894
|
+
yaxis_data=plt.ylim()
|
2895
|
+
plt.fill_betweenx(yaxis_data,apa_list[0],apa_list[1],color='powderblue',alpha=0.5)
|
2896
|
+
|
2897
|
+
#坐标轴标记
|
2898
|
+
plt.ylabel(auto_translate2(y_label,translate),fontweight='bold',fontsize=ylabel_txt_size)
|
2899
|
+
if x_label != "":
|
2900
|
+
plt.xlabel(auto_translate2(x_label,translate),fontweight='bold',fontsize=xlabel_txt_size,ha='center')
|
2901
|
+
#图示标题
|
2902
|
+
plt.title(auto_translate2(title_txt,translate),fontweight='bold',fontsize=title_txt_size)
|
2903
|
+
|
2904
|
+
# 使用 AutoDateLocator 自动选择最佳间隔,目的是显示最后一个日期,亲测有效!!!
|
2905
|
+
if not isinstance(maxticks,bool):
|
2906
|
+
import matplotlib.dates as mdates
|
2907
|
+
ax.xaxis.set_major_locator(mdates.AutoDateLocator(maxticks=maxticks))
|
2908
|
+
|
2909
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
2910
|
+
try:
|
2911
|
+
plt.gca().set_facecolor(facecolor)
|
2912
|
+
except:
|
2913
|
+
print(" #Warning(draw_lines2): color",facecolor,"is unsupported, changed to default setting")
|
2914
|
+
plt.gca().set_facecolor("whitesmoke")
|
2915
|
+
|
2916
|
+
#if not annotate:
|
2917
|
+
#plt.legend没有图例标签时会提示信息No artists...
|
2918
|
+
if not annotate or attention_value !='' or attention_point !='':
|
2919
|
+
plt.legend(loc=loc,fontsize=legend_txt_size)
|
2920
|
+
|
2921
|
+
#设置绘图风格:关闭网格虚线
|
2922
|
+
plt.rcParams['axes.grid']=False
|
2923
|
+
|
2924
|
+
plt.show()
|
2925
|
+
|
2926
|
+
return
|
2927
|
+
|
2928
|
+
#==============================================================================
|
2929
|
+
def plot_barh(df,colname,titletxt,footnote,datatag=True, \
|
2930
|
+
colors=['r','g','b','c','m','y','aquamarine','dodgerblue', \
|
2931
|
+
'deepskyblue','silver'],tag_offset=0.01,axisamp=1.3, \
|
2932
|
+
facecolor='whitesmoke'):
|
2933
|
+
"""
|
2934
|
+
功能:绘制水平单值柱状图,并可标注数据标签。
|
2935
|
+
输入:数据集df;列名colname;标题titletxt;脚注footnote;
|
2936
|
+
是否绘制数据标签datatag,默认是;柱状图柱子色彩列表。
|
2937
|
+
输出:水平柱状图
|
2938
|
+
"""
|
2939
|
+
#空值判断
|
2940
|
+
if len(df) ==0:
|
2941
|
+
print (" #Warning(plot_barh): no data to plot.")
|
2942
|
+
return
|
2943
|
+
|
2944
|
+
plt.barh(df.index,df[colname],align='center',color=colors,alpha=0.8)
|
2945
|
+
coltxt=ectranslate(colname)
|
2946
|
+
plt.xlabel(footnote,fontsize=xlabel_txt_size,ha='center')
|
2947
|
+
plt.title(titletxt,fontweight='bold',fontsize=title_txt_size)
|
2948
|
+
|
2949
|
+
#xmin=int(min(df[colname]))
|
2950
|
+
xmin0=min(df[colname])
|
2951
|
+
if xmin0 > 0:
|
2952
|
+
xmin=xmin0*0.8
|
2953
|
+
else:
|
2954
|
+
xmin=xmin0*1.05
|
2955
|
+
#xmax=(int(max(df[colname]))+1)*1.1
|
2956
|
+
xmax0=max(df[colname])
|
2957
|
+
if not (xmax0 == 0):
|
2958
|
+
scale_max=abs((xmax0-xmin0)/xmax0)*axisamp #经验值放大倍数
|
2959
|
+
xmax=xmax0*scale_max
|
2960
|
+
else:
|
2961
|
+
scale_max=abs((xmax0-xmin0))*axisamp
|
2962
|
+
xmax=xmax0+scale_max
|
2963
|
+
|
2964
|
+
"""
|
2965
|
+
if xmax0 > 0:
|
2966
|
+
xmax=xmax0*1.8
|
2967
|
+
else:
|
2968
|
+
xmax=xmax0*1.2
|
2969
|
+
"""
|
2970
|
+
plt.xlim([xmin,xmax])
|
2971
|
+
|
2972
|
+
tag_off=tag_offset * xmax
|
2973
|
+
for x,y in enumerate(list(df[colname])):
|
2974
|
+
#plt.text(y+0.1,x,'%s' % y,va='center')
|
2975
|
+
plt.text(y+tag_off,x,'%s' % y,va='center')
|
2976
|
+
|
2977
|
+
"""
|
2978
|
+
yticklist=list(df.index)
|
2979
|
+
yticknames=[]
|
2980
|
+
for yt in yticklist:
|
2981
|
+
ytname=codetranslate(yt)
|
2982
|
+
yticknames=yticknames+[ytname]
|
2983
|
+
"""
|
2984
|
+
yticknames=list(df.index)
|
2985
|
+
plt.yticks(df.index,yticknames)
|
2986
|
+
|
2987
|
+
try:
|
2988
|
+
plt.gca().set_facecolor(facecolor)
|
2989
|
+
except:
|
2990
|
+
print(" #Warning(plot_barh): color",facecolor,"is unsupported, changed to default setting")
|
2991
|
+
plt.gca().set_facecolor("whitesmoke")
|
2992
|
+
|
2993
|
+
plt.show(); plt.close()
|
2994
|
+
|
2995
|
+
return
|
2996
|
+
|
2997
|
+
#==============================================================================
|
2998
|
+
if __name__=='__main__':
|
2999
|
+
import pandas as pd
|
3000
|
+
df = pd.read_excel('S:/QTEMP/px_test.xlsx',header=0, index_col=0)
|
3001
|
+
|
3002
|
+
colname='Exp Ret%'
|
3003
|
+
titletxt="This is a title"
|
3004
|
+
footnote="This is a footnote"
|
3005
|
+
|
3006
|
+
def plot_barh2(df,colname,titletxt,footnote,facecolor='lightblue'):
|
3007
|
+
"""
|
3008
|
+
功能:绘制水平单值柱状图,并在外侧标注数据标签。
|
3009
|
+
输入:数据集df;列名colname;标题titletxt;脚注footnote;
|
3010
|
+
输出:水平柱状图
|
3011
|
+
注意:在Spyder中可能工作不正常,使用plotly_express.bar
|
3012
|
+
"""
|
3013
|
+
#空值判断
|
3014
|
+
if len(df) ==0:
|
3015
|
+
print (" #Warning(plot_barh): no data to plot.")
|
3016
|
+
return
|
3017
|
+
|
3018
|
+
#改造df
|
3019
|
+
df['ycolname']=df.index
|
3020
|
+
df['xcolname']=df[colname]
|
3021
|
+
xlabel=colname+'颜色棒'
|
3022
|
+
df[xlabel]=df[colname]
|
3023
|
+
|
3024
|
+
#import plotly_express as px
|
3025
|
+
import plotly.express as px
|
3026
|
+
|
3027
|
+
fig=px.bar(data_frame = df,
|
3028
|
+
y='ycolname', #纵轴绘制的字段
|
3029
|
+
x=colname, #横轴绘制的字段
|
3030
|
+
color=xlabel, #基于df中xlabel字段的数值大小配色,并将xlabel作为颜色棒顶部的标注
|
3031
|
+
orientation='h', #绘制水平直方图
|
3032
|
+
text=colname, #在直方图顶端标数字
|
3033
|
+
labels={'ycolname':'',colname:footnote,xlabel:''} #将字段改名作为纵轴、横轴或颜色棒的标注
|
3034
|
+
)
|
3035
|
+
|
3036
|
+
fig.update_coloraxes(showscale=False) # 隐藏颜色条
|
3037
|
+
|
3038
|
+
fig.update_traces(textposition='outside',#直方图顶端的数值标在外侧
|
3039
|
+
)
|
3040
|
+
|
3041
|
+
fig.update_layout(
|
3042
|
+
title={
|
3043
|
+
'text': titletxt, # 标题名称
|
3044
|
+
'y':0.95, # 位置,坐标轴的长度看做1
|
3045
|
+
'x':0.5,
|
3046
|
+
'xanchor': 'center', # 相对位置
|
3047
|
+
'yanchor': 'top'},
|
3048
|
+
plot_bgcolor=facecolor, #设置画布背景颜色
|
3049
|
+
coloraxis_showscale=False, #彻底移除颜色条,需要升级plotly!
|
3050
|
+
)
|
3051
|
+
|
3052
|
+
fig.show()
|
3053
|
+
|
3054
|
+
return
|
3055
|
+
|
3056
|
+
if __name__=='__main__':
|
3057
|
+
plot_barh2(df,colname,titletxt,footnote)
|
3058
|
+
#==============================================================================
|
3059
|
+
|
3060
|
+
#==============================================================================
|
3061
|
+
#==============================================================================
|
3062
|
+
def plot_2lines(df01,colname1,label1, \
|
3063
|
+
df02,colname2,label2, \
|
3064
|
+
ylabeltxt,titletxt,footnote,hline=0,vline=0,resample_freq='D', \
|
3065
|
+
date_range=False,date_freq=False,date_fmt='%Y-%m-%d', \
|
3066
|
+
facecolor='whitesmoke', \
|
3067
|
+
maxticks=15):
|
3068
|
+
"""
|
3069
|
+
功能:绘制两个证券的折线图。如果hline=0不绘制水平虚线,vline=0不绘制垂直虚线
|
3070
|
+
假定:数据表有日期索引,且已经按照索引排序
|
3071
|
+
输入:
|
3072
|
+
证券1:数据表df1,列名1,列名标签1;
|
3073
|
+
证券2:数据表df2,列名2,列名标签2;
|
3074
|
+
标题titletxt,脚注footnote
|
3075
|
+
输出:绘制同轴折线图
|
3076
|
+
|
3077
|
+
若date_range=True,尽量在横轴上标注日期的起止时间
|
3078
|
+
若date_freq不为False,可以为类似于'3m'或'1Y'等
|
3079
|
+
date_fmt可以为'%Y-%m-%d'或'%Y-%m'或'%Y'等
|
3080
|
+
|
3081
|
+
返回值:无
|
3082
|
+
"""
|
3083
|
+
import pandas as pd
|
3084
|
+
|
3085
|
+
#空值判断
|
3086
|
+
if len(df01) ==0:
|
3087
|
+
print (" #Warning(plot_2lines): no data to plot df01.")
|
3088
|
+
if len(df02) ==0:
|
3089
|
+
print (" #Warning(plot_2lines): no data to plot df02.")
|
3090
|
+
if (len(df01) ==0) and (len(df02) ==0):
|
3091
|
+
return
|
3092
|
+
|
3093
|
+
#插值平滑
|
3094
|
+
if not isinstance(maxticks,bool):
|
3095
|
+
try:
|
3096
|
+
df01x=df01[[colname1]].astype('float')
|
3097
|
+
df1=df_smooth_manual(df01x,resample_freq=resample_freq)
|
3098
|
+
except:
|
3099
|
+
df1=df01
|
3100
|
+
|
3101
|
+
try:
|
3102
|
+
df02x=df02[[colname2]].astype('float')
|
3103
|
+
df2=df_smooth_manual(df02x,resample_freq=resample_freq)
|
3104
|
+
except:
|
3105
|
+
df2=df02
|
3106
|
+
else:
|
3107
|
+
df1=df01; df2=df02
|
3108
|
+
|
3109
|
+
plt.title(titletxt,fontweight='bold',fontsize=title_txt_size)
|
3110
|
+
|
3111
|
+
#证券1:先绘制折线图
|
3112
|
+
if not isinstance(maxticks,bool):
|
3113
|
+
date_start=df1.index[0]
|
3114
|
+
date_end=df1.index[-1]
|
3115
|
+
if date_range and not date_freq:
|
3116
|
+
ax=plt.gca()
|
3117
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
3118
|
+
plt.xticks(pd.date_range(date_start,date_end))
|
3119
|
+
if not date_range and date_freq:
|
3120
|
+
ax=plt.gca()
|
3121
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
3122
|
+
plt.xticks(pd.date_range(freq=date_freq))
|
3123
|
+
if date_range and date_freq:
|
3124
|
+
ax=plt.gca()
|
3125
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
3126
|
+
plt.xticks(pd.date_range(date_start,date_end,freq=date_freq))
|
3127
|
+
|
3128
|
+
lwadjust=linewidth_adjust(df1)
|
3129
|
+
plt.plot(df1.index,df1[colname1],label=label1,linestyle='-',linewidth=lwadjust)
|
3130
|
+
|
3131
|
+
#证券2:先绘制折线图
|
3132
|
+
if not isinstance(maxticks,bool):
|
3133
|
+
date_start=df2.index[0]
|
3134
|
+
date_end=df2.index[-1]
|
3135
|
+
if date_range and not date_freq:
|
3136
|
+
ax=plt.gca()
|
3137
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
3138
|
+
plt.xticks(pd.date_range(date_start,date_end))
|
3139
|
+
if not date_range and date_freq:
|
3140
|
+
ax=plt.gca()
|
3141
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
3142
|
+
plt.xticks(pd.date_range(freq=date_freq))
|
3143
|
+
if date_range and date_freq:
|
3144
|
+
ax=plt.gca()
|
3145
|
+
ax.xaxis.set_major_formatter(mdate.DateFormatter(date_fmt))
|
3146
|
+
plt.xticks(pd.date_range(date_start,date_end,freq=date_freq))
|
3147
|
+
|
3148
|
+
lwadjust=linewidth_adjust(df2)
|
3149
|
+
plt.plot(df2.index,df2[colname2],label=label2,linestyle='-.',linewidth=lwadjust)
|
3150
|
+
|
3151
|
+
#是否绘制水平虚线
|
3152
|
+
if not (hline == 0):
|
3153
|
+
plt.axhline(y=hline,ls=":",c="black")
|
3154
|
+
#是否绘制垂直虚线
|
3155
|
+
if not (vline == 0):
|
3156
|
+
plt.axvline(x=vline,ls=":",c="black")
|
3157
|
+
|
3158
|
+
plt.ylabel(ylabeltxt,fontsize=ylabel_txt_size)
|
3159
|
+
plt.xlabel(footnote,fontsize=xlabel_txt_size,ha='center')
|
3160
|
+
plt.legend(loc='best',fontsize=legend_txt_size)
|
3161
|
+
|
3162
|
+
# 使用 AutoDateLocator 自动选择最佳间隔,目的是显示最后一个日期,亲测有效!!!
|
3163
|
+
if not isinstance(maxticks,bool):
|
3164
|
+
import matplotlib.dates as mdates
|
3165
|
+
ax=plt.gca()
|
3166
|
+
ax.xaxis.set_major_locator(mdates.AutoDateLocator(maxticks=maxticks))
|
3167
|
+
|
3168
|
+
plt.gcf().autofmt_xdate(ha="center") # 优化标注(自动倾斜)
|
3169
|
+
try:
|
3170
|
+
plt.gca().set_facecolor(facecolor)
|
3171
|
+
except:
|
3172
|
+
print(" #Warning(plot_2lines): color",facecolor,"is unsupported, changed to default setting")
|
3173
|
+
plt.gca().set_facecolor("whitesmoke")
|
3174
|
+
|
3175
|
+
plt.show()
|
3176
|
+
|
3177
|
+
return
|
3178
|
+
|
3179
|
+
if __name__ =="__main__":
|
3180
|
+
df1=bsm_call_maturity(42,40,[50,200],0.015,0.23,90,1.5)
|
3181
|
+
df2=bsm_put_maturity(42,40,[50,200],0.015,0.23,90,1.5)
|
3182
|
+
ticker1='A'; colname1='Option Price'; label1='A1'
|
3183
|
+
ticker2='B'; colname2='Option Price'; label2='B2'
|
3184
|
+
ylabeltxt='ylabel'; titletxt='title'; footnote='\n\n\n\n4lines'
|
3185
|
+
power=0; datatag1=False; datatag2=False; zeroline=False
|
3186
|
+
|
3187
|
+
#==============================================================================
|
3188
|
+
def df_smooth(df):
|
3189
|
+
"""
|
3190
|
+
功能:对df中的数值型样本进行插值,以便绘制的折线图相对平滑。
|
3191
|
+
要求:df的索引为pandas的datetime日期型
|
3192
|
+
注意1:如果样本数量较多,例如多于100个,平滑效果不明显。
|
3193
|
+
注意2:order阶数仅对'spline'和'polynomial'方法有效,其中'polynomial'方法的阶数只能为奇数。
|
3194
|
+
"""
|
3195
|
+
|
3196
|
+
# df索引项若非日期型,不适合采用本函数进行插值
|
3197
|
+
import pandas as pd
|
3198
|
+
if not pd.api.types.is_datetime64_any_dtype(df.index):
|
3199
|
+
return df
|
3200
|
+
|
3201
|
+
#如果样本个数多于100个,其实没必要进行平滑,因为看不出效果
|
3202
|
+
if len(df) >= 100: return df
|
3203
|
+
|
3204
|
+
#定义重采样频率
|
3205
|
+
"""
|
3206
|
+
常用的采样频率:
|
3207
|
+
H: hourly, BH: business hour, T: minutely, S: secondly, B: business day, W: weekly,
|
3208
|
+
SM: semi-month end, SMS: semi-month start,
|
3209
|
+
BMS: business month start,BM: business month end,
|
3210
|
+
BQ: business quarter end, BQS: business quarter start,
|
3211
|
+
BA/BY: business year end, BAS/BYS: business year start.
|
3212
|
+
|
3213
|
+
例如:
|
3214
|
+
df2=df.resample('2D').sum()
|
3215
|
+
df2=df.resample('W').mean()
|
3216
|
+
"""
|
3217
|
+
#将索引转换为Datetimeindex,不然resample会失败
|
3218
|
+
df['date']=pd.to_datetime(df.index)
|
3219
|
+
df.set_index('date',inplace=True)
|
3220
|
+
|
3221
|
+
#重新采样
|
3222
|
+
rflag=False
|
3223
|
+
freqlist=['H','B','W','M','Q']
|
3224
|
+
for f in freqlist:
|
3225
|
+
try:
|
3226
|
+
#dfh=df.resample(f).ffill()
|
3227
|
+
dfh=df.resample(f)
|
3228
|
+
except:
|
3229
|
+
continue
|
3230
|
+
else:
|
3231
|
+
rflag=True
|
3232
|
+
break
|
3233
|
+
|
3234
|
+
if not rflag:
|
3235
|
+
#print(' #Warning(df_smooth): resampling failed for frequency',freqlist)
|
3236
|
+
dfh=df
|
3237
|
+
|
3238
|
+
#重采样后插值
|
3239
|
+
methodlist=['pchip','nearest','cubic','quadratic','slinear','linear','zero','time','index', \
|
3240
|
+
'piecewise_polynomial','akima','from_derivatives','spline','polynomial']
|
3241
|
+
methodlist_order=['spline','polynomial']
|
3242
|
+
order=3
|
3243
|
+
for method in methodlist:
|
3244
|
+
if method in methodlist_order:
|
3245
|
+
try:
|
3246
|
+
dfm=dfh.interpolate(method=method,order=order)
|
3247
|
+
except:
|
3248
|
+
#print(' #Warning(df_smooth): interpolate failed for method',method,'with order',order)
|
3249
|
+
#若出错就原样返回
|
3250
|
+
return df
|
3251
|
+
else: break
|
3252
|
+
else:
|
3253
|
+
try:
|
3254
|
+
dfm=dfh.interpolate(method=method)
|
3255
|
+
except:
|
3256
|
+
#print(' #Warning(df_smooth): interpolate failed for method',method)
|
3257
|
+
return df
|
3258
|
+
else: break
|
3259
|
+
|
3260
|
+
#成功返回经过重采样的df
|
3261
|
+
return dfm
|
3262
|
+
|
3263
|
+
|
3264
|
+
#==============================================================================
|
3265
|
+
def df_smooth_manual(df,method='linear',resample_freq='D',order=3):
|
3266
|
+
"""
|
3267
|
+
功能:对df中的第一个数值列样本进行插值,以便绘制的折线图相对平滑。
|
3268
|
+
要求:df的索引为pandas的datetime日期型
|
3269
|
+
注意1:如果样本数量较多,例如多于100个,平滑效果不明显。
|
3270
|
+
注意2:order阶数仅对'spline'和'polynomial'方法有效,其中'polynomial'方法的阶数只能为奇数。
|
3271
|
+
注意3:pchip方法经常失败,可改为cubic
|
3272
|
+
"""
|
3273
|
+
|
3274
|
+
# df索引项若非日期型,不适合采用本函数进行插值
|
3275
|
+
import pandas as pd
|
3276
|
+
if not pd.api.types.is_datetime64_any_dtype(df.index):
|
3277
|
+
return df
|
3278
|
+
|
3279
|
+
#如果样本个数多于100个,没必要进行平滑,完全看不出效果
|
3280
|
+
if len(df) >= 100: return df
|
3281
|
+
|
3282
|
+
#检查插值方法是否支持
|
3283
|
+
methodlist=['quadratic','cubic','slinear','linear','zero','nearest','time','index', \
|
3284
|
+
'piecewise_polynomial','pchip','akima','from_derivatives','spline','polynomial']
|
3285
|
+
if not (method in methodlist): return df
|
3286
|
+
|
3287
|
+
#定义重采样频率
|
3288
|
+
"""
|
3289
|
+
常用的采样频率:
|
3290
|
+
H: hourly, BH: business hour, T: minutely, S: secondly, B: business day, W: weekly,
|
3291
|
+
SM: semi-month end, SMS: semi-month start,
|
3292
|
+
BMS: business month start,BM: business month end,
|
3293
|
+
BQ: business quarter end, BQS: business quarter start,
|
3294
|
+
BA/BY: business year end, BAS/BYS: business year start.
|
3295
|
+
|
3296
|
+
例如:
|
3297
|
+
df2=df.resample('2D').sum()
|
3298
|
+
df2=df.resample('W').mean()
|
3299
|
+
"""
|
3300
|
+
#将索引转换为Datetimeindex,不然resample会失败
|
3301
|
+
try:
|
3302
|
+
df['date']=pd.to_datetime(df.index)
|
3303
|
+
except:
|
3304
|
+
return df
|
3305
|
+
df.set_index('date',inplace=True)
|
3306
|
+
|
3307
|
+
#重新采样
|
3308
|
+
try:
|
3309
|
+
dfh=df.resample(resample_freq)
|
3310
|
+
except:
|
3311
|
+
print(' #Warning(df_smooth): resampling failed for frequency',resample_freq)
|
3312
|
+
return df
|
3313
|
+
|
3314
|
+
#重采样后插值(不然太多nan):是否methodlist_o里面的特别插值方法
|
3315
|
+
methodlist_o=['spline','polynomial']
|
3316
|
+
if method in methodlist_o:
|
3317
|
+
try:
|
3318
|
+
dfm=dfh.interpolate(method=method,order=order)
|
3319
|
+
except:
|
3320
|
+
print(' #Warning(df_smooth_manual): interpolate failed for method',method,'with order',order)
|
3321
|
+
#若出错就原样返回
|
3322
|
+
return df
|
3323
|
+
#成功返回经过重采样的df
|
3324
|
+
return dfm
|
3325
|
+
|
3326
|
+
#重采样后插值:其他插值方法
|
3327
|
+
try:
|
3328
|
+
dfm=dfh.interpolate(method=method)
|
3329
|
+
except:
|
3330
|
+
#print(' #Warning(df_smooth_manual): interpolate failed for method',method)
|
3331
|
+
#print(' Possible reason: interpolating row must be int or float instead of string')
|
3332
|
+
"""
|
3333
|
+
#改为cubic方法
|
3334
|
+
if not (method == 'cubic'):
|
3335
|
+
try:
|
3336
|
+
dfm=dfh.interpolate(method='cubic')
|
3337
|
+
except:
|
3338
|
+
print(' #Warning(df_smooth_manual): interpolate failed for method cubic')
|
3339
|
+
return df
|
3340
|
+
else:
|
3341
|
+
return df
|
3342
|
+
"""
|
3343
|
+
return df
|
3344
|
+
|
3345
|
+
# check whether dfm becomes empty
|
3346
|
+
if len(dfm)==0:
|
3347
|
+
return df
|
3348
|
+
else:
|
3349
|
+
return dfm
|
3350
|
+
#==============================================================================
|
3351
|
+
if __name__=='__main__':
|
3352
|
+
wid=5
|
3353
|
+
mu=0
|
3354
|
+
sd=1
|
3355
|
+
obs_num=100
|
3356
|
+
|
3357
|
+
def plot_norm(mu,sd,graph='pdf',obs_num=100,facecolor='whitesmoke'):
|
3358
|
+
"""
|
3359
|
+
绘制正态分布图形
|
3360
|
+
mu:均值
|
3361
|
+
sd:标准差
|
3362
|
+
graph:图形种类,pdf,cdf,ppf
|
3363
|
+
"""
|
3364
|
+
if not (graph in ['pdf','cdf','ppf']):
|
3365
|
+
print(" #Warning(plot_norm): support pdf/cdf/ppf only")
|
3366
|
+
return
|
3367
|
+
|
3368
|
+
#计算概率密度:连续分布用pdf,离散分布用pmf
|
3369
|
+
import scipy.stats as st
|
3370
|
+
import numpy as np
|
3371
|
+
|
3372
|
+
if graph=='pdf':
|
3373
|
+
wid=4*sd+mu
|
3374
|
+
X=np.linspace(-wid,wid,obs_num)
|
3375
|
+
y_pdf=st.norm.pdf(X,mu,sd)
|
3376
|
+
|
3377
|
+
if graph=='cdf':
|
3378
|
+
wid=3*sd+mu
|
3379
|
+
X=np.linspace(-wid,wid,obs_num)
|
3380
|
+
y_cdf=st.norm.cdf(X,mu,sd)
|
3381
|
+
|
3382
|
+
if graph=='ppf':
|
3383
|
+
X=np.linspace(0,1,obs_num)
|
3384
|
+
y_ppf=st.norm.ppf(X,mu,sd)
|
3385
|
+
|
3386
|
+
#绘图
|
3387
|
+
if graph=='pdf':
|
3388
|
+
plt.plot(X,y_pdf,c="red",label='pdf')
|
3389
|
+
if graph=='cdf':
|
3390
|
+
plt.plot(X,y_cdf,c="blue",label='cdf')
|
3391
|
+
if graph=='ppf':
|
3392
|
+
plt.plot(X,y_ppf,c="green",label='ppf')
|
3393
|
+
|
3394
|
+
if graph=='pdf':
|
3395
|
+
wid1=5*sd+mu
|
3396
|
+
wid2=1*sd+mu
|
3397
|
+
plt.xticks(np.arange(-wid,wid1,wid2))
|
3398
|
+
plt.xlabel('分位点',fontsize=xlabel_txt_size,ha='center') #x轴文本
|
3399
|
+
plt.yticks(np.arange(0,0.45,0.05))
|
3400
|
+
plt.ylabel('概率密度',fontsize=ylabel_txt_size) #y轴文本
|
3401
|
+
|
3402
|
+
if graph=='cdf':
|
3403
|
+
wid1=3.5*sd+mu
|
3404
|
+
wid2=0.5*sd+mu
|
3405
|
+
plt.xticks(np.arange(-wid,wid1,wid2))
|
3406
|
+
plt.xlabel('分位点',fontsize=xlabel_txt_size,ha='center') #x轴文本
|
3407
|
+
plt.yticks(np.arange(0,1.1,0.1))
|
3408
|
+
plt.ylabel('累积概率密度',fontsize=ylabel_txt_size) #y轴文本
|
3409
|
+
|
3410
|
+
if graph=='ppf':
|
3411
|
+
wid=2.5*sd+mu
|
3412
|
+
wid1=3*sd+mu
|
3413
|
+
wid2=0.5*sd+mu
|
3414
|
+
plt.yticks(np.arange(-wid,wid1,wid2))
|
3415
|
+
plt.ylabel('分位点',fontsize=ylabel_txt_size) #y轴文本
|
3416
|
+
plt.xticks(np.arange(0,1.1,0.1))
|
3417
|
+
plt.xlabel('累积概率密度',fontsize=xlabel_txt_size,ha='center') #x轴文本
|
3418
|
+
|
3419
|
+
plt.title('正态分布示意图: $\mu$=%.1f, $\sigma$=%.1f'%(mu,sd),fontweight='bold',fontsize=title_txt_size) #标题
|
3420
|
+
plt.tight_layout()
|
3421
|
+
#plt.grid() #网格
|
3422
|
+
plt.legend(loc='best',fontsize=legend_txt_size)
|
3423
|
+
|
3424
|
+
try:
|
3425
|
+
plt.gca().set_facecolor(facecolor)
|
3426
|
+
except:
|
3427
|
+
print(" #Warning(plot_norm): color",facecolor,"is unsupported, changed to default setting")
|
3428
|
+
plt.gca().set_facecolor("whitesmoke")
|
3429
|
+
|
3430
|
+
plt.show() #显示图形
|
3431
|
+
|
3432
|
+
return
|
3433
|
+
|
3434
|
+
if __name__=='__main__':
|
3435
|
+
plot_norm(4,mu,sd,graph='pdf')
|
3436
|
+
plot_norm(3,mu,sd,graph='cdf')
|
3437
|
+
plot_norm(3,mu,sd,graph='ppf')
|
3438
|
+
|
3439
|
+
#==============================================================================
|
3440
|
+
#==============================================================================
|
3441
|
+
|
3442
|
+
if __name__=='__main__':
|
3443
|
+
firstColSpecial=True
|
3444
|
+
colWidth=0.1
|
3445
|
+
tabScale=2
|
3446
|
+
figsize=(12.8,6.4)
|
3447
|
+
cellLoc='right'
|
3448
|
+
fontsize=10
|
3449
|
+
|
3450
|
+
firstColSpecial=False
|
3451
|
+
cellLoc='center'
|
3452
|
+
auto_len=True
|
3453
|
+
|
3454
|
+
df=market_detail_china(category='price')
|
3455
|
+
pandas2plttable(df)
|
3456
|
+
|
3457
|
+
def pandas2plttable(df,titletxt,firstColSpecial=True,colWidth=0.1,tabScale=2,cellLoc='right', \
|
3458
|
+
figsize=(12.8,6.4),fontsize=13,auto_len=False,title_x=0.5):
|
3459
|
+
"""
|
3460
|
+
功能:将一个df转换为matplotlib表格格式,打印图形表格,适应性广
|
3461
|
+
firstColSpecial:第一列是否特殊处理,默认True
|
3462
|
+
|
3463
|
+
注意1:引入表格的字段不包括索引字段
|
3464
|
+
"""
|
3465
|
+
|
3466
|
+
#列名列表
|
3467
|
+
col=list(df)
|
3468
|
+
numOfCol=len(col)
|
3469
|
+
|
3470
|
+
# 第一列长度取齐处理
|
3471
|
+
if firstColSpecial:
|
3472
|
+
#第一列的最长长度
|
3473
|
+
firstcol=col[0]
|
3474
|
+
maxlen=0
|
3475
|
+
for f in firstcol:
|
3476
|
+
flen=hzlen(f.strip())
|
3477
|
+
if flen > maxlen:
|
3478
|
+
maxlen=flen
|
3479
|
+
|
3480
|
+
#将第一列内容的长度取齐
|
3481
|
+
df[col[0]]=df[col[0]].apply(lambda x:equalwidth(x.strip(),maxlen=maxlen,extchar=' ',endchar=' '))
|
3482
|
+
|
3483
|
+
#设置每列的宽度
|
3484
|
+
col_len_list=[]
|
3485
|
+
col_len_list_rel=[]
|
3486
|
+
if auto_len:
|
3487
|
+
|
3488
|
+
# 计算每列的相对宽度
|
3489
|
+
for c in col:
|
3490
|
+
heading_len=hzlen(c.strip())
|
3491
|
+
df['col_len']=df[c].apply(lambda x: hzlen(x.strip()))
|
3492
|
+
field_len=df['col_len'].max()
|
3493
|
+
col_len=max([heading_len,field_len])
|
3494
|
+
|
3495
|
+
col_len_list=col_len_list+[col_len]
|
3496
|
+
|
3497
|
+
col_len_min=min(col_len_list)
|
3498
|
+
for l in col_len_list:
|
3499
|
+
rel_len=l / col_len_min
|
3500
|
+
col_len_list_rel=col_len_list_rel+[round(rel_len*colWidth,3)]
|
3501
|
+
|
3502
|
+
del df['col_len']
|
3503
|
+
|
3504
|
+
|
3505
|
+
#表格里面的具体值
|
3506
|
+
vals=[]
|
3507
|
+
for i in range(0,len(df)):
|
3508
|
+
vals=vals+[list(df.iloc[i])]
|
3509
|
+
|
3510
|
+
plt.figure(figsize=figsize)
|
3511
|
+
|
3512
|
+
if not auto_len:
|
3513
|
+
tab = plt.table(cellText=vals,
|
3514
|
+
colLabels=col,
|
3515
|
+
loc='best',
|
3516
|
+
cellLoc=cellLoc)
|
3517
|
+
else:
|
3518
|
+
tab = plt.table(cellText=vals,
|
3519
|
+
colLabels=col,
|
3520
|
+
colWidths=col_len_list_rel,
|
3521
|
+
loc='best',
|
3522
|
+
rowLoc='center',
|
3523
|
+
cellLoc=cellLoc)
|
3524
|
+
|
3525
|
+
|
3526
|
+
tab.scale(1,tabScale) #让表格纵向扩展tabScale倍数
|
3527
|
+
|
3528
|
+
# 试验参数:查询tab对象的属性使用dir(tab)
|
3529
|
+
tab.auto_set_font_size(False)
|
3530
|
+
tab.set_fontsize(fontsize)
|
3531
|
+
|
3532
|
+
if auto_len:
|
3533
|
+
tab.auto_set_column_width(True) #此功能有bug,只能对前几列起作用
|
3534
|
+
|
3535
|
+
plt.axis('off') #关闭plt绘制纵横轴线
|
3536
|
+
|
3537
|
+
#plt.xlabel(footnote,fontsize=xlabel_txt_size)
|
3538
|
+
if not auto_len:
|
3539
|
+
plt.title(titletxt,fontweight='bold',fontsize=title_txt_size)
|
3540
|
+
else:
|
3541
|
+
plt.title(titletxt,fontweight='bold',fontsize=title_txt_size,x=title_x)
|
3542
|
+
|
3543
|
+
plt.gca().set_facecolor('whitesmoke')
|
3544
|
+
|
3545
|
+
plt.show()
|
3546
|
+
|
3547
|
+
return
|
3548
|
+
|
3549
|
+
#==============================================================================
|
3550
|
+
|
3551
|
+
if __name__=='__main__':
|
3552
|
+
firstColSpecial=True
|
3553
|
+
colWidth=0.1
|
3554
|
+
tabScale=2
|
3555
|
+
figsize=(10,6)
|
3556
|
+
cellLoc='right'
|
3557
|
+
fontsize=10
|
3558
|
+
|
3559
|
+
firstColSpecial=False
|
3560
|
+
cellLoc='center'
|
3561
|
+
auto_len=True
|
3562
|
+
|
3563
|
+
df=market_detail_china(category='price')
|
3564
|
+
pandas2plttable(df)
|
3565
|
+
|
3566
|
+
def pandas2plttable2(df,titletxt,firstColSpecial=True,cellLoc='right'):
|
3567
|
+
"""
|
3568
|
+
功能:将一个df转换为matplotlib表格格式,打印图形表格,适应性广,自动适应列宽和字体大小
|
3569
|
+
firstColSpecial:第一列是否特殊处理,默认True
|
3570
|
+
|
3571
|
+
注意1:引入表格的字段不包括索引字段
|
3572
|
+
"""
|
3573
|
+
|
3574
|
+
df.fillna('',inplace=True)
|
3575
|
+
|
3576
|
+
#列名列表
|
3577
|
+
col=list(df)
|
3578
|
+
numOfCol=len(col)
|
3579
|
+
|
3580
|
+
# 第一列长度取齐处理
|
3581
|
+
if firstColSpecial:
|
3582
|
+
|
3583
|
+
#第一列的最长长度
|
3584
|
+
firstcol=col[0]
|
3585
|
+
maxlen=0
|
3586
|
+
for f in df[firstcol]:
|
3587
|
+
flen=hzlen(f)
|
3588
|
+
if flen > maxlen:
|
3589
|
+
maxlen=flen
|
3590
|
+
|
3591
|
+
#将第一列内容的长度取齐
|
3592
|
+
extchar='.'
|
3593
|
+
df[firstcol]=df[firstcol].apply(lambda x: str(x) + extchar*(maxlen-hzlen(x)))
|
3594
|
+
|
3595
|
+
|
3596
|
+
#表格里面的具体值
|
3597
|
+
vals=[]
|
3598
|
+
for i in range(0,len(df)):
|
3599
|
+
vals=vals+[list(df.iloc[i])]
|
3600
|
+
|
3601
|
+
plt.figure()
|
3602
|
+
|
3603
|
+
tab = plt.table(cellText=vals,
|
3604
|
+
colLabels=col,
|
3605
|
+
loc='best',
|
3606
|
+
rowLoc='center',
|
3607
|
+
cellLoc=cellLoc)
|
3608
|
+
|
3609
|
+
#tab.scale(1,tabScale) #让表格纵向扩展tabScale倍数
|
3610
|
+
|
3611
|
+
# 试验参数:查询tab对象的属性使用dir(tab)
|
3612
|
+
tab.auto_set_font_size(True)
|
3613
|
+
|
3614
|
+
tab.auto_set_column_width(True) #此功能有bug,只能对前几列起作用
|
3615
|
+
|
3616
|
+
plt.axis('off') #关闭plt绘制纵横轴线
|
3617
|
+
|
3618
|
+
plt.title(titletxt)
|
3619
|
+
|
3620
|
+
plt.gca().set_facecolor('whitesmoke')
|
3621
|
+
|
3622
|
+
plt.show()
|
3623
|
+
|
3624
|
+
return
|
3625
|
+
|
3626
|
+
|
3627
|
+
#==============================================================================
|
3628
|
+
#==============================================================================
|
3629
|
+
|
3630
|
+
|
3631
|
+
|
3632
|
+
|
3633
|
+
|
3634
|
+
|
3635
|
+
|
3636
|
+
|