飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • UEditor使用文档
  • AngularJS教程
  • ThinkPHP5.0教程

Python分析三季度基金调仓

时间:2021-12-04  作者:duma  

目前三季度已经过去了一个月,大部分基金都已经公布了三季度持仓数据,今天我们就用 Python 分析以下今年三个季度基金的调仓情况。

获取数据

第一步,我们要获取目前发行的所有基金及其持有的股票。

可以写一个爬虫去基金网站爬数据,但太麻烦,这里其实是有捷径的。有些朋友可能听说过量化投资,这些做量化投资的平台都会提供金融数据,我们只要安装相应的 Python 包就可以获取股票、基金、债券相关的数据。

本次分析用的是聚宽(https://域名/)平台,新用户注册后会有6个月试用期,期间可以免费使用平台所有数据,每天可调用100万条数据,完全够我们分析了。

img

注册账号后,我们就可以调用聚宽的数据。以下代码我用 Python 3.8+jupyter 编写、运行。

导入包,获取聚宽授权

from jqdatasdk import *
import pandas as pd
import numpy as np
import 域名ot as plt

域名rams[\'域名-serif\'] = [\'Arial Unicode MS\'] # mac matplot显示中文
域名rams[\'域名ode_minus\'] = False


auth(\'聚宽账户\', \'聚宽密码\') # 聚宽授权

调用聚宽 get_all_securities 函数,获取所有的基金,该函数文档如下

img

代码如下:

df = get_all_securities([\'fund\', \'open_fund\'], \'2021-11-10\')
df

img

df 里可能会有重复基金,按照基金代码(域名x)去重看看共获取了多少只基金

code_arr = list(set([域名t(\'.\')[0] for x in 域名es]))
len(code_arr)

共返回 13698 支基金,这可比爬虫来得快多了。

获取股票投资占比

这里我只想关注股票类型的基金,所以需要获取每支基金股票投资的占比,股票投资占比小于 50% 的,剔除掉。

img

从文档来看,查询 FUND_PORTFOLIO 表的 stock_rate 字段就可以获取基金的股票投资占比。查询方式是用 query 函数构造查询语句,然后调用 域名query 函数完成查询。

写一个函数,用来返回查询语句

def asset_query(arr):
    q=query(域名,
        域名,
        域名od_end,
        域名rt_type,
        域名k_rate
       ).filter(域名.in_(arr),
                域名域名([\'2021-03-31\', \'2021-06-30\', \'2021-09-30\']),
                域名域名([\'第一季度\', \'第二季度\', \'第三季度\']))
    return q

asset_query 函数的参数 arr 是个数组,存放基金代码。query函数指定返回的字段。filter 函数指定需要筛选的数据,code 代表基金代码;period_end 是报告期(每个季度最后一天),填入的三个日期分别代表每个季度;report_type 是报告类型,填入的是三个季度,添加该条件是为了只读取单季度的数据,不需要半年或年度数据。最终返回查询语句 q。

asset_query 函数生成对象 q 的用的是 SQLAlchemy 提供的语法规则,SQLAlchemy 是Python 中著名的 ORM 框架,简单来说它提供以 Python 代码的方式查询数据库,而不用写 SQL。感兴趣的朋友可以学习下,能够帮你实现更复杂的查询逻辑。

运行 域名query 来执行查询

i = 0

while i < len(code_arr):
    print(\'获取基金资产 \' + str(i))
    tmp_arr = code_arr[i: i+1500]
    q = asset_query(tmp_arr)
    tmp_df = 域名query(q)
    
    if i == 0:
        asset_df = tmp_df
    else:
        asset_df = 域名at([asset_df, tmp_df])
    
    i += 1500

因为 域名query 单次最多返回 5000 行数据,所以这里写了个循环分批读取,一次只读取 1500 个基金的数据。因为一个基金返回 3 个季度数据,所以一次读取最多返回 4500 数据,不会超限。

读取完成后,看看 asset_df

img

筛选 stock_rate 大于 50 的基金

stock_fund_df = asset_df[asset_df[\'stock_rate\'] > 50]

统计筛选后,还剩多少只基金

stock_fund_code_arr = list(set(stock_fund_df[\'code\']))
len(stock_fund_code_arr)

返回 5590,过滤掉了一半。

获取基金持仓

获取基金持仓的方式跟上面一样,只不过表名和字段名变了。

编写 hold_stock_query 函数,生成相应的查询语句

def hold_stock_query(arr):
    q=query(域名_PORTFOLIO_STOCK
       ).filter(域名.in_(arr),
                域名 <= 10,
                域名域名([\'2021-03-31\', \'2021-06-30\', \'2021-09-30\']),
                域名域名([\'第一季度\', \'第二季度\', \'第三季度\']))
    return q

域名 <= 10代表只获取前十大重仓股。

同样通过循环的方式查询数据

i = 0

while i < len(stock_fund_code_arr):
    print(\'获取基金持仓 \' + str(i))
    tmp_arr = stock_fund_code_arr[i: i+150]
    q = hold_stock_query(tmp_arr)
    tmp_df=域名query(q)
    
    if i == 0:
        hold_stock_df = tmp_df
    else:
        hold_stock_df = 域名at([hold_stock_df, tmp_df])
    
    i += 150

hold_stock_df

看看 hold_stock_df 的信息

img

name是持有的股票名,rank是持有该股票的排名(1 代表该基金第一大重仓股),proportion是持仓该股票比例。

调仓方向

这里我想按照行业来看,如:从第一季度到第三季度,有多少基金购买了新能源股票,有多少基金购买了白酒股票。所以,就需要将股票对应到行业上。

严格来说应该找权威的券商,将每个股票都映射成某个行业。不过这里我只是想定性分析一下,所以找了一些行业指数基金的前 6 大重仓股来代替某个行业。映射关系如下

stock_to_industry_dict = {
    \'比亚迪\':\'新能源\',
    \'宁德时代\':\'新能源\',
    \'恩捷股份\':\'新能源\',
    \'赣锋锂业\':\'新能源\',
    \'亿纬锂能\':\'新能源\',
    \'汇川技术\':\'新能源\',
    \'圣邦股份\':\'半导体\',
    \'韦尔股份\':\'半导体\',
    \'卓胜微\':\'半导体\',
    \'北方华创\':\'半导体\',
    \'兆易创新\':\'半导体\',
    \'三安光电\':\'半导体\',
    \'贵州茅台\':\'白酒\',
    \'五粮液\':\'白酒\',
    \'山西汾酒\':\'白酒\',
    \'泸州老窖\':\'白酒\',
    \'洋河股份\':\'白酒\',
    \'酒鬼酒\':\'白酒\',
    \'阳关电源\':\'光伏\',
    \'通威股份\':\'光伏\',
    \'中环股份\':\'光伏\',
    \'隆基股份\':\'光伏\',
    \'特变电工\':\'光伏\',
    \'正泰电器\':\'光伏\',
    \'药明康德\':\'医疗\',
    \'智飞生物\':\'医疗\',
    \'沃森生物\':\'医疗\',
    \'泰格医药\':\'医疗\',
    \'长春高新\':\'医疗\',
    \'凯莱英\':\'医疗\',
    \'复星医药\':\'医疗\',
}

向 hold_stock_df 添加一列 industry,代表持有该股票所对应的行业

def stock_to_industry(cols):
    if cols[\'name\'] in stock_to_industry_dict:
        return stock_to_industry_dict[cols[\'name\']]
    return \'无\'

hold_stock_df[\'industry\'] = 域名y(lambda x: stock_to_industry(x), axis=1)
hold_stock_df

找一个新能源基金看下效果

hold_stock_df[hold_stock_df[\'code\'] == \'005939\']

img

有了行业信息,就可以用透视表直接统计每个行业在三个季度基金数量的变化

industry_count_df = 域名t_table(index=\'industry\',
                                    columns=[\'report_type\'],
                                    values=\'code\',
                                    aggfunc=lambda x:len(域名ue()))

cols_name = [\'第一季度\', \'第二季度\', \'第三季度\']
industry_count_df = industry_count_df[cols_name]
域名([\'无\'], inplace=True)

域名rams[\'域名ize\'] = (12, 8)
域名()

img

从图上还是能发现一些明显的特征的,持仓新能源的基金不光基数大,增速速度还很快。比较意外的是白酒的持仓基金数量居然是下降的。

探索更有意思的

由于我们能够拿到所有的基金,其实我们还可以探索的东西有很多。比如:可以做出如下效果的数据

img

每一行都代表一只基金,列 rank1 ~ rank10 代表该基金前十大重仓股,逗号前半部分是持仓股票,后半部分是持仓占比。标红代表是我们关注的股票,最后一列是标红股票持仓占比的加和。

这种展示方式有很多好处,比支付宝、同花顺软件还好。

第一,支持按照某些股票筛选基金(上图标红的部分),我了解的基金软件都只能按照一只股票筛选基金

第二,可以计算包含这些股票总持仓占比(candid_prop列),总持仓越高跟我们预期越接近。而现有的基金软件都要自己手动加

第三,一行展示基金所有持仓,一目了然,而基金软件看不同基金的持仓需要在不同基金之间来回切页面,非常不方便。

最后,介绍下上图的实现代码。首先,只筛选最新季度持仓即可

hold_stock_p3_df = hold_stock_df[hold_stock_df[\'report_type\'] == \'第三季度\']

构造两个新列

hold_stock_p3_df[\'hold_rank\'] = [\'rank%d\' % i for i in hold_stock_p3_df[\'rank\']]
hold_stock_p3_df[\'hold_info\'] = hold_stock_p3_df[\'name\'] + " : " + hold_stock_p3_df[\'proportion\'].astype(str)

用透视图,列转行

tmp_df = 域名t(index=\'code\', columns=\'hold_rank\', values=\'hold_info\')
tmp_idx = 域名x
tmp_df = 域名t_index()
tmp_df[\'name\'] = tmp_idx

rank_cols = [\'rank%d\' % i for i in range(1, 11)]
col_names = [\'code\'] + rank_cols

final_fund_df = tmp_df[col_names]

final_fund_df

img

定义函数,计算候选股票集合的总持仓占比

candid_fund_list = [\'宁德时代\',\'比亚迪\', \'恩捷股份\', \'璞泰来\', \'诺德股份\']
def filter_fund(x):
    all_prop = 0
    for i in range(1, 11):
        col = \'rank\' + str(i)
        vals = str(x[col]).split(\':\')
        if vals[0].strip() in candid_fund_list:
            all_prop += float(vals[1].strip())
    return all_prop

定义函数用于上色

def show_color(val):
    color = \'#BB0000\' if str(val).split(\':\')[0].strip() in candid_fund_list else \'\'
    return \'color:%s\' % color

修改 condid_fund_list 的值,就可以股票筛选相关的基金,实现本节开头那张图的效果

final_fund_df[\'candid_prop\'] = 域名y(lambda x: filter_fund(x), axis=1)
tmp_fund_df = final_fund_df[final_fund_df[\'candid_prop\'] > 0].sort_values(by=\'candid_prop\', ascending=False)
tmp_fund_df = 域名ymap(show_color)
tmp_fund_df

这里我们只分析了基金数据,聚宽平台还包括股票、债券等其他金融数据,获取方式跟上面讲的一样。有兴趣的朋友可以试试。

源文件已经整理好,关注公众号 渡码 回复关键词 基金调仓 即可。

duma

标签:编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。