首页 > 编程开发 > python数据分析 >
-
实战:绘制某只股票的完整技术分析面板
第10章 股票技术分析实战
10.1 实战:绘制某只股票的完整技术分析面板
10.1.1 先讲个真实需求:老板要我做一个专业的股票分析面板
上个月老板让我做一个专业的股票分析面板,要求包含K线图、成交量、MACD、KDJ、RSI、BOLL这些技术指标,还要能实时更新数据。我用Matplotlib花了三天时间才做出来,后来发现用mplfinance和plotly可以更快地实现。今天我就把这个完vb.net教程C#教程python教程SQL教程access 2010教程整的技术分析面板的实现方法讲清楚,帮你快速做出专业的股票分析工具。
10.1.2 核心技术:多子图布局与技术指标整合
要绘制完整的技术分析面板,需要使用多子图布局,把K线图、成交量、MACD、KDJ、RSI、BOLL这些技术指标整合到一个画布中,同时添加交互式功能,方便用户操作。
10.1.3 实战1:用Matplotlib绘制完整技术分析面板
-
实战代码:Matplotlib完整技术分析面板
python
# 1. 导入需要的库
import tushare as ts
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.pylab import date2num
from matplotlib.widgets import Cursor
# 2. 获取贵州茅台的日线数据
pro = ts.pro_api()
df = pro.daily(ts_code='600519.SH', start_date='20250101', end_date='20250710')
# 3. 数据预处理
# 转换日期格式
df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d')
# 按时间从早到晚排序
df.sort_values('trade_date', inplace=True)
# 设置日期为索引
df.set_index('trade_date', inplace=True)
# 把日期转成Matplotlib需要的数字格式
df['date_num'] = df.index.to_series().apply(date2num)
# 计算涨跌颜色
df['color'] = np.where(df['close'] >= df['open'], 'red', 'green')
# 4. 计算技术指标
# 计算均线
df['ma5'] = df['close'].rolling(window=5).mean()
df['ma20'] = df['close'].rolling(window=20).mean()
df['ma60'] = df['close'].rolling(window=60).mean()
# 计算MACD
ema12 = df['close'].ewm(span=12, adjust=False).mean()
ema26 = df['close'].ewm(span=26, adjust=False).mean()
df['dif'] = ema12 - ema26
df['dea'] = df['dif'].ewm(span=9, adjust=False).mean()
df['macd'] = 2 * (df['dif'] - df['dea'])
# 计算KDJ
low9 = df['low'].rolling(window=9).min()
high9 = df['high'].rolling(window=9).max()
df['rsv'] = (df['close'] - low9) / (high9 - low9) * 100
df['k'] = np.nan
df['d'] = np.nan
df.loc[df.index[8], 'k'] = df.loc[df.index[8], 'rsv']
df.loc[df.index[8], 'd'] = df.loc[df.index[8], 'rsv']
for i in range(9, len(df)):
df.loc[df.index[i], 'k'] = (2/3) * df.loc[df.index[i-1], 'k'] + (1/3) * df.loc[df.index[i], 'rsv']
df.loc[df.index[i], 'd'] = (2/3) * df.loc[df.index[i-1], 'd'] + (1/3) * df.loc[df.index[i], 'k']
df['j'] = 3 * df['k'] - 2 * df['d']
# 计算RSI
df['change'] = df['close'].diff()
df['up'] = np.where(df['change'] > 0, df['change'], 0)
df['down'] = np.where(df['change'] < 0, -df['change'], 0)
up6 = df['up'].rolling(window=6).mean()
down6 = df['down'].rolling(window=6).mean()
df['rsi6'] = up6 / (up6 + down6) * 100
# 计算BOLL
df['mid'] = df['close'].rolling(window=20).mean()
df['std'] = df['close'].rolling(window=20).std()
df['upper'] = df['mid'] + 2 * df['std']
df['lower'] = df['mid'] - 2 * df['std']
# 5. 创建画布和坐标轴
fig, ((ax1, ax2), (ax3, ax4), (ax5, ax6)) = plt.subplots(
3, 2, # 3行2列的坐标轴
figsize=(16, 18), # 画布大小
dpi=100, # 分辨率
gridspec_kw={'height_ratios': [3, 1, 1]} # 三个行的高度比例,3:1:1
)
# 6. 绘制K线图和BOLL指标
# 绘制BOLL上轨和下轨
ax1.plot(
df['date_num'], df['upper'],
label='BOLL上轨', color='#ff7f0e', linewidth=1, linestyle='--'
)
ax1.plot(
df['date_num'], df['mid'],
label='BOLL中轨', color='#2ca02c', linewidth=1, linestyle='--'
)
ax1.plot(
df['date_num'], df['lower'],
label='BOLL下轨', color='#d62728', linewidth=1, linestyle='--'
)
# 填充BOLL上轨和下轨之间的区域
ax1.fill_between(
df['date_num'], df['upper'], df['lower'],
color='#f0f0f0', alpha=0.3
)
# 绘制K线影线
ax1.vlines(
x=df['date_num'],
ymin=df['low'],
ymax=df['high'],
color=df['color'],
linewidth=1
)
# 绘制K线实体
ax1.bar(
x=df['date_num'],
height=abs(df['close'] - df['open']),
bottom=np.minimum(df['open'], df['close']),
width=0.6,
color=df['color'],
edgecolor='#333333',
linewidth=1
)
# 绘制均线
ax1.plot(
df['date_num'], df['ma5'],
label='5日均线', color='#1f77b4', linewidth=2
)
ax1.plot(
df['date_num'], df['ma20'],
label='20日均线', color='#ff7f0e', linewidth=2
)
ax1.plot(
df['date_num'], df['ma60'],
label='60日均线', color='#2ca02c', linewidth=2
)
# 设置K线图属性
ax1.set_title('贵州茅台2025年K线图与BOLL指标', fontsize=16, fontweight='bold', pad=20)
ax1.set_ylabel('价格(元)', fontsize=14, labelpad=15)
ax1.legend(fontsize=10, loc='upper right')
ax1.grid(True, linestyle='--', alpha=0.7, color='#cccccc')
# 设置日期显示格式
ax1.xaxis_date()
ax1.xaxis.set_major_formatter(plt.FixedFormatter(df.index.strftime('%Y-%m-%d')))
plt.setp(ax1.get_xticklabels(), rotation=45, ha='right', fontsize=8)
# 7. 绘制成交量柱形图
ax2.bar(
x=df['date_num'],
height=df['vol'],
width=0.6,
color=df['color'],
edgecolor='#333333',
linewidth=1
)
# 绘制成交量均线
ax2.plot(
df['date_num'], df['vol'].rolling(window=5).mean(),
label='5日成交量均线', color='#1f77b4', linewidth=2
)
ax2.plot(
df['date_num'], df['vol'].rolling(window=20).mean(),
label='20日成交量均线', color='#ff7f0e', linewidth=2
)
# 设置成交量属性
ax2.set_title('成交量柱形图', fontsize=16, fontweight='bold', pad=20)
ax2.set_ylabel('成交量(手)', fontsize=14, labelpad=15)
ax2.legend(fontsize=10, loc='upper right')
ax2.grid(True, linestyle='--', alpha=0.7, color='#cccccc')
# 设置日期显示格式
ax2.xaxis_date()
ax2.xaxis.set_major_formatter(plt.FixedFormatter(df.index.strftime('%Y-%m-%d')))
plt.setp(ax2.get_xticklabels(), rotation=45, ha='right', fontsize=8)
# 8. 绘制MACD指标
ax3.plot(
df['date_num'], df['dif'],
label='DIF(快线)', color='#1f77b4', linewidth=2
)
ax3.plot(
df['date_num'], df['dea'],
label='DEA(慢线)', color='#ff7f0e', linewidth=2
)
# 绘制MACD柱状图
ax3.bar(
x=df['date_num'],
height=df['macd'],
width=0.6,
color=np.where(df['macd'] > 0, '#1f77b4', '#d62728'),
alpha=0.5
)
# 设置MACD属性
ax3.set_title('MACD指标', fontsize=16, fontweight='bold', pad=20)
ax3.set_ylabel('MACD', fontsize=14, labelpad=15)
ax3.legend(fontsize=10, loc='upper right')
ax3.grid(True, linestyle='--', alpha=0.7, color='#cccccc')
# 设置日期显示格式
ax3.xaxis_date()
ax3.xaxis.set_major_formatter(plt.FixedFormatter(df.index.strftime('%Y-%m-%d')))
plt.setp(ax3.get_xticklabels(), rotation=45, ha='right', fontsize=8)
# 9. 绘制KDJ指标
ax4.plot(
df['date_num'], df['k'],
label='K线', color='#1f77b4', linewidth=2
)
ax4.plot(
df['date_num'], df['d'],
label='D线', color='#ff7f0e', linewidth=2
)
ax4.plot(
df['date_num'], df['j'],
label='J线', color='#2ca02c', linewidth=2
)
# 添加超买超卖线
ax4.axhline(y=80, color='#d62728', linestyle='--', linewidth=1)
ax4.axhline(y=20, color='#2ca02c', linestyle='--', linewidth=1)
# 设置KDJ属性
ax4.set_title('KDJ指标', fontsize=16, fontweight='bold', pad=20)
ax4.set_ylabel('KDJ', fontsize=14, labelpad=15)
ax4.legend(fontsize=10, loc='upper right')
ax4.grid(True, linestyle='--', alpha=0.7, color='#cccccc')
# 设置日期显示格式
ax4.xaxis_date()
ax4.xaxis.set_major_formatter(plt.FixedFormatter(df.index.strftime('%Y-%m-%d')))
plt.setp(ax4.get_xticklabels(), rotation=45, ha='right', fontsize=8)
# 10. 绘制RSI指标
ax5.plot(
df['date_num'], df['rsi6'],
label='6日RSI', color='#1f77b4', linewidth=2
)
# 添加超买超卖线
ax5.axhline(y=70, color='#d62728', linestyle='--', linewidth=1)
ax5.axhline(y=30, color='#2ca02c', linestyle='--', linewidth=1)
# 设置RSI属性
ax5.set_title('RSI指标', fontsize=16, fontweight='bold', pad=20)
ax5.set_xlabel('日期', fontsize=14, labelpad=15)
ax5.set_ylabel('RSI', fontsize=14, labelpad=15)
ax5.legend(fontsize=10, loc='upper right')
ax5.grid(True, linestyle='--', alpha=0.7, color='#cccccc')
# 设置日期显示格式
ax5.xaxis_date()
ax5.xaxis.set_major_formatter(plt.FixedFormatter(df.index.strftime('%Y-%m-%d')))
plt.setp(ax5.get_xticklabels(), rotation=45, ha='right', fontsize=8)
# 11. 绘制数据统计指标
# 计算统计指标
stats = pd.DataFrame({
'指标': ['最新价', '开盘价', '最高价', '最低价', '成交量', '5日均线', '20日均线', '60日均线', 'DIF', 'DEA', 'MACD', 'K', 'D', 'J', 'RSI6'],
'数值': [
df['close'].iloc[-1], df['open'].iloc[-1], df['high'].iloc[-1], df['low'].iloc[-1], df['vol'].iloc[-1],
df['ma5'].iloc[-1], df['ma20'].iloc[-1], df['ma60'].iloc[-1],
df['dif'].iloc[-1], df['dea'].iloc[-1], df['macd'].iloc[-1],
df['k'].iloc[-1], df['d'].iloc[-1], df['j'].iloc[-1], df['rsi6'].iloc[-1]
]
})
# 绘制表格
table = ax6.table(
cellText=stats.values, # 表格内容
colLabels=stats.columns, # 列标签
cellLoc='center', # 单元格内容居中
loc='center' # 表格位置居中
)
# 设置表格样式
table.auto_set_font_size(False)
table.set_fontsize(10)
table.scale(1, 1.5) # 缩放表格,行高1.5倍
# 隐藏坐标轴
ax6.axis('off')
# 设置标题
ax6.set_title('数据统计指标', fontsize=16, fontweight='bold', pad=20)
# 12. 添加十字光标
cursor = Cursor(
ax1, # 只在K线图上添加十字光标
useblit=True,
color='#333333',
linewidth=1,
linestyle='--'
)
# 13. 添加数据提示框
text = ax1.text(
0.02, 0.98,
'',
transform=ax1.transAxes,
va='top',
ha='left',
fontsize=10,
bbox=dict(
boxstyle='round',
facecolor='#ffffff',
edgecolor='#333333',
alpha=0.8
)
)
def on_mouse_move(event):
if not event.inaxes:
return
x = event.xdata
idx = np.argmin(np.abs(df['date_num'] - x))
data = df.iloc[idx]
text_str = f'日期: {data.name.strftime("%Y-%m-%d")}
'
f'开盘价: {data["open"]:.2f}元
'
f'收盘价: {data["close"]:.2f}元
'
f'最高价: {data["high"]:.2f}元
'
f'最低价: {data["low"]:.2f}元
'
f'成交量: {data["vol"]:.0f}手'
text.set_text(text_str)
fig.canvas.draw_idle()
fig.canvas.mpl_connect('motion_notify_event', on_mouse_move)
# 14. 调整布局
plt.tight_layout()
# 15. 保存图片
plt.savefig('贵州茅台2025年技术分析面板.png', dpi=300, bbox_inches='tight')
# 16. 显示画布
plt.show()
逐行讲解:
gridspec_kw={'height_ratios': [3, 1, 1]}:设置3行2列的坐标轴高度比例,第一行(K线图)占3份,第二行和第三行各占1份
ax1.fill_between(...):填充BOLL上轨和下轨之间的区域,使BOLL指标更直观
ax2.plot(...):绘制成交量的5日和20日均线,帮助判断成交量的趋势
ax6.table(...):绘制数据统计表格,显示最新的技术指标数值
ax6.axis('off'):隐藏表格所在的坐标轴,只显示表格
运行结果:
会弹出一个窗口,显示贵州茅台2025年的完整技术分析面板,包括:
1.K线图和BOLL指标
2.成交量柱形图和成交量均线
3.MACD指标
4.KDJ指标
5.RSI指标
6.数据统计表格
同时支持十字光标和数据提示功能,鼠标悬停在K线上时会显示详细数据。
10.1.4 实战2:用mplfinance绘制专业技术分析面板
-
实战代码:mplfinance专业技术分析面板
python
# 1. 导入需要的库
import tushare as ts
import mplfinance as mpf
import pandas as pd
import numpy as np
# 2. 获取贵州茅台的日线数据
pro = ts.pro_api()
df = pro.daily(ts_code='600519.SH', start_date='20250101', end_date='20250710')
# 3. 数据预处理
df['trade_date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d')
df.set_index('trade_date', inplace=True)
df.sort_index(ascending=True, inplace=True)
df_rename = df.rename(columns={
'open': 'Open',
'high': 'High',
'low': 'Low',
'close': 'Close',
'vol': 'Volume'
})
# 4. 计算技术指标
# 计算MACD
ema12 = df_rename['Close'].ewm(span=12, adjust=False).mean()
ema26 = df_rename['Close'].ewm(span=26, adjust=False).mean()
df_rename['MACD'] = ema12 - ema26
df_rename['Signal'] = df_rename['MACD'].ewm(span=9, adjust=False).mean()
df_rename['Histogram'] = 2 * (df_rename['MACD'] - df_rename['Signal'])
# 计算KDJ
low9 = df_rename['Low'].rolling(window=9).min()
high9 = df_rename['High'].rolling(window=9).max()
df_rename['RSV'] = (df_rename['Close'] - low9) / (high9 - low9) * 100
df_rename['K'] = np.nan
df_rename['D'] = np.nan
df_rename.loc[df_rename.index[8], 'K'] = df_rename.loc[df_rename.index[8], 'RSV']
df_rename.loc[df_rename.index[8], 'D'] = df_rename.loc[df_rename.index[8], 'RSV']
for i in range(9, len(df_rename)):
df_rename.loc[df_rename.index[i], 'K'] = (2/3) * df_rename.loc[df_rename.index[i-1], 'K'] + (1/3) * df_rename.loc[df_rename.index[i], 'RSV']
df_rename.loc[df_rename.index[i], 'D'] = (2/3) * df_rename.loc[df_rename.index[i-1], 'D'] + (1/3) * df_rename.loc[df_rename.index[i], 'K']
df_rename['J'] = 3 * df_rename['K'] - 2 * df_rename['D']
# 计算RSI
df_rename['Change'] = df_rename['Close'].diff()
df_rename['Up'] = np.where(df_rename['Change'] > 0, df_rename['Change'], 0)
df_rename['Down'] = np.where(df_rename['Change'] < 0, -df_rename['Change'], 0)
up6 = df_rename['Up'].rolling(window=6).mean()
down6 = df_rename['Down'].rolling(window=6).mean()
df_rename['RSI6'] = up6 / (up6 + down6) * 100
# 计算BOLL
df_rename['Mid'] = df_rename['Close'].rolling(window=20).mean()
df_rename['Std'] = df_rename['Close'].rolling(window=20).std()
df_rename['Upper'] = df_rename['Mid'] + 2 * df_rename['Std']
df_rename['Lower'] = df_rename['Mid'] - 2 * df_rename['Std']
# 5. 自定义样式
my_style = mpf.make_mpf_style(
base_mpf_style='yahoo',
marketcolors=mpf.make_marketcolors(
up='red',
down='green',
edge='inherit',
wick='inherit',
volume='inherit'
),
gridstyle='--',
y_on_right=False,
facecolor='#f0f0f0',
edgecolor='#333333',
figcolor='#f0f0f0',
gridcolor='#cccccc'
)
# 6. 创建附加指标面板
apds = [
# 绘制BOLL指标
mpf.make_addplot(df_rename['Upper'], color='#ff7f0e', linestyle='--', width=1),
mpf.make_addplot(df_rename['Mid'], color='#2ca02c', linestyle='--', width=1),
mpf.make_addplot(df_rename['Lower'], color='#d62728', linestyle='--', width=1),
# 绘制MACD指标
mpf.make_addplot(df_rename['Histogram'], type='bar', color='#1f77b4', alpha=0.5, panel=1),
mpf.make_addplot(df_rename['MACD'], color='#1f77b4', width=2, panel=1),
mpf.make_addplot(df_rename['Signal'], color='#ff7f0e', width=2, panel=1),
# 绘制KDJ指标
mpf.make_addplot(df_rename['K'], color='#1f77b4', width=2, panel=2),
mpf.make_addplot(df_rename['D'], color='#ff7f0e', width=2, panel=2),
mpf.make_addplot(df_rename['J'], color='#2ca02c', width=2, panel=2),
# 绘制RSI指标
mpf.make_addplot(df_rename['RSI6'], color='#1f77b4', width=2, panel=3)
]
# 7. 绘制技术分析面板
mpf.plot(
df_rename,
type='candle',
style=my_style,
title='贵州茅台2025年技术分析面板',
ylabel='价格(元)',
ylabel_lower='成交量(手)',
volume=True,
mav=(5, 20, 60),
figratio=(16, 12),
figscale=1.2,
tight_layout=True,
warn_too_much_data=1000,
interactive=True,
addplot=apds,
panels=[
{'name': 'Volume', 'height': 1},
{'name': 'MACD', 'height': 1},
{'name': 'KDJ', 'height': 1},
{'name': 'RSI', 'height': 1}
]
)
逐行讲解:
mpf.make_addplot(...):创建附加指标面板,每个addplot对应一个技术指标
panel=1:指定指标显示在第2个面板(从0开始计数)
panels=[...]:定义各个面板的名称和高度比例
运行结果:
会弹出一个窗口,显示贵州茅台2025年的专业技术分析面板,支持交互式操作,包括缩放、平移、悬停提示等功能。
10.1.5 基础知识拓展:技术指标的综合应用
趋势判断:
使用BOLL指标判断趋势,当股价在BOLL上轨上方时,是上升趋势;当股价在BOLL下轨下方时,是下降趋势
使用均线判断趋势,当短期均线上穿长期均线时,是上升趋势;当短期均线下穿长期均线时,是下降趋势
买卖信号:
MACD金叉(DIF上穿DEA)时买入,死叉(DIF下穿DEA)时卖出
KDJ金叉(K线上穿D线)时买入,死叉(K线下穿D线)时卖出
RSI低于30时买入,高于70时卖出
成交量配合:
股价上涨时成交量放大,下跌时成交量缩小,是健康的上涨趋势
股价上涨时成交量缩小,下跌时成交量放大,是不健康的上涨趋势,可能反转
10.1.6 总结:技术分析面板的应用场景
1.股票分析:分析师可以通过技术分析面板快速查看股票的走势、成交量和各种技术指标,提高分析效率
2.量化交易:量化交易员可以通过技术分析面板监控股票的技术指标,当指标达到预设条件时自动执行交易
3.投资决策:投资者可以通过技术分析面板判断股票的买卖时机,提高投资收益
通过这个实战,你应该已经掌握了绘制完整技术分析面板的方法,接下来可以尝试用这些方法制作更专业的股票分析工具,或者结合量化策略实现自动交易。
下一节咱们就讲如何用Python做量化策略回测,帮你验证策略的有效性。










