社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

【量化投资】运用Python分析公募基金

蚂蚁学Python • 4 年前 • 470 次点击  

运用Python分析公募基金

1、背景

学校财富管理课程的期末论文是分析中国各种投资标的的收益,笔者分配到的研究的细分类别为:通过大集合转公募基金的方式,成立的公募基金的收益分析。Python在量化投资,尤其是投资的分析、策略回测等方面有着广泛的运用,所以笔者结合在帅帅老师课程中学习的知识,运用Python对基金的收益进行分析。

2、数据来源

“巧妇难为无米之炊”,寻找高质量的数据是分析的第一步。本文的数据来自于Wind客户端。数据分为两个:

链接:https://pan.baidu.com/s/1JzJWxM9CyxTotldu5BjbjA 提取码:clki

3、数据分析

3.1 导入数据

def day_data(self):
    day_data = pd.read_csv(root_path+'/data/大集合转公募基金复权净值day.CSV', encoding='gbk')
    day_data.rename(columns={'Unnamed: 0''交易日期'}, inplace=True)
    day_data['交易日期'] = pd.to_datetime(day_data['交易日期'])
    #day_rtn.set_index('交易日期', inplace=True)
    day_data.dropna(axis=1, how='any', inplace=True)
    return day_data

def index_day_data(self):
    index_day_rtn = pd.read_csv(root_path+'/data/中国基金加权指数day.csv',
                                 encoding='gbk')
    index_day_rtn.rename(columns={'Unnamed: 0''交易日期'}, inplace=True)
    index_day_rtn['交易日期'] = pd.to_datetime(index_day_rtn['交易日期'])
    index_day_rtn['中国基金加权总指数'] = index_day_rtn['中国基金加权总指数'].pct_change()
    return index_day_rtn

在导入数据时,我们发现有许多缺失值,这是因为大部分大集合在2021年才转为公募基金,所以仅有几个月的收益。我们在此处采取最简单的数据清洗方式:将含有缺失值的基金删除。

这是整理后的数据:

3.2数据信息提取

观察数据,发现这些公募基金的名字既长又复杂,分析的时候一个一个输入名字肯定非常费时间。通过观察发现,这些基金的名字有个特点:基金名字的前两个或多个字,为基金公司的名字。如:海通的基金就命名为:海通量化价值精选一年持有B、海通海升六个月持有A等。

那么我们运用正则表达式,实现输入证券资管的名称,就得到其旗下的公募基金的名称。

def get_col_name(self):
    day_data = self.day_data()
    # 匹配正则表达式
    pattern = re.compile('^%s'% self.company)
    col_name = day_data.columns.tolist()
    choose_name = []
    for name in col_name:
        if pattern.match(name):
            choose_name.append(name)
    print(choose_name)
    return choose_name


def col_num(self):
    choose_name = self.get_col_name()
    num = len(choose_name)
    # print(num)
    return num
3.3 指数信息和基金信息按日期合并
def choose_data(self):
    choose_name = self.get_col_name()
    day_data = self.day_data()
    day_data = day_data[['交易日期', choose_name[self.fund_num]]]
    day_data[choose_name[self.fund_num]] = day_data[choose_name[self.fund_num]].pct_change()
    day_rtn = day_data
    # 和指数信息合并
    index_day_rtn = self.index_day_data()
    equity_day = pd.merge(day_rtn, index_day_rtn, on='交易日期', how='left' )
    equity_day.dropna(axis=0, how='any', inplace=True)
    equity_day.rename(columns={choose_name[self.fund_num]: '涨跌幅''中国基金加权总指数''指数涨跌幅'}, inplace=True)
    return equity_day

此处计算基金收益时运用了dataframe.pct_change()函数(Pandas dataframe.pct_change()函数计算当前元素与之前元素之间的百分比变化。默认情况下,此函数计算前一行的百分比变化)。

此处我们使用的是环比增长,假如想用对数收益率,则不可以使用dataframe.pct_change()函数。

3.4画收益率曲线

为了能够在以后的研究中,方便调用绘画收益率曲线的函数,我们新建一个专门存放自建函数的文档,将函数保存。

# ===绘制收益率曲线
# 绘制策略曲线
def draw_equity_curve(df, data_dict, time=None, pic_size=[169], dpi=72, font_size=25, save_path='fig.jpg'):
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False
    # plt.style.use('dark_background')
    plt.figure(figsize=(pic_size[0], pic_size[1]), dpi=dpi)
    plt.xticks(fontsize=font_size)
    plt.yticks(fontsize=font_size)
    for key in data_dict:
        if time:
            plt.plot(df[time], df[data_dict[key]], label=key)
        else:
            plt.plot(df.index, df[data_dict[key]], label=key)
    plt.legend(fontsize=font_size)
    plt.grid()
    #plt.show(
    plt.savefig(save_path)

在主程序中调用绘画收益率曲线的函数:

def yield_curve(self):
    equity = self.choose_data()
    equity['equity_curve'] = (equity['涨跌幅'] + 1).cumprod()
    equity['benchmark'] = (equity['指数涨跌幅'] + 1).cumprod()
    equity = equity[['交易日期''equity_curve''benchmark''涨跌幅''指数涨跌幅']]
    return equity

def draw_curve(self, save_path = r'fig.jpg'):
    choose_name = self.get_col_name()
    equity = self.yield_curve()
    equity = equity.reset_index(drop=True)
    equity['交易日期'] = pd.to_datetime(equity['交易日期'])
    draw_equity_curve(equity, time='交易日期', data_dict={choose_name[self.fund_num]: 'equity_curve''中国基金加权总指数''benchmark'},
                      save_path=save_path)
3.5 评价指标

对于基金的收益,仅仅看收益率曲线,获得的评价较为主观,要客观比较收益的好坏,还要借助指标,比如:年化收益率、最大回撤等,更加复杂的还有夏普比率、Jensen指数等,需要的评价指标加入一下自定义的函数strategy_evaluate()即可。

# 计算策略评价指标
def strategy_evaluate(equity):

    # ===新建一个dataframe保存回测指标
    results = pd.DataFrame()

    # ===计算累积净值
    results.loc[0'累积净值'] = round(equity['equity_curve'].iloc[-1], 2)

    # ===计算年化收益
    annual_return = (equity['equity_curve'].iloc[-1]) ** (
            '1 days 00:00:00' / (equity['交易日期'].iloc[-1] - equity['交易日期'].iloc[0]) * 365) - 1
    results.loc[0'年化收益'] = str(round(annual_return * 1002)) + '%'
    # 计算当日之前的资金曲线的最高点
    equity['max2here'] = equity['equity_curve'].expanding().max()
    # 计算到历史最高值到当日的跌幅,drowdwon
    equity['dd2here'] = equity['equity_curve'] / equity['max2here'] - 1
    # 计算最大回撤,以及最大回撤结束时间
    end_date, max_draw_down = tuple(equity.sort_values(by=['dd2here']).iloc[0][['交易日期''dd2here']])
    # 计算最大回撤开始时间
    start_date = equity[equity['交易日期'] <= end_date].sort_values(by='equity_curve', ascending=False).iloc[0]['交易日期']
    # 将无关的变量删除
    equity.drop(['max2here''dd2here'], axis=1, inplace=True)
    results.loc[0'最大回撤'] = format(max_draw_down, '.2%')
    results.loc[0'最大回撤开始时间'] = str(start_date)
    results.loc[0'最大回撤结束时间'] = str(end_date)

    return results.T

在主程序中调用业绩评价函数

def evaluate(self):
    equity = self.yield_curve()
    choose_name = self.get_col_name()
    # ===计算策略评价指标
    rtn_data= strategy_evaluate(equity)
    rtn_data.rename(columns={0: choose_name[self.fund_num]}, inplace=True)
    #print(rtn_data)
    return rtn_data
3.6运行程序

在运行程序前,要在目录里面建好保存收益率曲线和收益率评价的文件夹,如图所示:在company_name处输入基金公司的简称,num即返回基金公司下的基金数量,程序自动遍历其旗下基金,并将其收益曲线图存入chart,将其收益评价指标合并后存入sheet。

if __name__ == '__main__':
    company_name = '光大'
    analyse = Analyse(company=company_name, fund_num = 0)

    num = analyse.col_num()
    df_ret_analyse = pd.DataFrame()
    for i in range(num):
        name = analyse.get_col_name()[i]
        try:
            analyse = Analyse(company=company_name, fund_num=i)
            analyse.draw_curve(save_path = root_path + r'/result\chart/%s.jpg' % name)
            df = analyse.evaluate()
            df_ret_analyse = pd.concat([df_ret_analyse, df], axis=1)
        except:
            print(name + '数据错误')


    print(df_ret_analyse)
    df_ret_analyse.to_csv(root_path + f'/result\sheet\{company_name}retns.csv')

4、运行效果展示

在company_name处输入'光大',查看光大资管的公募基金收益情况。运行后,收益率曲线已经全部保存至/result/chart点开其中一个查看:

收益指标则保存到了/result/sheet下:

5、完整代码

5.1 函数
import os
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np


# ===获取项目根目录
_ = os.path.abspath(os.path.dirname(__file__))  # 返回当前文件路径
root_path = os.path.abspath(os.path.join(_, '..'))  # 返回根目录文件夹

# ===绘制收益率曲线
# 绘制策略曲线
def draw_equity_curve(df, data_dict, time=None, pic_size=[169], dpi=72, font_size=25 , save_path='fig.jpg'):
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False
    # plt.style.use('dark_background')
    plt.figure(figsize=(pic_size[0], pic_size[1]), dpi=dpi)
    plt.xticks(fontsize=font_size)
    plt.yticks(fontsize=font_size)
    for key in data_dict:
        if time:
            plt.plot(df[time], df[data_dict[key]], label=key)
        else:
            plt.plot(df.index, df[data_dict[key]], label=key)
    plt.legend(fontsize=font_size)
    plt.grid()
    #plt.show(
    plt.savefig(save_path)


# 计算策略评价指标
def strategy_evaluate(equity):

    # ===新建一个dataframe保存回测指标
    results = pd.DataFrame()

    # ===计算累积净值
    results.loc[0'累积净值'] = round(equity['equity_curve'].iloc[-1], 2)

    # ===计算年化收益
    annual_return = (equity['equity_curve'].iloc[-1]) ** (
            '1 days 00:00:00' / (equity['交易日期'].iloc[-1] - equity['交易日期'].iloc[0]) * 365) - 1
    results.loc[0'年化收益'] = str(round(annual_return * 1002)) + '%'
    # 计算当日之前的资金曲线的最高点
    equity['max2here'] = equity['equity_curve'].expanding().max()
    # 计算到历史最高值到当日的跌幅,drowdwon
    equity['dd2here'] = equity['equity_curve'] / equity['max2here'] - 1
    # 计算最大回撤,以及最大回撤结束时间
    end_date, max_draw_down = tuple(equity.sort_values(by=['dd2here']).iloc[0][['交易日期''dd2here']])
    # 计算最大回撤开始时间
    start_date = equity[equity['交易日期'] <= end_date].sort_values(by='equity_curve', ascending=False).iloc[0]['交易日期']
    # 将无关的变量删除
    equity.drop(['max2here''dd2here'], axis=1, inplace=True)
    results.loc[0'最大回撤'] = format(max_draw_down, '.2%')
    results.loc[0'最大回撤开始时间'] = str(start_date)
    results.loc[0'最大回撤结束时间'] = str(end_date)

    return results.T

5.2主程序
import pandas as pd
import re
from 基金收益分析.func.myfunc import *
class Analyse(object):
    def __init__(self, company='海通', fund_num = 0):
        self.company = company
        self.fund_num = fund_num
        pass

    def day_data(self):
        day_data = pd.read_csv(root_path+'/data/大集合转公募基金复权净值day.CSV', encoding='gbk')
        day_data.rename(columns={'Unnamed: 0''交易日期'}, inplace=True)
        day_data['交易日期'] = pd.to_datetime(day_data['交易日期'])
        #day_rtn.set_index('交易日期', inplace=True)
        day_data.dropna(axis=1, how='any', inplace=True)
        return day_data

    def index_day_data(self):
        index_day_rtn = pd.read_csv(root_path+'/data/中国基金加权指数day.csv',
                                     encoding='gbk')
        index_day_rtn.rename(columns={'Unnamed: 0''交易日期'}, inplace=True)
        index_day_rtn['交易日期'] = pd.to_datetime(index_day_rtn['交易日期'])
        index_day_rtn['中国基金加权总指数'] = index_day_rtn['中国基金加权总指数' ].pct_change()
        return index_day_rtn

    def get_col_name(self):
        day_data = self.day_data()
        # 匹配正则表达式
        pattern = re.compile('^%s'% self.company)
        col_name = day_data.columns.tolist()
        choose_name = []
        for name in col_name:
            if pattern.match(name):
                choose_name.append(name)
        print(choose_name)
        return choose_name


    def col_num(self):
        choose_name = self.get_col_name()
        num = len(choose_name)
        # print(num)
        return num


    def choose_data(self):
        choose_name = self.get_col_name()
        day_data = self.day_data()
        day_data = day_data[['交易日期', choose_name[self.fund_num]]]
        day_data[choose_name[self.fund_num]] = day_data[choose_name[self.fund_num]].pct_change()
        day_rtn = day_data
        # 和指数信息合并
        index_day_rtn = self.index_day_data()
        equity_day = pd.merge(day_rtn, index_day_rtn, on='交易日期', how='left' )
        equity_day.dropna(axis=0, how='any', inplace=True)
        equity_day.rename(columns={choose_name[self.fund_num]: '涨跌幅''中国基金加权总指数''指数涨跌幅'}, inplace=True)
        return equity_day

    def yield_curve(self):
        equity = self.choose_data()
        equity['equity_curve'] = (equity['涨跌幅'] + 1).cumprod()
        equity['benchmark'] = (equity['指数涨跌幅'] + 1).cumprod()
        equity = equity[['交易日期''equity_curve''benchmark''涨跌幅''指数涨跌幅']]
        return equity

    def draw_curve(self, save_path = r'fig.jpg'):
        choose_name = self.get_col_name()
        equity = self.yield_curve()
        equity = equity.reset_index(drop=True)
        equity['交易日期'] = pd.to_datetime(equity['交易日期'])
        draw_equity_curve(equity, time='交易日期', data_dict={choose_name[self.fund_num]: 'equity_curve''中国基金加权总指数''benchmark'},
                          save_path=save_path)

    def evaluate(self):
        equity = self.yield_curve()
        choose_name = self.get_col_name()
        # ===计算策略评价指标
        rtn_data= strategy_evaluate(equity)
        rtn_data.rename(columns={0: choose_name[self.fund_num]}, inplace=True)
        #print(rtn_data)
        return rtn_data

if __name__ == '__main__':
    company_name = '光大'
    analyse = Analyse(company=company_name, fund_num = 0)

    num = analyse.col_num()
    df_ret_analyse = pd.DataFrame()
    for i in range(num):
        name = analyse.get_col_name()[i]
        try:
            analyse = Analyse(company=company_name, fund_num=i)
            analyse.draw_curve(save_path = root_path + r'/result\chart/%s.jpg' % name)
            df = analyse.evaluate()
            df_ret_analyse = pd.concat([df_ret_analyse, df], axis=1)
        except:
            print(name + '数据错误')


    print(df_ret_analyse)
    df_ret_analyse.to_csv(root_path + f'/result\sheet\{company_name}retns.csv')

6、结语

此处的分析作为分析的一个框架,可以添加更多的有趣的研究内容,比如:对基金的收益数据可以计算其期望,方差,峰度,偏度等指标,对收益率也可以计算夏普比率等指标。可以更加深入的分析基金的收益的变动。

最后强烈推荐帅帅老师的python课程,科班程序员出生的帅帅老师,代码写的非常规范,初学者可以少走弯路。并且帅帅老师的答疑非常及时,仔细回复每个问题,笔者每次遇到网上查询不到答案的问题时,都会马上请教帅帅老师,节省了许多自己debug的时间!

总之,无论是初学者从头学习,还是老手查漏补缺,找一位专家答疑,帅帅的课程都物超所值。


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/124162