跳转至

个股研报事件策略&选股结果自动推送到微信

基于东方财富网的个股研报数据进行事件策略选股,并实现自动化筛选股票和推送选股结果。

image-20221023210513295

App

策略逻辑:选择当天发布的研报中评级为“买入”(最高评级)的股票,按照“近一个月个股研报数目”降序排列,选择前若干只股票进行等权买入。

将策略代码部署到Streamlit上,定期执行事件选股策略并将选股结果推送到微信,可以大大节省重复运行策略代码的时间,提高交易的执行效率。

演示地址

东方财富网个股研报数据爬虫

Python
from urllib.request import urlopen  # python 自带爬虫库
import json  # python 自带的 JSON 数据库
import pandas as pd
import time
import datetime

def change(x):
    if x[:1] == '6' or x[:3] == '900':
        return 'sh' + x
    elif x[:2] == '00' or x[:2] == '30' or x[:3] == '200':
        return 'sz' + x
    else:
        return x

def get_research_report_data(begin_time, until_time):
    """
    获取个股研报历史数据,设置起始时间和结束时间
    :param until_time:
    :param begin_time: 起始时间 格式为'2020-06-01'
    :param until_time: 起始时间 格式为'2020-06-01'
    :return:
    """
    # http://data.eastmoney.com/report/stock.jshtml
    if len(begin_time.split("-")) != 3:
        print("begin_tiem_str 格式有误 格式为’%Y-%m-%d‘")
        return None

    try:
        ts = int(time.time() * 1000)
        if until_time is None:
            until_time = now_date.strftime("%Y-%m-%d")
        pageNum = 1
        data = []

        while True:
            url_1 = "http://reportapi.eastmoney.com/report/list?cb=datatable4263982&industryCode=*&pageSize=100&industry=*&rating=*&ratingChange=*&beginTime=%s&endTime=%s&pageNo=%d&fields=&qType=0&orgCode=&code=*&rcode=&_=%d"
            url_stockResearch = url_1 % (begin_time, until_time, pageNum, ts)
            content = urlopen(url_stockResearch).read().decode("utf-8")

            content = content.split("(", maxsplit=1)[-1][:-1]  # 去掉括号
            # content_0 = content_0.split(")", maxsplit=1)[0]  # 去掉右括号
            content_1 = json.loads(content)  # 自己去仔细看下这里面有什么数据
            if content_1["data"]:
                df = pd.DataFrame(content_1["data"])
            else:
                print('今日个股研报无数据')
                break
            df_0 = df[
                ["stockName", "stockCode", "title", "orgName", "orgSName", "predictThisYearPe", "predictThisYearEps",
                 "predictNextYearPe", "predictNextYearEps", "predictNextTwoYearPe", "predictNextTwoYearEps",
                 "indvInduName", "indvInduCode", "emRatingName", "lastEmRatingName", "count",
                 "publishDate"]].copy()
            df_0.rename(columns={
                "stockName": "股票名称",
                "stockCode": "股票代码",
                "title": "研报标题",
                "orgName": "机构",
                "orgSName": "机构名称",
                "predictThisYearPe": "预测今年市盈率",
                "predictThisYearEps": "预测今年每股收益",
                "predictNextYearPe": "预测明年市盈率",
                "predictNextYearEps": "预测明年每股收益",
                "predictNextTwoYearPe": "预测后年市盈率",
                "predictNextTwoYearEps": "预测后年每股收益",
                "indvInduName": "行业名称",
                "indvInduCode": "行业代码",
                "emRatingName": "评级",
                "lastEmRatingName": "上次评级",
                "count": "近一个月个股研报数目",
                "publishDate": "发布日期"
            }, inplace=True)
            data.append(df_0)
            print("已爬取第%d页,当前页有%d行数据" % (pageNum, len(df_0)))
            pageNum += 1
            time.sleep(1)
        if not data:
            return None
        else:
            total = pd.concat(data, ignore_index=True)
            total['股票代码'] = total['股票代码'].apply(lambda x: change(x))
            return total
    except Exception as e:
        print("报错,报错内容:", str(e))
        return None

获取指定日期范围的个股研报数据(默认为当日)

Python
now_date = datetime.datetime.now()
begin_time = st.sidebar.date_input(
    "起始日期",
    now_date)
until_time = st.sidebar.date_input(
    "结束日期",
    now_date)

begin_time_str = begin_time.strftime("%Y-%m-%d")
until_time_str = until_time.strftime("%Y-%m-%d")

data = get_research_report_data(begin_time_str, until_time_str)

通过Akshare获取股票最新行情数据

Python
import akshare as ak

def get_price():
    return ak.stock_zh_a_spot_em()

设置选股条件

Python
st.sidebar.write('设置选股条件')
# 设置选股条件,对股票进行筛选
condition = None
# 评级为最高等级的买入
if st.sidebar.checkbox("投资评级为买入", True):
    condition = (data['评级'] == '买入')
# 应用选股条件
selected_stock = data[condition]
if st.sidebar.checkbox("选择研报数量最多的股票", True):
    selected_stock = selected_stock.sort_values(by='近一个月个股研报数目', ascending=False)
# 删除重复的股票
selected_stock.drop_duplicates(subset=['股票代码'],keep='first', inplace=True)
# 重新索引
selected_stock.index = range(len(selected_stock))

# 设置买入股票数量
buy_num = st.sidebar.number_input("买入股票数量", 1, None, 5)
# 求出买入股票的代码和名称
buy_stock = selected_stock[['股票代码', '股票名称', '近一个月个股研报数目']][:buy_num]

结合最新价,计算应买入的股票数量

Python
# 每只股票买入金额
buy_money = st.sidebar.number_input("每只股票买入金额", 1, None, 100000, 10000)
# 删除代码中的前缀
buy_stock['代码'] = buy_stock['股票代码'].apply(lambda x: x[2:])
# 获取股票的实时数据
price = get_price()
# 将最新价合并到买入股票的代码和名称中
buy_stock = pd.merge(buy_stock, price, on='代码', how='left')
# 计算每只股票买入的股数
buy_stock['买入股数'] = buy_stock['最新价'].apply(lambda x: int((buy_money // x) // 100 * 100))

通过pushplus推送到微信

Python
# 开始推动到 pushplus
if st.button("推送到微信"):
    # 推送标题
    title= now_date.strftime("%Y-%m-%d") + "个股研报策略选股结果"
    # 将 buy_stock 转换为 Markdown 格式
    buy_stock_md = buy_stock.to_markdown(index=False)
    # 推送内容
    content = buy_stock_md
    template = 'markdown'
    headers = {'Content-Type':'application/json'}
    url = 'http://www.pushplus.plus/send?token='+token+'&title='+title+'&content='+content+'&template='+template
    requests.get(url)
    st.success("推送成功")

推送到微信:

image-20221023210918610

点击消息后可看到选股结果表格:

image-20221023211001264

评论