用python来自动生成word版投资报告
从4月1日起,我开始把跟踪的几个股市策略的每日成果与下一日买卖计划发到微信公众号(比如今日投资总结与明日买卖计划)。作为一个python工程师,尽管不太纯正,在这个RPA大火的时代,当然不会傻傻地每天手写《今日投资总结与明日买卖计划》,而是用python生成txt文件,然后通过markdown转成微信公众号文档。但现在的发布很不直观,最好能把每个策略的趋势图加上去,这就不是txt能实现的了,有必要直接写到word文档里去,直接用Python来生成word版的投资报告。
1. 原来的代码
# -*- coding: utf-8 -*-
"""
Created on Sun Apir 3 22:36:43 2022
@author: Charles
"""
import pandas as pd
from sqlalchemy import create_engine
import datetime
text = "今日上证指数"
engine=create_engine('mysql+pymysql://root:password@localhost:3306/stock')
SSEC = pd.read_sql("select close from SSEC order by trade_date desc limit 0,2", engine)
if SSEC['close'][0]>SSEC['close'][1]:
text += ("上涨" + str(round(SSEC['close'][0]-SSEC['close'][1],2)) + "点,涨幅" + str(round(SSEC['close'][0]/SSEC['close'][1]*100-100,2)) + '%')
elif SSEC['close'][1]>SSEC['close'][0]:
text += "下跌" + str(round(SSEC['close'][1]-SSEC['close'][0],2)) + "点,跌幅" + str(round(100 - SSEC['close'][0]/SSEC['close'][1]*100, 2)) + '%'
else:
text += "不涨不跌"
text += '。\n'
text += '\n今日各投资策略的表现如下:<br>\n'
strategies = pd.read_sql("select id, annual_return, sharpe, amount, accumulated_return from strategies order by sharpe desc", engine)
text += '|序号|年化收益率|夏普比率|累计收益|今日浮盈|今日涨幅|\n|---|---|---|---|---|---|\n'
text1 = ""
xh = 1
current_date = datetime.datetime.strftime(datetime.datetime.now(), '%Y%m%d')
for i in strategies.index:
amount = pd.read_sql("select trade_date, amount from strategy_data where strategy=%d order by trade_date desc limit 0,2" % strategies['id'][i], engine)
today_get = round(amount['amount'][0]-amount['amount'][1],2)
today_increase = round(amount['amount'][0]/amount['amount'][1]*100-100,2)
text += '|' + str(strategies['id'][i]) + '|' + str(strategies['annual_return'][i]) + '|' + str(strategies['sharpe'][i]) + '|' + str(strategies['accumulated_return'][i]) +'|' + str(today_get) + '|' + str(today_increase) + '%|\n'
text1 += '# ' + str(xh) + '. ' + str(strategies['id'][i]) + '号策略\n'
text1 += '## ' + str(xh) + '.1. 今日买入\n'
buy = pd.read_sql("select distinct ts_code, buy_price from hold_detail where strategy=%d and buy_date=%d" % (strategies['id'][i], int(current_date)), engine)
if len(buy):
for j in buy.index:
text1 += '代码:' + buy['ts_code'][j] + ' ' + '价格:' + str(buy['buy_price'][j]) + '<br>\n'
else:
text1 += '\n'
text1 += '## ' + str(xh) + '.2. 今日卖出\n'
sale = pd.read_sql("select distinct ts_code, sale_price from hold_detail where strategy=%d and sale_date=%d and sale_price>0" % (strategies['id'][i], int(current_date)), engine)
if len(sale):
for j in sale.index:
text1 += '代码:' + sale['ts_code'][j] + ' ' + '价格:' + str(sale['sale_price'][j]) + '<br>\n'
else:
text1 += '\n'
text1 += '## ' + str(xh) + '.3. 今日浮盈:' + str(today_get) + '\n'
text1 += '## ' + str(xh) + '.4. 明日计划买入\n'
plan_buy = pd.read_sql("select distinct ts_code, price from plan where strategy=%d and direction='b' and date=%d" % (strategies['id'][i], int(current_date)), engine)
if len(plan_buy):
for j in plan_buy.index:
text1 += '代码:' + plan_buy['ts_code'][j] + ' ' + '价格:' + str(plan_buy['price'][j]) + '<br>\n'
else:
text1 += '\n'
text1 += '## ' + str(xh) + '.5. 明日计划卖出\n'
plan_sale = pd.read_sql("select distinct ts_code, min(price) as price from plan where strategy=%d and direction='s' and date=%d group by ts_code" % (strategies['id'][i], int(current_date)), engine)
if len(plan_sale):
for j in plan_sale.index:
text1 += '代码:' + plan_sale['ts_code'][j] + ' ' + '价格:' + str(plan_sale['price'][j]) + '<br>\n'
else:
text1 += '\n'
xh += 1
text1 += '\n\n**免责声明:**入市有风险,投资需谨慎。在任何情况下,本文的内容、信息及数据或所表述的意见并不构成对任何人的投资建议,作者不对任何人因使用本文的任何内容所引致的任何损失负任何责任。'
with open('offical_account.text', 'w') as f:
f.write(text+text1)
print('done!')
2. 安装并导入包
python写word文档可以用python-docx包来实现,首先安装包。
pip install python-docx
然后导入python-docx包下需要的操作,注意这个包的名字是python-docx,但导入的却是docx。
from docx import Document
from docx.shared import Inches
3. 创建word文档并写入各个段落
首先创建word文档。
doc = Document()
然后写入各个段落,只需要将原来每一段的开头改写成add_paragraph,并把第一段的继续改写成add_run即可。
比如把
text = "今日上证指数"
改写成
par = doc.add_paragraph("今日上证指数")
把
text += ("上涨" + str(round(SSEC['close'][0]-SSEC['close'][1],2)) + "点,涨幅" + str(round(SSEC['close'][0]/SSEC['close'][1]*100-100,2)) + '%')
改写成
par.add_run("上涨" + str(round(SSEC['close'][0]-SSEC['close'][1],2)) + "点,涨幅" + str(round(SSEC['close'][0]/SSEC['close'][1]*100-100,2)) + '%')
最后,把word文档进行保存。
doc.save(f'offical_account.doc')
如此,一个简单的word文档就生成了。
4. 调整段落格式
可以看到上述生成的word文档很奇怪,最突出的是有很多空行,这是原来为适应markdown格式而留下的,只需要去掉原来生成文档里特地加上去的"\n"、"
"和"#"等就行了。
调整后结果如下:
5. 调整字体和字号
尽管段落间的多余空行已经去掉,但字体和字号都是一样的,看起来很不美观。
简单的做法是将原来的部分段落改成标题。
比如把
doc.add_paragraph(str(xh) + '. ' + str(strategies['id'][i]) + '号策略')
改成
doc.add_heading(str(xh) + '. ' + str(strategies['id'][i]) + '号策略', 0)
后面的数字表示几号标题,有0-9共10个等级的标题供选择。
6. 插入表格
可以看到格式已经基本可以了,但原来的表格还没有插入。
首先插入表格并写入表头。
table = doc.add_table(1, 6)
table_title = ['序号', '年化收益率', '夏普比率', '累计收益', '今日浮盈', '今日涨幅']
for i in range(len(table_title)):
table.cell(0, i).text = table_title[i]
然后在每个策略下加上
table.add_row()
table.cell(row, 0).text = str(strategies['id'][i])
table.cell(row, 1).text = str(strategies['annual_return'][i])
table.cell(row, 2).text = str(strategies['sharpe'][i])
table.cell(row, 3).text = str(strategies['accumulated_return'][i])
table.cell(row, 4).text = str(today_get)
table.cell(row, 5).text = str(today_increase)
row += 1
7. 插入图片
首先准备大盘数据
SSEC = pd.read_sql("select trade_date, close from SSEC order by trade_date", engine)
然后与各策略数据做交集
amount = pd.read_sql("select trade_date, amount from strategy_data where strategy=%d order by trade_date" % strategies['id'][i], engine)
amount = pd.merge(amount, SSEC, on='trade_date')
amount['amount'] = amount['amount'] / list(amount['amount'])[0]
amount['close'] = amount['close'] / list(amount['close'])[0]
再然后调用matplotlib画拆线图
import matplotlib.pyplot as plt
fig = plt.figure(0)
plt.plot(amount['trade_date'].to_list(), amount['amount'].to_list(), label=str(strategies['id'][i])+'号策略')
plt.plot(amount['trade_date'].to_list(), amount['close'].to_list(), label='上证指数')
plt.legend()
plt.savefig('趋势图.png')
plt.close(0)
doc.add_picture('趋势图.png')
如此,图已经画成了,但X轴的时间出现了重叠。我们可以把X轴的日期斜过来。
plt.xticks(rotation=15)
如此,图片插入完成。
8. 后记
我们的word文档生成完成了,但word无法直接复制到公众号,复制过去格式就会丢失。
所以,从自动发公众号的视角来说,这一次的尝试并不是很成功,但从python生成word报告来说已经基本完成了,还是值得记录的。
- 点赞
- 收藏
- 关注作者
评论(0)