Python获取全部基金前十大持仓股并进行选股分析

选股逻辑

股市有风险,投资需谨慎。

投资方向:跟随基金机构进行选股,简单来说,就是优先筛选那些基金公司重仓持有的股票。

目标设定

获取全部基金前十大持仓股的持股数、市值等信息,分析出排名比较靠前的基金重仓股。

爬取全部基金代码信息

注:使用python内置sqlite数据库

建表存储基金代码信息

1
2
3
4
5
6
7
8
9
10
11
lua复制代码-- 全部标的信息
create table if not exists targets_all
(
  uid     string,
  name     string,
  id       string,
  category string,
  tag     string,
  type     string,
  version  datetime not null default (datetime('now', 'localtime'))
);

参考:juejin.cn/post/699352…

image-20210816231040667

根据基金代码获取相应持仓信息

建表存储持仓信息

1
2
3
4
5
6
7
8
9
10
11
12
13
php复制代码-- 基金前十大持仓股
create table if not exists top10_stocks_in_fund
(
  uid string,
  fund_id string,
  rank_num integer,
  stock_id string,
  stock_name string,
  hold_rate float,
  hold_count float,
  hold_value float,
  update_time string
)

爬取并存入数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
python复制代码# coding:utf-8
import logging
from collections import defaultdict
from bs4 import BeautifulSoup as BS
import requests
import uuid

from common.constant import CONN_KEY
from util import db_util
from common import global_var as gv

logging.basicConfig(level=logging.INFO)


# 将请求到的数据进行解析
def _parse_content(content):
   result = content.replace("var apidata={", "")
   result = result[:-2]

   content = result.split("",arryear:")[0].replace("content:"", "")
   content = BS(content).find_all("tr")

   result = defaultdict(list)

   for tr in content:
       date = tr.parent.parent.previous_sibling.previous_sibling.find("font").text
       td = tr.find_all("td")
       if len(td) == 9:
           result[date].append({
               "rank_num": td[0].text,
               "stock_id": td[1].text,
               "stock_name": td[2].text,
               "hold_rate": float(td[6].text.replace("%", ""))/100,
               "hold_count": td[7].text,
               "hold_value": eval(td[8].text.replace(",", ""))
          })
       elif len(td) == 7:
           result[date].append({
               "rank_num": td[0].text,
               "stock_id": td[1].text,
               "stock_name": td[2].text,
               "hold_rate": float(td[4].text.replace("%", ""))/100,
               "hold_count": td[5].text,
               "hold_value": eval(td[6].text.replace(",", ""))
          })

   return result


# 根据基金代码获取基金持仓
def get_top10_stocks_by_fund(fund_code):
   base_url = "https://fundf10.eastmoney.com/FundArchivesDatas.aspx?type=jjcc&code={fund_code}&topline=10"
   url = base_url.format(fund_code=fund_code)
   response = str(requests.get(url).content, "utf-8")
   return _parse_content(response)


# 判断当前基金的股票持仓是否存在
def _is_exist(conn, fund_id, stock_id, update_time):
   query_sql = f"select 1 from top10_stocks_in_fund " \
               f"where fund_id = '{fund_id}'" \
               f"and stock_id = '{stock_id}'" \
               f"and update_time = '{update_time}'"

   res = conn.query(query_sql)
   if res.fetchone():
       return True
   return False


# 更新基金前10大持仓股信息
def update_top10_stocks_by_fund():
   # 初始化数据库连接池
   db_util.init_conn_pool()
   conn = gv.get_value(CONN_KEY)
   query_sql = "select * from targets_all where type = '基金'"
   targets = conn.query(query_sql)

   # 记录更新成功和失败的条数
   success_count = 0
   failure_count = 0

   for (uid, fund_name, fund_id, category, tag, type, version) in targets:
       fund_id = str(fund_id).zfill(6)
       logging.info(f"正在请求{fund_name}-{fund_id}的持仓股信息...")
       stocks_in_fund = get_top10_stocks_by_fund(fund_id)

       # 获取持仓信息
       for update_time, stocks in stocks_in_fund.items():
           # 遍历各条持股信息
           for stock in stocks:
               # 若当前持股信息已存在,则不再插入
               if _is_exist(conn, fund_id, stock["stock_id"], update_time):
                   continue

               # 插入新的持股信息
               insert_sql = f"insert into top10_stocks_in_fund" \
                            f"(uid,fund_id,rank_num,stock_id,stock_name,hold_rate,hold_count,hold_value,update_time)" \
                            f"values('{uuid.uuid4()}','{fund_id}',{stock['rank_num']}," \
                            f"'{stock['stock_id']}','{stock['stock_name']}',{stock['hold_rate']}," \
                            f"{stock['hold_count']},{stock['hold_value']},'{update_time}')"

               if conn.operate(insert_sql):
                   logging.info("插入成功!")
                   success_count = success_count+1
               else:
                   logging.warning(f"插入失败:{insert_sql}")
                   failure_count = failure_count+1

   logging.info(f"全部基金持仓信息更新完毕,成功:{success_count}条,失败:{failure_count}条。")


if __name__ == "__main__":
   update_top10_stocks_by_fund()

image-20210816231409806

分析第一/二季度基金排名靠前的重仓股

1
2
3
4
5
sql复制代码select stock_id "股票代码", stock_name "股票名称", cast(sum(hold_value) as double) "基金持有总市值"
from top10_stocks_in_fund
where update_time = '2021-06-30'
group by 1, 2
order by 3 desc

image-20210816232230853

1
2
3
4
5
sql复制代码select stock_id "股票代码", stock_name "股票名称", cast(sum(hold_count) as double) "基金持有数(万股)"
from top10_stocks_in_fund
where update_time = '2021-06-30'
group by 1, 2
order by 3 desc

image-20210816232322994

结论

金融市场偏好能源消费、银行券商等行业龙头股,深入研究持股数和市值变化,可以为加仓换股提供比较有意义的参考。(注:基金持仓信息大多是每季度末更新,距离越久,参考意义越不大)

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%