先上最终成果图:


最终效果就是希望生成一个pdf文件,包含多只股票的K线图以及买入卖出点(图中部分红色方框为pdf软件的注释功能绘制的),因为本人技术面分析主要依靠裸K,辅助参考成交量、振幅、MACD进行交易,所以本文也只是抛砖引玉,给大家一个思路方向,具体复盘指标可自行设计(其中股票历史数据基于tushare平台,K线图基于mplfinance库,MACD指标的轮子是用的talib库)。
另外,为什么要用pdf,也是因为个人有一个pad,用pad复盘很容易去画画写写,搞一些技术面分析的线非常方便,并且可以反复复盘,所以还用到了reportlab这个库来生成pdf文件。
本方法用到的数据是基于tushare平台,里面数据很多都可以用,这里我只用了最基础的获取日K的方法,能够将日K的历史数据全部获取,具体方法后续会介绍到,如果想进一步得到股价当日实时数据,还调用了easyquotation库获得当日的分时数据,如果做长期复盘倒也不是很重要。
用到的所有引用如下:
import numpy as np
import pandas as pd
import tushare as ts
import mplfinance as mpf
import os
import easyquotation
from reportlab.lib.pagesizes import A4, landscape
from reportlab.pdfgen import canvas
import matplotlib.pyplot as plt
import datetime
from talib import MACD
quotation = easyquotation.use('sina') # 调用easyquotation接口
ts.set_token('***') # 进入tushare的钥匙
date1 = datetime.datetime.now().strftime('%Y%m%d') # 记录日期,生成文件名备用
为了保证后续中文显示正常,加入这两个
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
首先是建立自己复盘股票信息表格。

我这边里后续程序主要用到的是 股票代码 、 股票名称 、 形态开始时间 、 预设止损价 、 入场时间 、 出场时间 。
利用pandas读取数据
orgdata = pd.read_excel('复盘股票.xlsx', converters={'代码': str, '形态开始': str, '入场': str, '出场': str})
之后,为了生成pdf,先初始化信息。
(w, h) = landscape(A4)
f_pdf = "复盘图/复盘图_%s.pdf" % date1
c = canvas.Canvas(f_pdf, pagesize=(w, h))
dxx = (w-20)
dyy = (h-20)
xx = 10
yy = h-10-dyy
简单解释一下,这几个参数主要用于将生成的K线图导入pdf的位置和大小,以及文件目录。
之后,便是遍历自己的复盘股票信息表,完成读取数据,绘制K线图,写入pdf,我们就把循环里面的内容逐个介绍下。
for ind, ro in orgdata.iterrows():
"""
1. 获取数据
2. 绘制K线
3. 写入pdf
"""
end
首先是获取数据
# 读取数据
code = '%s.%s' % (ro['代码'][2:], ro['代码'][:2])
stock_total = ts.pro_bar(ts_code=code, adj='qfq', start_date="20190101")[["trade_date", "open", "close", "high", 'low', 'vol']][::-1]
# 补充数据
stock_add = quotation.real(code[:-3])[code[:-3]]
nowdate = stock_add['date'].replace('-', '')
if nowdate > stock_total['trade_date'].max():
stock_total = stock_total.append({'trade_date': nowdate,
'open': stock_add["open"],
'close': stock_add["now"],
'high': stock_add["high"],
'low': stock_add["low"],
'vol': stock_add["turnover"]/100,
}, ignore_index=True)
_, _, macdhist = MACD(stock_total["close"], fastperiod=12, slowperiod=26, signalperiod=9)
stock_total["MACD"] = macdhist * 2
# 删选行情
stock_total = stock_total[stock_total["trade_date"] >= ro["形态开始"]]
stock = stock_total.copy()
stock.rename(columns={'trade_date': 'date', 'vol': 'volume'}, inplace=True)
stock["in"] = np.nan
stock.loc[stock["date"] == ro["入场"], "in"] = stock.loc[stock["date"] == ro["入场"], "low"]
stock["out"] = np.nan
stock.loc[stock["date"] == ro["出场"], "out"] = stock.loc[stock["date"] == ro["出场"], "high"]
这里就涉及到tushare的用法了,因为我只用到裸K数据,所以一行代码就搞定了,当然,tushare很多获取数据方法也都是一行搞定,大家如有需要可以去其官网找找。
代码中还用到了补充数据,根据easyquotation获取数据进行的添加当日实时信息。
再就是生成了macdhist即MACD技术指标,后面绘图时会用到。
删除行情就是将不想显示的历史数据进行过滤掉。
stock中"in" 和 "out"字段,则代表入场和出场点,可画出如下所示的图样。

这里有一个小问题,就是在自己的股票信息表中,一行只记录了一只股票的入场出场时间,也就是如果写了同一只股票多次进出场的话只会生成多个K线图,如果想放到同一张K线图里,我这边做了一点小处理。
if code in olddict:
for temp in olddict[code]:
stock.loc[stock["date"] == temp["入场"], "in"] = stock.loc[stock["date"] == temp["入场"], "low"]
stock.loc[stock["date"] == temp["出场"], "out"] = stock.loc[stock["date"] == temp["出场"], "high"]
else:
olddict[code] = []
olddict[code].extend([ro])
if ro["同只股"] == -1:
continue
在股票信息股中加入“同只股”字段,并引入olddict变量储存股票信息,如果碰到同一只股,且“同只股”为-1,说并不想绘制,只记录信息不进行绘制,利用continue完成该轮循环,之后下次再碰到相同股将之前的股进出场信息汇总绘制于当前K线图中,最终可以形成如下样式,即有了多个进出点

接下来是,利用K线图,这里K线图就默认为蜡烛图了,利用mplfinance可以绘制各式各样的K线图,具体也可自行探索。
stock['date'] = pd.to_datetime(stock['date'], format='%Y%m%d')
stock.set_index("date", inplace=True)
add_plot = [
mpf.make_addplot((stock['high'] - stock['low']) / stock['open'], panel=1, color='y'),
mpf.make_addplot(stock["MACD"], panel=2, color='r'),
]
if stock["in"].isnull().sum() < len(stock["in"]):
add_plot.append(mpf.make_addplot(stock["in"], scatter=True, markersize=20, marker='^', color='r'))
if stock["out"].isnull().sum() < len(stock["out"]):
add_plot.append(mpf.make_addplot(stock["out"], scatter=True, markersize=20, marker='v', color='g'))
fname = '复盘图/%s.png' % str(index).zfill(6)
index += 1
save = dict(fname=fname, dpi=600, bbox_inches='tight')
mpf.plot(stock, figsize=(12, 10), type='candle', title='%s-%s' % (ro['名称'], ro['入场'][:6]), datetime_format='%Y-%m-%d',
addplot=add_plot, volume=True, tight_layout=False, savefig=save, ylabel='Profit Rate')
这里就是mplfinance的一些基本用法,整体逻辑就是将日期作为数据索引,后直接调用plot函数绘制K线图和成交量,附加一些其他指标,如MACD、进出场点,就利用addplot的方法,
这里需要注意的是,因为我们后需要生成pdf文件,所以这里先保存生成png图片格式到本地目录中。
最后就是让之前初始化生成的pdf读取该图像文件,最终不断循环将图片导入
# 存入pdf
c.drawImage(fname, xx, yy, dxx, dyy)
# 换页
c.showPage()
循环结束后,生成最终的复盘pdf文件:
c.showPage()
c.save()
os.startfile(os.path.join(os.getcwd(), "复盘图/复盘图_%s.pdf" % date1))
这样就结束了。
再次强调一下,本文只是做了一点简单思路的说明,只是针对我个人的交易策略中技术面分析建立的一个复盘思路,如果大家也有类似的需求,且能够帮助到大家我就非常开心了,毕竟现在网络上还是有很多轮子可以为我们所用,最后祝大家牛年牛运来,全是大牛股!
完整源码我也放这了,大家自行取用~
import numpy as np
import pandas as pd
import tushare as ts
import mplfinance as mpf
import os
import easyquotation
from reportlab.lib.pagesizes import A4, landscape
from reportlab.pdfgen import canvas
import matplotlib.pyplot as plt
import datetime
from talib import MACD
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
ts.set_token('***')
date1 = datetime.datetime.now().strftime('%Y%m%d')
olddict = {}
# 看盘:1,复盘:0
look_re = 0
# 读取数据
orgdata = pd.read_excel('复盘股票.xlsx', converters={'代码': str, '形态开始': str, '入场': str, '出场': str})
# 当日补充信息
quotation = easyquotation.use('sina')
# 生成pdf信息
(w, h) = landscape(A4)
f_pdf = "复盘图/复盘图_%s.pdf" % date1
c = canvas.Canvas(f_pdf, pagesize=(w, h))
dxx = (w-20)
dyy = (h-20)
xx = 10
yy = h-10-dyy
index = 0
for ind, ro in orgdata.iterrows():
print(ind+1, len(orgdata), ro["名称"])
# 读取数据
code = '%s.%s' % (ro['代码'][2:], ro['代码'][:2])
stock_total = ts.pro_bar(ts_code=code, adj='qfq', start_date="20190101")[["trade_date", "open", "close", "high", 'low', 'vol']][::-1]
# 补充数据
stock_add = quotation.real(code[:-3])[code[:-3]]
nowdate = stock_add['date'].replace('-', '')
if nowdate > stock_total['trade_date'].max():
stock_total = stock_total.append({'trade_date': nowdate,
'open': stock_add["open"],
'close': stock_add["now"],
'high': stock_add["high"],
'low': stock_add["low"],
'vol': stock_add["turnover"]/100,
}, ignore_index=True)
_, _, macdhist = MACD(stock_total["close"], fastperiod=12, slowperiod=26, signalperiod=9)
stock_total["MACD"] = macdhist * 2
# 删选行情
stock_total = stock_total[stock_total["trade_date"] >= ro["形态开始"]]
stock = stock_total.copy()
stock.rename(columns={'trade_date': 'date', 'vol': 'volume'}, inplace=True)
stock["in"] = np.nan
stock.loc[stock["date"] == ro["入场"], "in"] = stock.loc[stock["date"] == ro["入场"], "low"]
stock["out"] = np.nan
stock.loc[stock["date"] == ro["出场"], "out"] = stock.loc[stock["date"] == ro["出场"], "high"]
# 如果之前有此股票,则增加入场
if code in olddict:
for temp in olddict[code]:
stock.loc[stock["date"] == temp["入场"], "in"] = stock.loc[stock["date"] == temp["入场"], "low"]
stock.loc[stock["date"] == temp["出场"], "out"] = stock.loc[stock["date"] == temp["出场"], "high"]
else:
olddict[code] = []
olddict[code].extend([ro])
if ro["预设止损价"] == -1:
continue
stock['date'] = pd.to_datetime(stock['date'], format='%Y%m%d')
stock.set_index("date", inplace=True)
add_plot = [
mpf.make_addplot((stock['high'] - stock['low']) / stock['open'], panel=1, color='y'),
mpf.make_addplot(stock["MACD"], panel=2, color='r'),
]
if stock["in"].isnull().sum() < len(stock["in"]):
add_plot.append(mpf.make_addplot(stock["in"], scatter=True, markersize=20, marker='^', color='r'))
if stock["out"].isnull().sum() < len(stock["out"]):
add_plot.append(mpf.make_addplot(stock["out"], scatter=True, markersize=20, marker='v', color='g'))
fname = '复盘图/%s.png' % str(index).zfill(6)
index += 1
save = dict(fname=fname, dpi=600, bbox_inches='tight')
mpf.plot(stock, figsize=(12, 10), type='candle', title='%s-%s' % (ro['名称'], ro['入场'][:6]), datetime_format='%Y-%m-%d',
addplot=add_plot, volume=True, tight_layout=False, savefig=save, ylabel='Profit Rate')
# 存入pdf
c.drawImage(fname, xx, yy, dxx, dyy)
# 换页
c.showPage()
c.showPage()
c.save()
os.startfile(os.path.join(os.getcwd(), "复盘图/复盘图_%s.pdf" % date1))