# -*- coding: utf-8 -*-

from datetime import datetime, timedelta
from docx import Document
from docx.shared import Cm, Pt
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.shared import RGBColor
from hbshare.fe.mutual_analysis import holdind_based as hlb
from hbshare.fe.mutual_analysis.holding_compare_analysis import HoldingCompare
from hbshare.fe.xwq.analysis.orm.fedb import FEDB
from hbshare.fe.xwq.analysis.orm.hbdb import HBDB
import docx
import os
import numpy as np
import pandas as pd
from matplotlib.ticker import FuncFormatter
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import seaborn as sns
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
sns.set_style('white', {'font.sans-serif': ['simhei', 'Arial']})
line_color_list = ['#F04950', '#6268A2', '#959595', '#333335', '#EE703F', '#7E4A9B', '#8A662C',
                  '#44488E', '#BA67E9', '#3FAEEE']
bar_color_list = ['#C94649', '#EEB2B4', '#E1777A', '#D57C56', '#E39A79', '#DB8A66', '#E5B88C',
                  '#8588B7', '#B4B6D1', '#55598D', '#628497', '#A9C6CB', '#866EA9', '#B79BC7',
                  '#7D7D7E', '#CACACA', '#A7A7A8', '#606063', '#C4C4C4', '#99999B', '#B7B7B7']


def add_hyperlink(paragraph, url, text, color, underline):
    """
    A function that places a hyperlink within a paragraph object.

    :param paragraph: The paragraph we are adding the hyperlink to.
    :param url: A string containing the required url
    :param text: The text displayed for the url
    :return: The hyperlink object
    """
    # This gets access to the document.xml.rels file and gets a new relation id value
    part = paragraph.part
    r_id = part.relate_to(url, docx.opc.constants.RELATIONSHIP_TYPE.HYPERLINK, is_external=True)

    # Create the w:hyperlink tag and add needed values
    hyperlink = docx.oxml.shared.OxmlElement('w:hyperlink')
    hyperlink.set(docx.oxml.shared.qn('r:id'), r_id, )

    # Create a w:r element
    new_run = docx.oxml.shared.OxmlElement('w:r')

    # Create a new w:rPr element
    rPr = docx.oxml.shared.OxmlElement('w:rPr')

    # Add color if it is given
    if not color is None:
      c = docx.oxml.shared.OxmlElement('w:color')
      c.set(docx.oxml.shared.qn('w:val'), color)
      rPr.append(c)

    # Remove underlining if it is requested
    if not underline:
      u = docx.oxml.shared.OxmlElement('w:u')
      u.set(docx.oxml.shared.qn('w:val'), 'none')
      rPr.append(u)

    # Join all the xml elements together add add the required text to the w:r element
    new_run.append(rPr)
    new_run.text = text
    hyperlink.append(new_run)

    paragraph._p.append(hyperlink)

    return hyperlink

def to_percent(temp, position):
    return '%1.0f'%(temp) + '%'

def to_100percent(temp, position):
    return '%1.0f'%(temp * 100) + '%'

def get_change_dates_loc(change_dates, all_dates):
    change_dates_loc = []
    for change_date in change_dates:
        for idx, date in enumerate(all_dates[:-1]):
            if change_date >= all_dates[idx] and change_date <= all_dates[idx + 1]:
                loc = idx + (datetime.strptime(change_date, '%Y%m%d') - datetime.strptime(all_dates[idx], '%Y%m%d')) / (datetime.strptime(all_dates[idx + 1], '%Y%m%d') - datetime.strptime(all_dates[idx], '%Y%m%d'))
                change_dates_loc.append(loc)
                break
    return change_dates_loc


class QuantitativeReport:
    def __init__(self, file_path, target_fund_code, compare_fund_code_list=[]):
        self.file_path = file_path + target_fund_code + '/'
        if not os.path.exists(self.file_path):
            os.makedirs(self.file_path)
        self.target_fund_code = target_fund_code
        self.compare_fund_code_list = compare_fund_code_list
        self.stock_fund = HBDB().read_stock_fund_info()
        self.stock_fund = self.stock_fund.rename(columns={'jjdm': 'FUND_CODE', 'jjmc': 'FUND_FULL_NAME', 'jjjc': 'FUND_SHORT_NAME', 'clrq': 'BEGIN_DATE', 'zzrq': 'END_DATE', 'ejfl': 'FUND_TYPE', 'kffb': 'OPEN_CLOSE'})
        self.stock_fund['FUND_TYPE'] = self.stock_fund['FUND_TYPE'].replace({'13': '普通股票型', '35': '灵活配置型', '37': '偏股混合型'})
        self.target_fund = self.stock_fund[self.stock_fund['FUND_CODE'] == self.target_fund_code]
        self.compare_fund = self.stock_fund[self.stock_fund['FUND_CODE'].isin(compare_fund_code_list)]
        self.target_fund_name = self.target_fund['FUND_SHORT_NAME'].values[0]
        self.compare_fund_name_dic = self.compare_fund.set_index('FUND_CODE')['FUND_SHORT_NAME'].to_dict()
        self.compare_fund_name_dic[self.target_fund_code] = self.target_fund_name
        self.target_fund_manager = HBDB().read_fund_manager_given_code(self.target_fund_code)
        self.target_fund_manager = self.target_fund_manager.rename(columns={'jjdm': 'FUND_CODE', 'rydm': 'MANAGER_CODE', 'ryxm': 'MANAGER_NAME', 'ryzw': 'POSITION', 'bdrq': 'CHANGE_DATE', 'ryzt': 'STATUS', 'rzrq': 'BEGIN_DATE', 'lrrq': 'END_DATE', 'lryy': 'REASON'})
        self.target_fund_manager = self.target_fund_manager[self.target_fund_manager['POSITION'] == '基金经理']
        self.target_fund_manager['BEGIN_DATE'] = self.target_fund_manager['BEGIN_DATE'].astype(str)
        self.target_fund_manager['END_DATE'] = self.target_fund_manager['END_DATE'].astype(str)
        self.target_fund_manager['END_DATE'] = self.target_fund_manager['END_DATE'].replace('29991231', '-')
        self.change_dates = [date for date in self.target_fund_manager['BEGIN_DATE'].unique().tolist() + self.target_fund_manager['END_DATE'].unique().tolist() if date != '-']
        self.change_dates = sorted(list(set(self.change_dates)))[1:]
        self.fund = HBDB().read_fund_info()
        self.fund = self.fund.rename(columns={'jjdm': 'FUND_CODE', 'jjmc': 'FUND_FULL_NAME', 'jjjc': 'FUND_SHORT_NAME', 'clrq': 'BEGIN_DATE', 'zzrq': 'END_DATE', 'jjfl': 'FUND_TYPE_1ST', 'ejfl': 'FUND_TYPE_2ND', 'kffb': 'OPEN_CLOSE'})
        self.fund['FUND_TYPE_1ST'] = self.fund['FUND_TYPE_1ST'].replace({'f': 'FOF型', '1': '股票型', '2': '债券型', '3': '混合型', '4': '另类投资型', '7': '货币型', '9': 'QDII型'})
        self.fund['FUND_TYPE_2ND'] = self.fund['FUND_TYPE_2ND'].replace(
            {'f3': '债券型FOF', 'f4': '货币型FOF', 'f1': '股票型FOF', 'f2': '混合型FOF', 'f5': '另类投资FOF',
             '13': '普通股票型', '14': '股票型', '15': '增强指数型', '16': '被动指数型',
             '21': '被动指数型债券', '22': '短期纯债型', '23': '混合债券型一级', '24': '混合债券型二级', '25': '增强指数型债券', '26': '债券型',
             '27': '中长期纯债型', '28': '可转换债券型',
             '34': '平衡混合型', '35': '灵活配置型', '36': '混合型', '37': '偏股混合型', '38': '偏债混合型',
             '41': '股票多空', '42': '商品型', '43': 'REITs',
             '91': '股票型QDII', '93': '混合型QDII', '94': '债券型QDII', '95': '另类型QDII'})

    def report_info(self):
        """
        风险提示及重要声明
        """
        risk = '投资有风险。基金的过往业绩并不预示其未来表现。基金管理人管理的其他基金的业绩并不构成基金业绩表现的保证。相关数据仅供参考，不构成投资建议。投资人请详阅基金合同等法律文件，了解产品风险收益特征，根据自身资产状况、风险承受能力审慎决策，独立承担投资风险。本资料仅为宣传用品，本机构及工作人员不存在直接或间接主动推介相关产品的行为。'
        statement1 = '本文件中的信息基于已公开的信息、数据及尽调访谈等，好买基金或好买基金研究中心（以下简称“本公司”）对这些信息的及时性、准确性及完整性不做任何保证，也不保证所包含的信息不会发生变更。文件中的内容仅供参考，不代表任何确定性的判断。本文件及其内容均不构成投资建议，也没有考虑个别客户特殊的投资目标、财务状况或需要。获得本文件的机构或个人据此做出投资决策，应自行承担投资风险。'
        statement2 = '本文件版权为本公司所有。未经本公司书面许可，任何机构或个人不得以翻版、复制、 发表、引用或再次分发他人等任何形式侵犯本公司版权。本文件中的信息均为保密信息，未经本公司事先同意，不得以任何目的，复制或传播本文本中所含信息，亦不可向任何第三方披露。'
        statement3 = '本文件系为好买基金备制并仅以非公开方式提交给符合监管规定之合格投资者。'
        return risk, statement1, statement2, statement3

    def get_fund_info(self):
        """
        生成基金简介数据
        """
        fund_full_name = self.target_fund['FUND_FULL_NAME'].values[0]
        establish_date = str(self.target_fund['BEGIN_DATE'].values[0])
        establish_date_cn = '{0}年{1}月{2}日'.format(int(establish_date[:4]), int(establish_date[4:6]), int(establish_date[6:]))
        fund_type = self.target_fund['FUND_TYPE'].values[0]

        company = HBDB().read_fund_company_given_code(self.target_fund_code)
        company = company.rename(columns={'JJDM': 'FUND_CODE', 'JGDM': 'COMPANY_CODE', 'JGMC': 'COMPANY_NAME'})
        company_name = company['COMPANY_NAME'].values[0]

        manager = HBDB().read_fund_manager_given_code(self.target_fund_code)
        manager = manager.rename(columns={'jjdm': 'FUND_CODE', 'rydm': 'MANAGER_CODE', 'ryxm': 'MANAGER_NAME', 'ryzw': 'POSITION', 'bdrq': 'CHANGE_DATE', 'ryzt': 'STATUS', 'rzrq': 'BEGIN_DATE', 'lrrq': 'END_DATE', 'lryy': 'REASON'})
        manager = manager[(manager['STATUS'] == '-1') & (manager['POSITION'] == '基金经理')].sort_values('BEGIN_DATE')
        manager['BEGIN_DATE'] = manager['BEGIN_DATE'].astype(int)
        manager['BEGIN_DATE_CN'] = manager['BEGIN_DATE'].apply(lambda x: '{0}年{1}月{2}日'.format(int(str(x)[:4]), int(str(x)[4:6]), int(str(x)[6:])))
        manager_name = manager['MANAGER_NAME'].values[0] if len(manager) == 1 else '、'.join(manager['MANAGER_NAME'].tolist())
        begin_date = '自{}'.format(manager['BEGIN_DATE_CN'].values[0]) if len(manager) == 1 else '分别自{}'.format('、'.join(manager['BEGIN_DATE_CN'].tolist()))

        scale = HBDB().read_fund_scale_given_code(self.target_fund_code)
        scale = scale.rename(columns={'JJDM': 'FUND_CODE', 'BBLB1': 'REPORT_TYPE', 'JSRQ': 'REPORT_DATE', 'GGRQ': 'PUBLISH_DATE', 'ZCJZ': 'EQUITY'})
        scale = scale[scale['REPORT_TYPE'] == 13]
        scale = scale.sort_values(['REPORT_DATE', 'PUBLISH_DATE']).drop_duplicates('REPORT_DATE', keep='last')
        scale_num = round(scale['EQUITY'].values[-1] / 100000000.0, 2)

        target = HBDB().read_fund_target_given_code(self.target_fund_code)
        target = target.rename(columns={'JJDM': 'FUND_CODE', 'TZMB': 'TARGET'})
        target_description = target['TARGET'].values[0].replace(' ', '').replace('\r\n', '')

        fund_info_text = '{0}（{1}），全称为{2}，是由{3}于{4}发行的{5}基金，最新资产规模为{6}亿，基金经理为{7}，{8}担任此基金的基金经理至今。{9}'\
            .format(self.target_fund_name, self.target_fund_code, fund_full_name, company_name, establish_date_cn, fund_type, scale_num, manager_name, begin_date, target_description)
        return fund_info_text

    def get_fund_company_info(self):
        """
        生成基金公司简介数据
        """
        company = HBDB().read_fund_company_given_code(self.target_fund_code)
        company = company.rename(columns={'JJDM': 'FUND_CODE', 'JGDM': 'COMPANY_CODE', 'JGMC': 'COMPANY_NAME'})
        company_name = company['COMPANY_NAME'].values[0]
        company_code = company['COMPANY_CODE'].values[0]

        company_fund = HBDB().read_company_fund_given_code(company_code)
        company_fund = company_fund.rename(columns={'JGDM': 'COMPANY_CODE', 'JGMC': 'COMPANY_NAME', 'JJDM': 'FUND_CODE'})
        company_fund_scale_list = []
        for code in company_fund['FUND_CODE'].unique().tolist():
            company_fund_scale_code = HBDB().read_fund_scale_given_code(code)
            company_fund_scale_list.append(company_fund_scale_code)
        company_fund_scale = pd.concat(company_fund_scale_list)
        company_fund_scale = company_fund_scale.rename(columns={'JJDM': 'FUND_CODE', 'BBLB1': 'REPORT_TYPE', 'JSRQ': 'REPORT_DATE', 'GGRQ': 'PUBLISH_DATE', 'ZCJZ': 'EQUITY'})
        company_fund_scale = company_fund_scale[company_fund_scale['REPORT_TYPE'] == 13]
        company_fund_scale = company_fund_scale.sort_values(['FUND_CODE', 'REPORT_DATE']).drop_duplicates(['FUND_CODE'], keep='last')
        company_fund_scale['EQUITY'] = company_fund_scale['EQUITY'] / 100000000.0
        company_fund_scale = company_fund_scale[['FUND_CODE', 'REPORT_DATE', 'EQUITY']].merge(self.fund, on=['FUND_CODE'], how='left')
        company_fund_scale = company_fund_scale[company_fund_scale['END_DATE'].isnull()]
        company_fund_scale.loc[company_fund_scale['FUND_TYPE_2ND'].isnull(), 'FUND_TYPE_2ND'] = company_fund_scale.loc[company_fund_scale['FUND_TYPE_2ND'].isnull(), 'FUND_TYPE_1ST']
        company_fund_count_1st = company_fund_scale.drop_duplicates('FUND_FULL_NAME')[['FUND_TYPE_1ST', 'FUND_CODE']].groupby('FUND_TYPE_1ST').count().rename(columns={'FUND_CODE': '数量'})
        company_fund_count_2nd = company_fund_scale.drop_duplicates('FUND_FULL_NAME')[['FUND_TYPE_2ND', 'FUND_CODE']].groupby('FUND_TYPE_2ND').count().rename(columns={'FUND_CODE': '数量'})
        company_fund_count_1st['数量占比'] = company_fund_count_1st['数量'] / float(company_fund_count_1st['数量'].sum())
        company_fund_count_2nd['数量占比'] = company_fund_count_2nd['数量'] / float(company_fund_count_2nd['数量'].sum())
        company_fund_count_1st['数量占比'] = company_fund_count_1st['数量占比'].apply(lambda x: '{0}%'.format(round(x * 100, 2)))
        company_fund_count_2nd['数量占比'] = company_fund_count_2nd['数量占比'].apply(lambda x: '{0}%'.format(round(x * 100, 2)))
        company_fund_scale_1st = company_fund_scale[['FUND_TYPE_1ST', 'EQUITY']].groupby('FUND_TYPE_1ST').sum().rename(columns={'EQUITY': '规模（亿元）'})
        company_fund_scale_2nd = company_fund_scale[['FUND_TYPE_2ND', 'EQUITY']].groupby('FUND_TYPE_2ND').sum().rename(columns={'EQUITY': '规模（亿元）'})
        company_fund_scale_1st['规模（亿元）'] = company_fund_scale_1st['规模（亿元）'].apply(lambda x: round(x, 2))
        company_fund_scale_2nd['规模（亿元）'] = company_fund_scale_2nd['规模（亿元）'].apply(lambda x: round(x, 2))
        company_fund_scale_1st['规模占比'] = company_fund_scale_1st['规模（亿元）'] / float(company_fund_scale_1st['规模（亿元）'].sum())
        company_fund_scale_2nd['规模占比'] = company_fund_scale_2nd['规模（亿元）'] / float(company_fund_scale_2nd['规模（亿元）'].sum())
        company_fund_scale_1st['规模占比'] = company_fund_scale_1st['规模占比'].apply(lambda x: '{0}%'.format(round(x * 100, 2)))
        company_fund_scale_2nd['规模占比'] = company_fund_scale_2nd['规模占比'].apply(lambda x: '{0}%'.format(round(x * 100, 2)))
        company_fund_1st = pd.concat([company_fund_count_1st, company_fund_scale_1st], axis=1).sort_values('规模（亿元）', ascending=False)
        company_fund_2nd = pd.concat([company_fund_count_2nd, company_fund_scale_2nd], axis=1).sort_values('规模（亿元）', ascending=False)
        company_fund_1st = company_fund_1st.T.reset_index().T.reset_index()
        company_fund_2nd = company_fund_2nd.T.reset_index().T.reset_index()
        company_fund_1st.iloc[0, 0] = '一级分类'
        company_fund_2nd.iloc[0, 0] = '二级分类'

        fund_company_info_text = '该产品所在基金管理公司{0}旗下公募基金数量和规模分布如下：'.format(company_name)
        return fund_company_info_text, company_fund_1st, company_fund_2nd

    def get_fund_scale(self):
        """
        生成基金规模数据
        """
        scale = HBDB().read_fund_scale_given_code(self.target_fund_code)
        scale = scale.rename(columns={'JJDM': 'FUND_CODE', 'BBLB1': 'REPORT_TYPE', 'JSRQ': 'REPORT_DATE', 'GGRQ': 'PUBLISH_DATE', 'ZCJZ': 'EQUITY'})
        scale = scale[scale['REPORT_TYPE'] == 13]
        scale = scale.sort_values(['REPORT_DATE', 'PUBLISH_DATE']).drop_duplicates('REPORT_DATE', keep='last')
        scale['EQUITY'] = scale['EQUITY'] / 100000000.0
        scale = scale[['REPORT_DATE', 'EQUITY']]

        share = HBDB().read_fund_share_given_code(self.target_fund_code)
        share = share.rename(columns={'jjdm': 'FUND_CODE', 'bblb': 'REPORT_TYPE', 'jsrq': 'END_DATE', 'plrq': 'PUBLISH_DATE', 'qsrq': 'BEGIN_DATE', 'qcfe': 'BEGIN_SHARES', 'qjsgfe': 'PURCHASE_SHARES', 'cfzjfe': 'SPLIT_SHARES', 'wjshfe': 'REDEEM_SHARES', 'qmfe': 'END_SHARES'})
        share = share[share['REPORT_TYPE'].isin(['3', '5', '6', '7'])]
        share = share.sort_values(['END_DATE', 'PUBLISH_DATE']).drop_duplicates('END_DATE', keep='last')
        share[['END_SHARES', 'PURCHASE_SHARES', 'REDEEM_SHARES']] = share[['END_SHARES', 'PURCHASE_SHARES', 'REDEEM_SHARES']] / 100000000.0
        share = share[['END_DATE', 'END_SHARES', 'PURCHASE_SHARES', 'REDEEM_SHARES']].rename(columns={'END_DATE': 'REPORT_DATE'})

        fund_scale = scale.merge(share, on=['REPORT_DATE'], how='left')
        fund_scale = fund_scale.sort_values('REPORT_DATE').iloc[-12:]
        fund_scale.columns = ['报告日期', '净资产（亿元）', '份额（亿份）', '申购额（亿元）', '赎回额（亿元）']

        scale = fund_scale[['报告日期', '净资产（亿元）']].rename(columns={'净资产（亿元）': '规模（亿元）/份额（亿份）'})
        scale['规模/份额'] = '规模'
        share = fund_scale[['报告日期', '份额（亿份）']].rename(columns={'份额（亿份）': '规模（亿元）/份额（亿份）'})
        share['规模/份额'] = '份额'
        scale_share = pd.concat([scale, share]).reset_index().drop('index', axis=1)
        purchase = fund_scale[['报告日期', '申购额（亿元）']].rename(columns={'申购额（亿元）': '申赎额（亿元）'})
        purchase['申赎额'] = '申购额'
        redeem = fund_scale[['报告日期', '赎回额（亿元）']].rename(columns={'赎回额（亿元）': '申赎额（亿元）'})
        redeem['申赎额'] = '赎回额'
        purchase_redeem = pd.concat([purchase, redeem]).reset_index().drop('index', axis=1)

        scale_share['报告日期'] = scale_share['报告日期'].astype(str)
        change_dates = [date for date in self.change_dates if date >= min(scale_share['报告日期']) and date <= max(scale_share['报告日期'])]
        change_dates_loc = get_change_dates_loc(change_dates, sorted(scale_share['报告日期'].unique().tolist()))
        fig, ax1 = plt.subplots(figsize=(6, 3))
        ax2 = ax1.twinx()
        sns.lineplot(ax=ax1, x='报告日期', y='规模（亿元）/份额（亿份）', data=scale_share, hue='规模/份额', palette=['#F04950', '#6268A2'])
        sns.barplot(ax=ax2, x='报告日期', y='申赎额（亿元）', data=purchase_redeem, hue='申赎额', palette=['#C94649', '#8588B7'])
        # handles_ax1, labels_ax1 = ax1.get_legend_handles_labels()
        # ax1.legend(handles=handles_ax1, labels=labels_ax1, loc=2)
        # handles_ax2, labels_ax2 = ax2.get_legend_handles_labels()
        # ax2.legend(handles=handles_ax2, labels=labels_ax2, loc=1)
        ax1.vlines(x=change_dates_loc, ymin=0.0, ymax=max(scale_share['规模（亿元）/份额（亿份）']), linestyles='dashed', color='#959595')
        ax1.legend(loc=2)
        ax2.legend(loc=1)
        ax1.set_xticklabels(labels=fund_scale['报告日期'], rotation=90)
        plt.tight_layout()
        plt.savefig('{0}{1}_scale.png'.format(self.file_path, self.target_fund_code))

        lastet_scale = round(fund_scale['净资产（亿元）'].iloc[-1], 2)
        fund_scale_text = '该产品规模变动如下，截至最新报告期，基金最新规模为{0}亿元。'.format(lastet_scale)

        compare_scale = pd.DataFrame()
        if len(self.compare_fund_code_list) > 0:
            compare_scale_list = []
            for compare_fund_code in [self.target_fund_code] + self.compare_fund_code_list:
                compare_scale_code = HBDB().read_fund_scale_given_code(compare_fund_code)
                compare_scale_list.append(compare_scale_code)
            compare_scale = pd.concat(compare_scale_list)
            compare_scale = compare_scale.rename(columns={'JJDM': 'FUND_CODE', 'BBLB1': 'REPORT_TYPE', 'JSRQ': 'REPORT_DATE', 'GGRQ': 'PUBLISH_DATE', 'ZCJZ': 'EQUITY'})
            compare_scale = compare_scale[compare_scale['REPORT_TYPE'] == 13]
            compare_scale = compare_scale.sort_values(['FUND_CODE', 'REPORT_DATE', 'PUBLISH_DATE']).drop_duplicates(['FUND_CODE', 'REPORT_DATE'], keep='last')
            compare_scale = compare_scale[['FUND_CODE', 'REPORT_DATE', 'EQUITY']]
            compare_scale['EQUITY'] = compare_scale['EQUITY'].apply(lambda x: round(x / 100000000.0, 2))
            compare_scale = compare_scale.pivot(index='REPORT_DATE', columns='FUND_CODE', values='EQUITY')
            compare_scale = compare_scale.dropna(subset=[self.target_fund_code], axis=0).sort_index().iloc[-12:]
            compare_scale = compare_scale[[self.target_fund_code] + self.compare_fund_code_list]
            compare_scale.columns = [self.compare_fund_name_dic[col] for col in compare_scale.columns]
            compare_scale = compare_scale.replace(np.nan, '-')
            compare_scale = compare_scale.T.reset_index().T.reset_index()
            compare_scale.iloc[0, 0] = '规模（亿元）'
        return fund_scale_text, compare_scale

    def get_fund_holder(self):
        """
        生成基金持有人结构数据
        """
        fund_holder = HBDB().read_fund_holder_given_code(self.target_fund_code)
        fund_holder = fund_holder.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'ggrq': 'PUBLISH_DATE', 'jgcyfe': 'ORGAN_HOLDING', 'jgcybl': 'ORGAN_HOLDING_RATIO', 'grcyfe': 'PERSON_HOLDING', 'grcybl': 'PERSON_HOLDING_RATIO'})

        fund_holder = fund_holder.sort_values('REPORT_DATE').iloc[-12:]
        fund_holder = fund_holder[['REPORT_DATE', 'ORGAN_HOLDING_RATIO', 'PERSON_HOLDING_RATIO']]
        fund_holder.columns = ['报告日期', '机构持有比例', '个人持有比例']

        organ_person = fund_holder.copy(deep=True)
        organ_person['个人持有比例'] = organ_person['个人持有比例'] + organ_person['机构持有比例']

        organ_person['报告日期'] = organ_person['报告日期'].astype(str)
        change_dates = [date for date in self.change_dates if date >= min(organ_person['报告日期']) and date <= max(organ_person['报告日期'])]
        change_dates_loc = get_change_dates_loc(change_dates, sorted(organ_person['报告日期'].unique().tolist()))
        fig, ax = plt.subplots(figsize=(6, 3))
        sns.barplot(ax=ax, x='报告日期', y='个人持有比例', data=organ_person, label='个人持有比例', color='#8588B7')
        sns.barplot(ax=ax, x='报告日期', y='机构持有比例', data=organ_person, label='机构持有比例', color='#C94649')
        ax.vlines(x=change_dates_loc, ymin=0.0, ymax=100.0, linestyles='dashed', color='#959595')
        plt.legend(loc=2)
        plt.xticks(rotation=90)
        plt.ylabel('持有比例（%）')
        plt.tight_layout()
        plt.savefig('{0}{1}_holder.png'.format(self.file_path, self.target_fund_code))

        latest_holder = fund_holder.iloc[-1]
        (larger_name, larger_ratio) = ('机构持有比例', round(latest_holder['机构持有比例'], 2)) if latest_holder['机构持有比例'] > latest_holder['个人持有比例'] else ('个人持有比例', round(latest_holder['个人持有比例'], 2))
        (smaller_name, smaller_ratio) = ('机构持有比例', round(latest_holder['机构持有比例'], 2)) if latest_holder['机构持有比例'] < latest_holder['个人持有比例'] else ('个人持有比例', round(latest_holder['个人持有比例'], 2))
        fund_scale_text = '该产品持有人结构如下，从最新持有人结构看，{0}大于{1}，占比分别为{2}%、{3}%。'.format(larger_name, smaller_name, larger_ratio, smaller_ratio)

        compare_holding = pd.DataFrame()
        if len(self.compare_fund_code_list) > 0:
            compare_holding_list = []
            for compare_fund_code in [self.target_fund_code] + self.compare_fund_code_list:
                compare_holding_code = HBDB().read_fund_holder_given_code(compare_fund_code)
                compare_holding_list.append(compare_holding_code)
            compare_holding = pd.concat(compare_holding_list)
            compare_holding = compare_holding.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'ggrq': 'PUBLISH_DATE', 'jgcyfe': 'ORGAN_HOLDING', 'jgcybl': 'ORGAN_HOLDING_RATIO', 'grcyfe': 'PERSON_HOLDING', 'grcybl': 'PERSON_HOLDING_RATIO'})
            compare_holding['ORGAN_HOLDING_RATIO'] = compare_holding['ORGAN_HOLDING_RATIO'].fillna(0.0)
            compare_holding['PERSON_HOLDING_RATIO'] = compare_holding['PERSON_HOLDING_RATIO'].fillna(0.0)
            compare_holding = compare_holding.sort_values(['FUND_CODE', 'REPORT_DATE', 'PUBLISH_DATE']).drop_duplicates(['FUND_CODE', 'REPORT_DATE'], keep='last')
            compare_holding = compare_holding[['FUND_CODE', 'REPORT_DATE', 'ORGAN_HOLDING_RATIO']]
            compare_holding['ORGAN_HOLDING_RATIO'] = compare_holding['ORGAN_HOLDING_RATIO'].apply(lambda x: '{0}%'.format(round(x, 2)))
            compare_holding = compare_holding.pivot(index='REPORT_DATE', columns='FUND_CODE', values='ORGAN_HOLDING_RATIO')
            compare_holding = compare_holding.dropna(subset=[self.target_fund_code], axis=0).sort_index().iloc[-12:]
            compare_holding = compare_holding[[self.target_fund_code] + self.compare_fund_code_list]
            compare_holding.columns = [self.compare_fund_name_dic[col] for col in compare_holding.columns]
            compare_holding = compare_holding.replace(np.nan, '-')
            compare_holding = compare_holding.T.reset_index().T.reset_index()
            compare_holding.iloc[0, 0] = '机构持有比例'
        return fund_scale_text, compare_holding

    def get_fund_manager_info(self):
        """
        生成基金经理简介数据
        """
        manager = HBDB().read_fund_manager_given_code(self.target_fund_code)
        manager = manager.rename(columns={'jjdm': 'FUND_CODE', 'rydm': 'MANAGER_CODE', 'ryxm': 'MANAGER_NAME', 'ryzw': 'POSITION', 'bdrq': 'CHANGE_DATE', 'ryzt': 'STATUS', 'rzrq': 'BEGIN_DATE', 'lrrq': 'END_DATE', 'lryy': 'REASON'})
        manager_all = manager.copy(deep=True)
        manager = manager[(manager['STATUS'] == '-1') & (manager['POSITION'] == '基金经理')].sort_values('BEGIN_DATE')
        manager['BEGIN_DATE'] = manager['BEGIN_DATE'].astype(int)
        manager['BEGIN_DATE_CN'] = manager['BEGIN_DATE'].apply(lambda x: '{0}年{1}月{2}日'.format(int(str(x)[:4]), int(str(x)[4:6]), int(str(x)[6:])))
        manager_name = manager['MANAGER_NAME'].values[0] if len(manager) == 1 else '、'.join(manager['MANAGER_NAME'].tolist())
        begin_date = '自{}'.format(manager['BEGIN_DATE_CN'].values[0]) if len(manager) == 1 else '分别自{}'.format('、'.join(manager['BEGIN_DATE_CN'].tolist()))

        three_years_ago = (datetime.today() - timedelta(365 * 3)).strftime('%Y%m%d')
        change = '未经历' if max(manager['BEGIN_DATE'].unique().tolist()) <= int(three_years_ago) else '经历了'

        fund_manager_info_text = '{0}目前的基金经理为{1}，{2}担任此基金的基金经理至今，在定量分析统计期间（近三年）{3}基金经理变更。'\
            .format(self.target_fund_name, manager_name, begin_date, change)
        for manager_code in manager['MANAGER_CODE'].unique().tolist():
            manager_name = manager[manager['MANAGER_CODE'] == manager_code]['MANAGER_NAME'].values[0]

            manager_product = HBDB().read_fund_manager_product_given_code(manager_code)
            manager_product = manager_product.rename(columns={'rydm': 'MANAGER_CODE', 'jsrq': 'REPORT_DATE', 'zgcpsl': 'FUND_NUM', 'zgcpgm': 'FUND_SCALE', 'dbjj': 'FUND_REPRESENT'})
            manager_product = manager_product.sort_values('REPORT_DATE')
            manager_product_scale = round(manager_product['FUND_SCALE'].values[-1] / 100000000.0, 2)
            manager_product_repr = '，根据基金经理任职时长和基金净资产，其代表基金即为本产品' if manager_product['FUND_REPRESENT'].values[-1] == self.target_fund_code else ''

            manager_funds = HBDB().read_fund_given_manager_code(manager_code)
            manager_funds = manager_funds.rename(columns={'jjdm': 'FUND_CODE', 'rydm': 'MANAGER_CODE', 'ryxm': 'MANAGER_NAME', 'ryzw': 'POSITION', 'bdrq': 'CHANGE_DATE', 'ryzt': 'STATUS', 'rzrq': 'BEGIN_DATE', 'lrrq': 'END_DATE', 'lryy': 'REASON'})
            manager_funds = manager_funds[manager_funds['STATUS'] == '-1']
            manager_funds = self.fund[self.fund['FUND_CODE'].isin(manager_funds['FUND_CODE'].unique().tolist())]
            manager_product_num = len(manager_funds.drop_duplicates('FUND_FULL_NAME'))
            funds_scale_list = []
            for code in manager_funds['FUND_CODE'].unique().tolist():
                funds_scale_list.append(HBDB().read_fund_scale_given_code(code))
            funds_scale = pd.concat(funds_scale_list)
            funds_scale = funds_scale.rename(columns={'JJDM': 'FUND_CODE', 'BBLB1': 'REPORT_TYPE', 'JSRQ': 'REPORT_DATE', 'GGRQ': 'PUBLISH_DATE', 'ZCJZ': 'EQUITY'})
            funds_scale = funds_scale[funds_scale['REPORT_TYPE'] == 13]
            funds_scale = funds_scale.sort_values(['FUND_CODE', 'REPORT_DATE', 'PUBLISH_DATE']).drop_duplicates(['FUND_CODE'], keep='last')
            manager_funds = manager_funds.merge(funds_scale[['FUND_CODE', 'EQUITY']], on=['FUND_CODE'], how='left')
            manager_funds = manager_funds[['FUND_TYPE_2ND', 'EQUITY']].groupby('FUND_TYPE_2ND').sum().reset_index().sort_values('EQUITY', ascending=False)
            manager_funds['EQUITY'] = manager_funds['EQUITY'].apply(lambda x: str(round(x / 100000000.0, 2)))
            scale_type = '、'.join(manager_funds['FUND_TYPE_2ND'].unique().tolist())
            scale_value = '、'.join(manager_funds['EQUITY'].unique().tolist())
            manager_scale_repr = '{0}基金管理规模分别为{1}亿元'.format(scale_type, scale_value) if len(manager_funds) > 1 else '{0}基金管理规模为{1}亿元'.format(scale_type, scale_value)

            fund_manager_achievement = HBDB().read_fund_manager_achievement_given_code(manager_code, self.target_fund_code)
            fund_manager_achievement = fund_manager_achievement.rename(columns={'rydm': 'MANAGER_CODE', 'jjdm': 'FUND_CODE', 'qsrq': 'BEGIN_DATE', 'jsrq': 'END_DATE', 'rqts': 'MANAGE_DAYS', 'hbrq': 'RETURN', 'rqnhhb': 'ANNUAL_RETURN', 'zdyl': 'MAX_RETURN', 'zdhc': 'MAX_DRAWDOWN', 'nhxpbl': 'ANNUAL_SHARPE_RATIO', 'kmbl': 'CALMAR_RATIO'})
            annual_return = round(fund_manager_achievement['ANNUAL_RETURN'].values[0], 2)
            max_drawdown = round(fund_manager_achievement['MAX_DRAWDOWN'].values[0], 2)
            annual_sharpe_ratio = round(fund_manager_achievement['ANNUAL_SHARPE_RATIO'].values[0], 2)
            calmar_ratio = round(fund_manager_achievement['CALMAR_RATIO'].values[0], 2)

            fund_manager_info_text += '{0}目前在管产品数量为{1}只，在管基金规模为{2}亿元，其中{3}{4}。管理期内{5}年化收益为{6}%， 最大回撤为{7}%，年化夏普比率为{8}，卡玛比率为{9}。'\
                .format(manager_name, manager_product_num, manager_product_scale, manager_scale_repr, manager_product_repr, self.target_fund_name, annual_return, max_drawdown, annual_sharpe_ratio, calmar_ratio)

        fund_manager_info_text += '该产品自成立以来，基金经理变更情况如下：'
        manager_all = manager_all[manager_all['POSITION'] == '基金经理']
        manager_all['BEGIN_DATE'] = manager_all['BEGIN_DATE'].astype(str)
        manager_all['END_DATE'] = manager_all['END_DATE'].astype(str)
        manager_all['END_DATE'] = manager_all['END_DATE'].replace('29991231', '-')
        manager_all['REASON'] = manager_all['REASON'].replace(np.nan, '-')
        manager_all = manager_all.sort_values(['BEGIN_DATE', 'END_DATE'])
        manager_all = manager_all[['MANAGER_NAME', 'BEGIN_DATE', 'END_DATE', 'REASON']]
        manager_all.columns = ['基金经理', '任职日期', '离任日期', '离任原因']
        manager_all_ori = manager_all.copy(deep=True)
        manager_all = manager_all.T.reset_index().T.reset_index().drop('index', axis=1)
        return fund_manager_info_text, manager_all_ori, manager_all

    def get_fund_manager_method(self):
        """
        生成基金经理投资理念数据
        """
        # 换手率与集中度
        fund_size = FEDB().read_hbs_size_property_given_code(self.target_fund_code)
        fund_size = fund_size[['jjdm', 'asofdate', 'shift_lv_rank', 'cen_lv_rank']]
        fund_size = fund_size.rename(columns={'jjdm': 'FUND_CODE', 'asofdate': 'ASOFDATE', 'shift_lv_rank': 'SIZE_TURNOVER', 'cen_lv_rank': 'SIZE_CONCENTRATION'})
        fund_size['FUND_NAME'] = self.target_fund_name
        size_shift = fund_size[['FUND_NAME', 'FUND_CODE', 'SIZE_TURNOVER']]
        size_cen = fund_size[['FUND_NAME', 'FUND_CODE', 'SIZE_CONCENTRATION']]

        fund_style = FEDB().read_hbs_style_property_given_code(self.target_fund_code)
        fund_style = fund_style[['jjdm', 'asofdate', 'shift_lv_rank', 'cen_lv_rank']]
        fund_style = fund_style.rename(columns={'jjdm': 'FUND_CODE', 'asofdate': 'ASOFDATE', 'shift_lv_rank': 'STYLE_TURNOVER', 'cen_lv_rank': 'STYLE_CONCENTRATION'})
        fund_style['FUND_NAME'] = self.target_fund_name
        style_shift = fund_style[['FUND_NAME', 'FUND_CODE', 'STYLE_TURNOVER']]
        style_cen = fund_style[['FUND_NAME', 'FUND_CODE', 'STYLE_CONCENTRATION']]

        fund_theme = FEDB().read_hbs_theme_industry_property_given_code(self.target_fund_code)
        fund_theme = fund_theme[['jjdm', 'asofdate', 'ratio_theme_rank', 'cen_theme_rank']]
        fund_theme = fund_theme.rename(columns={'jjdm': 'FUND_CODE', 'asofdate': 'ASOFDATE', 'ratio_theme_rank': 'THEME_TURNOVER', 'cen_theme_rank': 'THEME_CONCENTRATION'})
        fund_theme['FUND_NAME'] = self.target_fund_name
        theme_shift = fund_theme[['FUND_NAME', 'FUND_CODE', 'THEME_TURNOVER']]
        theme_cen = fund_theme[['FUND_NAME', 'FUND_CODE', 'THEME_CONCENTRATION']]

        fund_industry = FEDB().read_hbs_theme_industry_property_given_code(self.target_fund_code)
        fund_industry = fund_industry[['jjdm', 'asofdate', 'ratio_ind_rank', 'cen_ind_rank']]
        fund_industry = fund_industry.rename(columns={'jjdm': 'FUND_CODE', 'asofdate': 'ASOFDATE', 'ratio_ind_rank': 'INDUSTRY_TURNOVER', 'cen_ind_rank': 'INDUSTRY_CONCENTRATION'})
        fund_industry['FUND_NAME'] = self.target_fund_name
        industry_shift = fund_industry[['FUND_NAME', 'FUND_CODE', 'INDUSTRY_TURNOVER']]
        industry_cen = fund_industry[['FUND_NAME', 'FUND_CODE', 'INDUSTRY_CONCENTRATION']]

        shift_disp = size_shift.merge(style_shift, on=['FUND_NAME', 'FUND_CODE'], how='left').merge(theme_shift, on=['FUND_NAME', 'FUND_CODE'], how='left').merge(industry_shift, on=['FUND_NAME', 'FUND_CODE'], how='left')
        shift_disp['TURNOVER'] = shift_disp.set_index(['FUND_NAME', 'FUND_CODE']).iloc[0].mean()
        shift_mark = '偏低' if shift_disp['TURNOVER'].iloc[0] < 0.4 else '偏高' if shift_disp['TURNOVER'].iloc[0] > 0.6 else '中等'
        for col in list(shift_disp.columns)[2:]:
            shift_disp[col].iloc[0] = '{0}%'.format(round(shift_disp[col].iloc[0] * 100.0, 2))
        shift_data = '、'.join(shift_disp.set_index(['FUND_NAME', 'FUND_CODE']).iloc[0].tolist()[:4])
        shift_disp.columns = ['基金名称', '基金代码', '规模换手率水平', '风格换手率水平', '主题换手率水平', '行业换手率水平', '整体换手率水平']
        shift_disp = shift_disp.T.reset_index().T.reset_index().drop('index', axis=1)
        cen_disp = size_cen.merge(style_cen, on=['FUND_NAME', 'FUND_CODE'], how='left').merge(theme_cen, on=['FUND_NAME', 'FUND_CODE'], how='left').merge(industry_cen, on=['FUND_NAME', 'FUND_CODE'], how='left')
        cen_disp['CONCENTRATION'] = cen_disp.set_index(['FUND_NAME', 'FUND_CODE']).iloc[0].mean()
        cen_mark = '偏低' if cen_disp['CONCENTRATION'].iloc[0] < 0.4 else '偏高' if cen_disp['CONCENTRATION'].iloc[0] > 0.6 else '中等'
        for col in list(cen_disp.columns)[2:]:
            cen_disp[col].iloc[0] = '{0}%'.format(round(cen_disp[col].iloc[0] * 100.0, 2))
        cen_data = '、'.join(cen_disp.set_index(['FUND_NAME', 'FUND_CODE']).iloc[0].tolist()[:4])
        cen_disp.columns = ['基金名称', '基金代码', '规模集中度水平', '风格集中度水平', '主题集中度水平', '行业集中度水平', '整体集中度水平']
        cen_disp = cen_disp.T.reset_index().T.reset_index().drop('index', axis=1)

        hbs_rank_history = FEDB().read_hbs_rank_history_given_code(self.target_fund_code)
        hbs_rank_history = hbs_rank_history.sort_values('jsrq').iloc[-12:]
        shift_ts = hbs_rank_history[['jjdm', 'jsrq', 'shift_ratio_size', 'shift_ratio_style', 'shift_ratio_theme', 'shift_ratio_ind']]
        shift_ts = shift_ts.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': '报告日期', 'shift_ratio_size': '规模换手率水平', 'shift_ratio_style': '风格换手率水平', 'shift_ratio_theme': '主题换手率水平', 'shift_ratio_ind': '行业换手率水平'})
        cen_ts = hbs_rank_history[['jjdm', 'jsrq', 'c_level_size', 'c_level_style', 'c_level_theme', 'c_level_ind']]
        cen_ts = cen_ts.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': '报告日期', 'c_level_size': '规模集中度水平', 'c_level_style': '风格集中度水平', 'c_level_theme': '主题集中度水平', 'c_level_ind': '行业集中度水平'})

        size_style_abs_history = FEDB().read_hbs_size_style_property_history_given_code(self.target_fund_code)
        theme_ind_abs_history = FEDB().read_hbs_theme_industry_property_history_given_code(int(self.target_fund_code))
        theme_ind_abs_history['jjdm'] = theme_ind_abs_history['jjdm'].apply(lambda x: str(x).zfill(6))
        stock_abs_history = FEDB().read_hbs_holding_property_history_given_code(self.target_fund_code)
        hbs_abs_history = size_style_abs_history.merge(theme_ind_abs_history, on=['jjdm', 'jsrq'], how='left').merge(stock_abs_history, on=['jjdm', 'jsrq'], how='left')
        hbs_abs_history = hbs_abs_history.sort_values('jsrq').iloc[-12:]
        shift_abs_ts = hbs_abs_history[['jjdm', 'jsrq', 'shift_ratio_size', 'shift_ratio_style', 'shift_ratio_theme', 'shift_ratio_ind']]
        shift_abs_ts = shift_abs_ts.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': '报告日期', 'shift_ratio_size': '规模换手率', 'shift_ratio_style': '风格换手率', 'shift_ratio_theme': '主题换手率', 'shift_ratio_ind': '行业换手率'})
        cen_abs_ts = hbs_abs_history[['jjdm', 'jsrq', 'c_level_size', 'c_level_style', 'c_level_theme', 'c_level_ind']]
        cen_abs_ts = cen_abs_ts.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': '报告日期', 'c_level_size': '规模集中度', 'c_level_style': '风格集中度', 'c_level_theme': '主题集中度', 'c_level_ind': '行业集中度'})

        shift_ts['报告日期'] = shift_ts['报告日期'].astype(str)
        change_dates = [date for date in self.change_dates if date >= min(shift_ts['报告日期']) and date <= max(shift_ts['报告日期'])]
        change_dates_loc = get_change_dates_loc(change_dates, sorted(shift_ts['报告日期'].unique().tolist()))
        fig, ax = plt.subplots(2, 2, figsize=(6, 6))
        pic_list = [['规模换手率水平', '风格换手率水平'], ['主题换手率水平', '行业换手率水平']]
        for i in range(2):
            for j in range(2):
                axij = sns.lineplot(ax=ax[i][j], x='报告日期', y=pic_list[i][j], data=shift_ts, color='#F04950')
                axij2 = sns.lineplot(ax=ax[i][j].twinx(), x='报告日期', y=pic_list[i][j][:5], data=shift_abs_ts, color='#6268A2')
                axij.set_xticklabels(labels=shift_ts['报告日期'], rotation=90)
                axij.set_ylim([0.0, 1.0])
                axij.hlines(y=0.5, xmin=0.0, xmax=11.0, linestyles='dashed', color='#959595')
                axij.vlines(x=change_dates_loc, ymin=0.0, ymax=1.0, linestyles='dashed', color='#959595')
                axij.yaxis.set_major_formatter(FuncFormatter(to_100percent))
                axij2.yaxis.set_major_formatter(FuncFormatter(to_100percent))
                axij.set_ylabel(pic_list[i][j] + '（红）')
                axij2.set_ylabel(pic_list[i][j][:5] + '（蓝）')
        plt.tight_layout()
        plt.savefig('{0}{1}_shift.png'.format(self.file_path, self.target_fund_code))

        fig, ax = plt.subplots(2, 2, figsize=(6, 6))
        pic_list = [['规模集中度水平', '风格集中度水平'], ['主题集中度水平', '行业集中度水平']]
        for i in range(2):
            for j in range(2):
                axij = sns.lineplot(ax=ax[i][j], x='报告日期', y=pic_list[i][j], data=cen_ts, color='#F04950')
                axij2 = sns.lineplot(ax=ax[i][j].twinx(), x='报告日期', y=pic_list[i][j][:5], data=cen_abs_ts, color='#6268A2')
                axij.set_xticklabels(labels=cen_ts['报告日期'], rotation=90)
                axij.set_ylim([0.0, 1.0])
                axij.hlines(y=0.5, xmin=0.0, xmax=11.0, linestyles='dashed', color='#959595')
                axij.vlines(x=change_dates_loc, ymin=0.0, ymax=1.0, linestyles='dashed', color='#959595')
                axij.yaxis.set_major_formatter(FuncFormatter(to_100percent))
                axij2.yaxis.set_major_formatter(FuncFormatter(to_100percent))
                axij.set_ylabel(pic_list[i][j] + '（红）')
                axij2.set_ylabel(pic_list[i][j][:5] + '（蓝）')
        plt.tight_layout()
        plt.savefig('{0}{1}_cen.png'.format(self.file_path, self.target_fund_code))

        stock_cen = FEDB().read_hbs_holding_property_given_code(self.target_fund_code)
        stock_cen = stock_cen.rename(columns={'jjdm': 'FUND_CODE', '个股集中度': 'STOCK_CONCENTRATION'})
        industry_stock_cen_disp = industry_cen.merge(stock_cen[['FUND_CODE', 'STOCK_CONCENTRATION']], on=['FUND_CODE'], how='left')
        industry_cen_mark = '低于50%' if industry_stock_cen_disp['INDUSTRY_CONCENTRATION'].iloc[0] < 0.5 else '高于70%' if industry_stock_cen_disp['INDUSTRY_CONCENTRATION'].iloc[0] > 0.7 else '处于50%与70%之间'
        stock_cen_mark = '低于50%' if industry_stock_cen_disp['STOCK_CONCENTRATION'].iloc[0] < 0.5 else '高于70%' if industry_stock_cen_disp['STOCK_CONCENTRATION'].iloc[0] > 0.7 else '处于50%与70%之间'
        select_mark = '选股方法为自下而上' if stock_cen_mark == '高于70%' and industry_cen_mark == '低于50%' else '选股方法为自上而下' if stock_cen_mark == '低于50%' and industry_cen_mark == '高于70%' else '无明显的自上而下或者自下而上选股特征'
        for col in list(industry_stock_cen_disp.columns)[2:]:
            industry_stock_cen_disp[col].iloc[0] = '{0}%'.format(round(industry_stock_cen_disp[col].iloc[0] * 100.0, 2))
        industry_stock_cen_disp.columns = ['基金名称', '基金代码', '行业集中度水平', '个股集中度水平']
        industry_stock_cen_disp = industry_stock_cen_disp.T.reset_index().T.reset_index().drop('index', axis=1)

        industry_stock_cen_ts = hbs_rank_history[['jjdm', 'jsrq', 'c_level_ind', 'c_level_stock']]
        industry_stock_cen_ts = industry_stock_cen_ts.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': '报告日期', 'c_level_ind': '行业集中度水平', 'c_level_stock': '个股集中度水平'})

        fig, ax = plt.subplots(figsize=(6, 3))
        sns.lineplot(x='报告日期', y='行业集中度水平', data=industry_stock_cen_ts, color='#F04950', label='行业集中度水平')
        sns.lineplot(x='报告日期', y='个股集中度水平', data=industry_stock_cen_ts, color='#6268A2', label='个股集中度水平')
        ax.set_xticklabels(labels=industry_stock_cen_ts['报告日期'], rotation=90)
        ax.set_ylim([0.0, 1.0])
        ax.hlines(y=0.5, xmin=0.0, xmax=11.0, linestyles='dashed', color='#959595')
        ax.vlines(x=change_dates_loc, ymin=0.0, ymax=1.0, linestyles='dashed', color='#959595')
        ax.yaxis.set_major_formatter(FuncFormatter(to_100percent))
        plt.ylabel('集中度水平')
        plt.legend(loc=2)
        plt.tight_layout()
        plt.savefig('{0}{1}_indu_stock_cen.png'.format(self.file_path, self.target_fund_code))

        holding_period = FEDB().read_hbs_stock_trading_property_given_code(self.target_fund_code)
        holding_period = holding_period[['jjdm', '平均持有时间（出持仓前）', '出全仓前平均收益率', '平均持有时间（出持仓前）_rank', '出全仓前平均收益率_rank']]
        holding_period = holding_period.rename(columns={'jjdm': 'FUND_CODE'})
        holding_period['FUND_NAME'] = self.target_fund_name
        holding_days = '{0}'.format(round(holding_period['平均持有时间（出持仓前）'].values[0], 0))
        holding_quarters = '{0}'.format(round(float(holding_days) / 91.0, 1))
        holding_rets = '{0}%'.format(round(holding_period['出全仓前平均收益率'].values[0] * 100, 2))
        holding_days_rank = '{0}%'.format(round(holding_period['平均持有时间（出持仓前）_rank'].values[0] * 100, 2))
        holding_rets_rank = '{0}%'.format(round(holding_period['出全仓前平均收益率_rank'].values[0] * 100, 2))
        holding_days_mark = '偏低' if holding_period['平均持有时间（出持仓前）_rank'].iloc[0] < 0.4 else '偏高' if holding_period['平均持有时间（出持仓前）_rank'].iloc[0] > 0.6 else '中等'
        holding_rets_mark = '偏低' if holding_period['出全仓前平均收益率_rank'].iloc[0] < 0.4 else '偏高' if holding_period['出全仓前平均收益率_rank'].iloc[0] > 0.6 else '中等'
        holding_period_disp = holding_period[['FUND_NAME', 'FUND_CODE', '平均持有时间（出持仓前）', '出全仓前平均收益率', '平均持有时间（出持仓前）_rank', '出全仓前平均收益率_rank']]
        for col in list(holding_period_disp.columns)[3:]:
            holding_period_disp[col].iloc[0] = '{0}%'.format(round(holding_period[col].iloc[0] * 100.0, 2))
        holding_period_disp.columns = ['基金名称', '基金代码', '平均持有时间', '持有期内平均收益率', '平均持有时间水平', '持有期内平均收益率水平']
        holding_period_disp = holding_period_disp.T.reset_index().T.reset_index().drop('index', axis=1)

        left_disp = FEDB().read_hbs_stock_trading_property_given_code(self.target_fund_code)
        left_disp = left_disp[['jjdm', '左侧概率（出持仓前,半年线）_rank', '左侧程度（出持仓前,半年线）']]
        left_disp = left_disp.rename(columns={'jjdm': 'FUND_CODE'})
        left_disp['FUND_NAME'] = self.target_fund_name
        left_mark = '该产品交易具有深度左侧特征' if (left_disp['左侧概率（出持仓前,半年线）_rank'][0] > 0.5 and left_disp['左侧程度（出持仓前,半年线）'][0] > 0.5) \
                    else '该产品交易具有左侧特征' if (left_disp['左侧概率（出持仓前,半年线）_rank'][0] > 0.5 and left_disp['左侧程度（出持仓前,半年线）'][0] <= 0.5) \
                    else '无明显的左侧特征'
        left_prob_data = '{0}%'.format(round(left_disp['左侧概率（出持仓前,半年线）_rank'].values[0] * 100, 2))
        left_level_data = '{0}%'.format(round(left_disp['左侧程度（出持仓前,半年线）'].values[0] * 100, 2))
        left_disp = left_disp[['FUND_NAME', 'FUND_CODE', '左侧概率（出持仓前,半年线）_rank', '左侧程度（出持仓前,半年线）']]
        for col in list(left_disp.columns)[2:]:
            left_disp[col].iloc[0] = '{0}%'.format(round(left_disp[col].iloc[0] * 100.0, 2))
        left_disp.columns = ['基金名称', '基金代码', '左侧概率水平', '左侧程度水平']
        left_disp = left_disp.T.reset_index().T.reset_index().drop('index', axis=1)

        new_disp = FEDB().read_hbs_stock_trading_property_given_code(self.target_fund_code)
        new_disp = new_disp[['jjdm', '新股概率（出持仓前）_rank', '次新股概率（出持仓前）_rank']]
        new_disp = new_disp.rename(columns={'jjdm': 'FUND_CODE'})
        new_disp['FUND_NAME'] = self.target_fund_name
        new_mark = '该产品选股偏好新股/次新股' if new_disp['新股概率（出持仓前）_rank'][0] > 0.75 and new_disp['次新股概率（出持仓前）_rank'][0] > 0.75 else '该产品选股偏好新股' if new_disp['新股概率（出持仓前）_rank'][0] > 0.75 and new_disp['次新股概率（出持仓前）_rank'][0] <= 0.75 else '该产品选股偏好次新股' if new_disp['次新股概率（出持仓前）_rank'][0] <= 0.75 and new_disp['次新股概率（出持仓前）_rank'][0] > 0.75 else '该产品选股无明显的新股/次新股偏好'
        new_data = '{0}%'.format(round(new_disp['新股概率（出持仓前）_rank'].values[0] * 100, 2))
        sub_new_data = '{0}%'.format(round(new_disp['次新股概率（出持仓前）_rank'].values[0] * 100, 2))
        new_disp = new_disp[['FUND_NAME', 'FUND_CODE', '新股概率（出持仓前）_rank', '次新股概率（出持仓前）_rank']]
        for col in list(new_disp.columns)[2:]:
            new_disp[col].iloc[0] = '{0}%'.format(round(new_disp[col].iloc[0] * 100.0, 2))
        new_disp.columns = ['基金名称', '基金代码', '新股概率水平', '次新股概率水平']
        new_disp = new_disp.T.reset_index().T.reset_index().drop('index', axis=1)

        fund_manager_method_text = '该产品近三年投资逻辑如下：'
        fund_manager_method_shift_text = '该产品整体换手率在所有偏股型基金中处于{0}水平，规模、风格、主题、行业换手率排名水平分别为{1}。'.format(shift_mark, shift_data)
        fund_manager_method_cen_text = '该产品整体集中度在所有偏股型基金中处于{0}水平，规模、风格、主题、行业集中度排名水平分别为{1}。'.format(cen_mark, cen_data)
        fund_manager_method_select_text = '该产品行业集中度{0}，个股集中度{1}，{2}。'.format(industry_cen_mark, stock_cen_mark, select_mark)
        fund_manager_method_holding_period_text = '该产品持仓平均时间为{0}天（大约{1}个季度），排名水平为{2}，在所有偏股型基金中处于{3}水平，持有期内平均收益率为{4}，排名水平为{5}，在所有偏股型基金中处于{6}水平。'.format(holding_days, holding_quarters, holding_days_rank, holding_days_mark, holding_rets, holding_rets_rank, holding_rets_mark)
        fund_manager_method_left_text = '该产品左侧概率水平排名{0}，左侧程度水平排名{1}，{2}。'.format(left_prob_data, left_level_data, left_mark)
        fund_manager_method_new_text = '该产品新股概率水平排名{0}，次新股概率水平排名{1}，{2}。'.format(new_data, sub_new_data, new_mark)

        compare_shift, compare_cen, compare_industry_stock_cen, compare_holding_period, compare_left, compare_new = pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame()
        if len(self.compare_fund_code_list) > 0:
            compare_shift_list, compare_cen_list, compare_industry_stock_cen_list, compare_holding_period_list, compare_left_list, compare_new_list = [], [], [], [], [], []
            for compare_fund_code in [self.target_fund_code] + self.compare_fund_code_list:
                fund_size_code = FEDB().read_hbs_size_property_given_code(compare_fund_code)
                fund_size_code = fund_size_code.rename(columns={'jjdm': 'FUND_CODE', 'asofdate': 'ASOFDATE', 'shift_lv': 'SIZE_TURNOVER_ABS', 'cen_lv': 'SIZE_CONCENTRATION_ABS', 'shift_lv_rank': 'SIZE_TURNOVER', 'cen_lv_rank': 'SIZE_CONCENTRATION'})
                fund_size_code['FUND_NAME'] = self.compare_fund_name_dic[compare_fund_code]
                size_shift_code = fund_size_code[['FUND_NAME', 'SIZE_TURNOVER_ABS', 'SIZE_TURNOVER']].set_index('FUND_NAME')
                size_cen_code = fund_size_code[['FUND_NAME', 'SIZE_CONCENTRATION_ABS', 'SIZE_CONCENTRATION']].set_index('FUND_NAME')
                size_shift_code.columns = ['规模换手率', '规模换手率排名']
                size_cen_code.columns = ['规模集中度', '规模集中度排名']

                fund_style_code = FEDB().read_hbs_style_property_given_code(compare_fund_code)
                fund_style_code = fund_style_code.rename(columns={'jjdm': 'FUND_CODE', 'asofdate': 'ASOFDATE', 'shift_lv': 'STYLE_TURNOVER_ABS', 'cen_lv': 'STYLE_CONCENTRATION_ABS', 'shift_lv_rank': 'STYLE_TURNOVER', 'cen_lv_rank': 'STYLE_CONCENTRATION'})
                fund_style_code['FUND_NAME'] = self.compare_fund_name_dic[compare_fund_code]
                style_shift_code = fund_style_code[['FUND_NAME', 'STYLE_TURNOVER_ABS', 'STYLE_TURNOVER']].set_index('FUND_NAME')
                style_cen_code = fund_style_code[['FUND_NAME', 'STYLE_CONCENTRATION_ABS', 'STYLE_CONCENTRATION']].set_index('FUND_NAME')
                style_shift_code.columns = ['风格换手率', '风格换手率排名']
                style_cen_code.columns = ['风格集中度', '风格集中度排名']

                fund_theme_code = FEDB().read_hbs_theme_industry_property_given_code(compare_fund_code)
                fund_theme_code = fund_theme_code.rename(columns={'jjdm': 'FUND_CODE', 'asofdate': 'ASOFDATE', 'ratio_theme': 'THEME_TURNOVER_ABS', 'cen_theme': 'THEME_CONCENTRATION_ABS', 'ratio_theme_rank': 'THEME_TURNOVER', 'cen_theme_rank': 'THEME_CONCENTRATION'})
                fund_theme_code['FUND_NAME'] = self.compare_fund_name_dic[compare_fund_code]
                theme_shift_code = fund_theme_code[['FUND_NAME', 'THEME_TURNOVER_ABS', 'THEME_TURNOVER']].set_index('FUND_NAME')
                theme_cen_code = fund_theme_code[['FUND_NAME', 'THEME_CONCENTRATION_ABS', 'THEME_CONCENTRATION']].set_index('FUND_NAME')
                theme_shift_code.columns = ['主题换手率', '主题换手率排名']
                theme_cen_code.columns = ['主题集中度', '主题集中度排名']

                fund_industry_code = FEDB().read_hbs_theme_industry_property_given_code(compare_fund_code)
                fund_industry_code = fund_industry_code.rename(columns={'jjdm': 'FUND_CODE', 'asofdate': 'ASOFDATE', 'ratio_ind': 'INDUSTRY_TURNOVER_ABS', 'cen_ind': 'INDUSTRY_CONCENTRATION_ABS', 'ratio_ind_rank': 'INDUSTRY_TURNOVER', 'cen_ind_rank': 'INDUSTRY_CONCENTRATION'})
                fund_industry_code['FUND_NAME'] = self.compare_fund_name_dic[compare_fund_code]
                industry_shift_code = fund_industry_code[['FUND_NAME', 'INDUSTRY_TURNOVER_ABS', 'INDUSTRY_TURNOVER']].set_index('FUND_NAME')
                industry_cen_code = fund_industry_code[['FUND_NAME', 'INDUSTRY_CONCENTRATION_ABS', 'INDUSTRY_CONCENTRATION']].set_index('FUND_NAME')
                industry_shift_code.columns = ['行业换手率', '行业换手率排名']
                industry_cen_code.columns = ['行业集中度', '行业集中度排名']

                stock_cen_code = FEDB().read_hbs_holding_property_given_code(compare_fund_code)
                stock_cen_code = stock_cen_code[['jjdm', '个股集中度']]
                stock_cen_code = stock_cen_code.rename(columns={'jjdm': 'FUND_CODE', '个股集中度': '个股集中度排名'})
                stock_cen_code['FUND_NAME'] = self.compare_fund_name_dic[compare_fund_code]
                stock_cen_code = stock_cen_code[['FUND_NAME', '个股集中度排名']].set_index('FUND_NAME')
                industry_stock_cen_code = industry_cen_code[['行业集中度排名']].merge(stock_cen_code, left_index=True, right_index=True, how='left')
                industry_stock_cen_code['MARK'] = industry_stock_cen_code.apply(lambda x: '自下而上' if x['行业集中度排名'] < 0.5 and x['个股集中度排名'] > 0.7 else '自上而下' if x['行业集中度排名'] > 0.7 and x['个股集中度排名'] < 0.5 else '无明显的自上而下或者自下而上选股特征', axis=1)
                industry_stock_cen_code.columns = ['行业集中度排名', '个股集中度排名', '选股方法']

                holding_period_code = FEDB().read_hbs_stock_trading_property_given_code(compare_fund_code)
                holding_period_code = holding_period_code[['jjdm', '平均持有时间（出持仓前）', '出全仓前平均收益率', '平均持有时间（出持仓前）_rank', '出全仓前平均收益率_rank']]
                holding_period_code = holding_period_code.rename(columns={'jjdm': 'FUND_CODE'})
                holding_period_code['FUND_NAME'] = self.compare_fund_name_dic[compare_fund_code]
                holding_period_code = holding_period_code[['FUND_NAME', '平均持有时间（出持仓前）', '出全仓前平均收益率', '平均持有时间（出持仓前）_rank', '出全仓前平均收益率_rank']].set_index('FUND_NAME')
                holding_period_code.columns = ['平均持有时间', '持有期内平均收益率', '平均持有时间排名', '持有期内平均收益率排名']

                left_code = FEDB().read_hbs_stock_trading_property_given_code(compare_fund_code)
                left_code = left_code[['jjdm', '左侧概率（出持仓前,半年线）_rank', '左侧程度（出持仓前,半年线）']]
                left_code = left_code.rename(columns={'jjdm': 'FUND_CODE'})
                left_code['FUND_NAME'] = self.compare_fund_name_dic[compare_fund_code]
                left_code['MARK'] = left_code.apply(lambda x: '深度左侧' if x['左侧概率（出持仓前,半年线）_rank'] > 0.5 and x['左侧程度（出持仓前,半年线）'] > 0.5 else '左侧' if x['左侧概率（出持仓前,半年线）_rank'] > 0.5 and x['左侧程度（出持仓前,半年线）'] <= 0.5 else '无明显的左侧特征', axis=1)
                left_code = left_code[['FUND_NAME', '左侧概率（出持仓前,半年线）_rank', '左侧程度（出持仓前,半年线）', 'MARK']].set_index('FUND_NAME')
                left_code.columns = ['左侧概率排名', '左侧程度排名', '左侧特征']

                new_code = FEDB().read_hbs_stock_trading_property_given_code(compare_fund_code)
                new_code = new_code[['jjdm', '新股概率（出持仓前）_rank', '次新股概率（出持仓前）_rank']]
                new_code = new_code.rename(columns={'jjdm': 'FUND_CODE'})
                new_code['FUND_NAME'] = self.compare_fund_name_dic[compare_fund_code]
                new_code['MARK'] = new_code.apply(lambda x: '偏好新股/次新股' if x['新股概率（出持仓前）_rank'] > 0.75 and x['次新股概率（出持仓前）_rank'] > 0.75 else '偏好新股' if x['新股概率（出持仓前）_rank'] > 0.75 and x['次新股概率（出持仓前）_rank'] <= 0.75 else '偏好次新股' if x['新股概率（出持仓前）_rank'] <= 0.75 and x['次新股概率（出持仓前）_rank'] > 0.75 else '无明显的新股/次新股偏好', axis=1)
                new_code = new_code[['FUND_NAME', '新股概率（出持仓前）_rank', '次新股概率（出持仓前）_rank', 'MARK']].set_index('FUND_NAME')
                new_code.columns = ['新股概率排名', '次新股概率排名', '新股/次新股特征']

                shift_code = pd.concat([size_shift_code, style_shift_code, theme_shift_code, industry_shift_code], axis=1)
                shift_code = shift_code[['规模换手率', '风格换手率', '主题换手率', '行业换手率', '规模换手率排名', '风格换手率排名', '主题换手率排名', '行业换手率排名']]
                cen_code = pd.concat([size_cen_code, style_cen_code, theme_cen_code, industry_cen_code], axis=1)
                cen_code = cen_code[['规模集中度', '风格集中度', '主题集中度', '行业集中度', '规模集中度排名', '风格集中度排名', '主题集中度排名', '行业集中度排名']]

                compare_shift_list.append(shift_code)
                compare_cen_list.append(cen_code)
                compare_industry_stock_cen_list.append(industry_stock_cen_code)
                compare_holding_period_list.append(holding_period_code)
                compare_left_list.append(left_code)
                compare_new_list.append(new_code)
            compare_shift = pd.concat(compare_shift_list)
            compare_cen= pd.concat(compare_cen_list)
            compare_industry_stock_cen = pd.concat(compare_industry_stock_cen_list)
            compare_holding_period = pd.concat(compare_holding_period_list)
            compare_left = pd.concat(compare_left_list)
            compare_new = pd.concat(compare_new_list)
            for col in list(compare_shift.columns):
                compare_shift[col] = compare_shift[col].apply(lambda x: '{0}%'.format(round(x * 100.0, 2)) if not np.isnan(x) else '-')
            for col in list(compare_cen.columns):
                compare_cen[col] = compare_cen[col].apply(lambda x: '{0}%'.format(round(x * 100.0, 2)) if not np.isnan(x) else '-')
            for col in list(compare_industry_stock_cen.columns)[:-1]:
                compare_industry_stock_cen[col] = compare_industry_stock_cen[col].apply(lambda x: '{0}%'.format(round(x * 100.0, 2)) if not np.isnan(x) else '-')
            for col in list(compare_holding_period.columns)[1:]:
                compare_holding_period[col] = compare_holding_period[col].apply(lambda x: '{0}%'.format(round(x * 100.0, 2)) if not np.isnan(x) else '-')
            for col in list(compare_left.columns)[:-1]:
                compare_left[col] = compare_left[col].apply(lambda x: '{0}%'.format(round(x * 100.0, 2)) if not np.isnan(x) else '-')
            for col in list(compare_new.columns)[:-1]:
                compare_new[col] = compare_new[col].apply(lambda x: '{0}%'.format(round(x * 100.0, 2)) if not np.isnan(x) else '-')
            compare_shift = compare_shift.T.reset_index().T.reset_index()
            compare_cen = compare_cen.T.reset_index().T.reset_index()
            compare_industry_stock_cen = compare_industry_stock_cen.T.reset_index().T.reset_index()
            compare_holding_period = compare_holding_period.T.reset_index().T.reset_index()
            compare_left = compare_left.T.reset_index().T.reset_index()
            compare_new = compare_new.T.reset_index().T.reset_index()
            compare_shift.iloc[0, 0] = ''
            compare_cen.iloc[0, 0] = ''
            compare_industry_stock_cen.iloc[0, 0] = ''
            compare_holding_period.iloc[0, 0] = ''
            compare_left.iloc[0, 0] = ''
            compare_new.iloc[0, 0] = ''
        return fund_manager_method_text, fund_manager_method_shift_text, shift_disp, fund_manager_method_cen_text, cen_disp, \
               fund_manager_method_select_text, industry_stock_cen_disp, fund_manager_method_holding_period_text, holding_period_disp, \
               fund_manager_method_left_text, left_disp, fund_manager_method_new_text, new_disp,\
               compare_shift, compare_cen, compare_industry_stock_cen, compare_holding_period, compare_left, compare_new

    def get_fund_position(self):
        """
        生成基金仓位数据
        """
        fund_position = HBDB().read_fund_position_given_code(self.target_fund_code)
        fund_position = fund_position.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'jjzzc': 'TOTAL', 'gptzsz': 'STOCK', 'zqzsz': 'BOND', 'jjtzszhj': 'FUND', 'hbzjsz': 'CASH'})
        fund_position['REPORT_DATE'] = fund_position['REPORT_DATE'].astype(str)
        fund_position['STOCK_RATIO'] = fund_position['STOCK'].fillna(0.0) / fund_position['TOTAL'] * 100.0
        fund_position['BOND_RATIO'] = fund_position['BOND'].fillna(0.0) / fund_position['TOTAL'] * 100.0
        fund_position['FUND_RATIO'] = fund_position['FUND'].fillna(0.0) / fund_position['TOTAL'] * 100.0
        fund_position['CASH_RATIO'] = fund_position['CASH'].fillna(0.0) / fund_position['TOTAL'] * 100.0
        fund_position['OTHER_RATIO'] = 100.0 - fund_position[['STOCK_RATIO', 'BOND_RATIO', 'FUND_RATIO', 'CASH_RATIO']].sum(axis=1)

        fund_position = fund_position.sort_values('REPORT_DATE').reset_index().drop('index', axis=1).iloc[-12:]
        fund_position = fund_position[['REPORT_DATE', 'STOCK_RATIO', 'BOND_RATIO', 'FUND_RATIO', 'CASH_RATIO', 'OTHER_RATIO']]
        fund_position.columns = ['报告日期', '股票', '债券', '基金', '现金', '其他']
        fund_position = fund_position.set_index('报告日期')

        change_dates = [date for date in self.change_dates if date >= min(fund_position.index) and date <= max(fund_position.index)]
        change_dates_loc = get_change_dates_loc(change_dates, sorted(fund_position.index.unique().tolist()))
        fig, ax = plt.subplots(figsize=(6, 3))
        fund_position.plot.area(ax=ax, stacked=True, color=['#D55659', '#E1777A', '#8588B7', '#626697', '#7D7D7E'])
        ax.vlines(x=change_dates_loc, ymin=0.0, ymax=100.0, linestyles='dashed', color='#959595')
        plt.legend(loc=2)
        plt.ylabel('占总资产比（%）')
        plt.tight_layout()
        plt.savefig('{0}{1}_position.png'.format(self.file_path, self.target_fund_code))

        fund_position_stock_mean = round(fund_position['股票'].mean(), 2)
        latest_fund_position = fund_position.iloc[-1].sort_values(ascending=False)
        fund_position_text = '该产品资产配置情况如下，其中股票资产近三年平均配置比例为{0}%，最新一期股票、债券、基金、现金、其他资产配置比例分别为{1}%、{2}%、{3}%、{4}%、{5}%。'\
            .format(fund_position_stock_mean, round(latest_fund_position['股票'].mean(), 2), round(latest_fund_position['债券'].mean(), 2), round(latest_fund_position['基金'].mean(), 2), round(latest_fund_position['现金'].mean(), 2), round(latest_fund_position['其他'].mean(), 2))

        compare_position = pd.DataFrame()
        if len(self.compare_fund_code_list) > 0:
            compare_position_list = []
            for compare_fund_code in [self.target_fund_code] + self.compare_fund_code_list:
                compare_position_code = HBDB().read_fund_position_given_code(compare_fund_code)
                compare_position_list.append(compare_position_code)
            compare_position = pd.concat(compare_position_list)
            compare_position = compare_position.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'jjzzc': 'TOTAL', 'gptzsz': 'STOCK', 'zqzsz': 'BOND', 'jjtzszhj': 'FUND', 'hbzjsz': 'CASH'})
            compare_position['STOCK_RATIO'] = compare_position['STOCK'].fillna(0.0) / compare_position['TOTAL']
            compare_position = compare_position[['FUND_CODE', 'REPORT_DATE', 'STOCK_RATIO']]
            compare_position['STOCK_RATIO'] = compare_position['STOCK_RATIO'].apply(lambda x: '{0}%'.format(round(x * 100.0, 2)))
            compare_position = compare_position.pivot(index='REPORT_DATE', columns='FUND_CODE', values='STOCK_RATIO')
            compare_position = compare_position.dropna(subset=[self.target_fund_code], axis=0).sort_index().iloc[-12:]
            compare_position = compare_position[[self.target_fund_code] + self.compare_fund_code_list]
            compare_position.columns = [self.compare_fund_name_dic[col] for col in compare_position.columns]
            compare_position = compare_position.replace(np.nan, '-')
            compare_position = compare_position.T.reset_index().T.reset_index()
            compare_position.iloc[0, 0] = '股票仓位占比'
        return fund_position_text, compare_position

    def get_fund_size(self):
        """
        生成基金规模数据
        """
        size_list = ['大盘', '中盘', '小盘']
        size_rank_list = ['大盘_rank', '中盘_rank', '小盘_rank']
        fund_size = FEDB().read_hbs_size_property_given_code(self.target_fund_code)
        fund_size = fund_size.rename(columns={'jjdm': 'FUND_CODE', 'asofdate': 'ASOFDATE'})

        fund_size_shift = round(fund_size['shift_lv_rank'].values[0] * 100.0, 2)
        fund_size_cen = round(fund_size['cen_lv_rank'].values[0] * 100.0, 2)
        size_type = '配置型' if fund_size_shift <= 50.0 and fund_size_cen <= 50.0 else '专注型' if fund_size_shift <= 50.0 and fund_size_cen > 50.0 else '轮动型' if fund_size_shift > 50.0 and fund_size_cen <= 50.0 else '博弈型'

        largest_size_df = fund_size[['FUND_CODE'] + size_list].set_index('FUND_CODE').T.sort_values(self.target_fund_code, ascending=False)
        largest_size_data = largest_size_df[self.target_fund_code].iloc[0]
        largest_size = '、'.join(list(largest_size_df[largest_size_df[self.target_fund_code] == largest_size_data].index))
        largest_size_data = round(largest_size_data * 100.0, 2)
        largest_size_rank_df = fund_size[['FUND_CODE'] + size_rank_list].set_index('FUND_CODE').T.sort_values(self.target_fund_code, ascending=False)
        largest_size_rank_df.index = map(lambda x: x.split('_')[0], largest_size_rank_df.index)
        largest_size_rank_data = largest_size_rank_df[self.target_fund_code].iloc[0]
        largest_size_rank = '、'.join(list(largest_size_rank_df[largest_size_rank_df[self.target_fund_code] == largest_size_rank_data].index))
        largest_size_rank_data = round(largest_size_rank_data * 100.0, 2)
        fund_size_disp = fund_size.copy(deep=True)
        fund_size_disp['FUND_NAME'] = self.target_fund_name
        for col in ['shift_lv', 'cen_lv'] + size_list + ['shift_lv_rank', 'cen_lv_rank'] + size_rank_list:
            fund_size_disp[col].iloc[0] = '{0}%'.format(round(fund_size_disp[col].iloc[0] * 100.0, 2))
        fund_size_disp = fund_size_disp[['FUND_NAME', 'FUND_CODE'] + ['shift_lv', 'cen_lv'] + size_list + ['shift_lv_rank', 'cen_lv_rank'] + size_rank_list]
        fund_size_disp.columns = ['基金名称', '基金代码'] + ['规模换手率', '规模集中度'] + size_list + ['规模换手率排名', '规模集中度排名'] + [size_rank.replace('_rank', '排名') for size_rank in size_rank_list]
        fund_size_disp = fund_size_disp.T.reset_index().T.reset_index().drop('index', axis=1)

        fund_size_ts = FEDB().read_size_exposure_given_code(self.target_fund_code)
        fund_size_ts = fund_size_ts.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'size_type': 'SIZE_TYPE', 'zjbl': 'MV_IN_NA'})
        fund_size_ts = fund_size_ts[fund_size_ts['REPORT_DATE'].isin(sorted(fund_size_ts['REPORT_DATE'].unique().tolist())[-12:])]
        fund_size_ts = fund_size_ts[['REPORT_DATE', 'SIZE_TYPE', 'MV_IN_NA']]
        fund_size_ts.columns = ['报告日期', '规模类型', '规模暴露水平（%）']

        fund_size_ts['报告日期'] = fund_size_ts['报告日期'].astype(str)
        change_dates = [date for date in self.change_dates if date >= min(fund_size_ts['报告日期']) and date <= max(fund_size_ts['报告日期'])]
        change_dates_loc = get_change_dates_loc(change_dates, sorted(fund_size_ts['报告日期'].unique().tolist()))
        fig, ax = plt.subplots(figsize=(6, 3))
        sns.barplot(ax=ax, x='报告日期', y='规模暴露水平（%）', data=fund_size_ts, hue='规模类型', hue_order=size_list, palette=['#C94649', '#8588B7', '#7D7D7E'])
        ax.vlines(x=change_dates_loc, ymin=0.0, ymax=max(fund_size_ts['规模暴露水平（%）']), linestyles='dashed', color='#959595')
        ax.set_xticklabels(labels=sorted(fund_size_ts['报告日期'].unique().tolist()), rotation=90)
        plt.legend(loc=2)
        plt.tight_layout()
        plt.savefig('{0}{1}_size.png'.format(self.file_path, self.target_fund_code))

        fund_size_text = '该产品在统计区间内（近三年）规模换手率在所有偏股型基金池中排名{0}%，规模集中度在所有偏股型基金池中排名{1}%，属于规模{2}基金。从规模绝对暴露看，其在{3}上暴露最大，绝对暴露度为{4}%，从规模相对暴露看，其在{5}上暴露最大，相对暴露度为{6}%。' \
            .format(fund_size_shift, fund_size_cen, size_type, largest_size, largest_size_data, largest_size_rank, largest_size_rank_data)

        compare_size = pd.DataFrame()
        if len(self.compare_fund_code_list) > 0:
            compare_size_list = []
            for compare_fund_code in [self.target_fund_code] + self.compare_fund_code_list:
                compare_size_code = FEDB().read_hbs_size_property_given_code(compare_fund_code)
                compare_size_list.append(compare_size_code)
            compare_size = pd.concat(compare_size_list)
            compare_size = compare_size.rename(columns={'jjdm': 'FUND_CODE', 'asofdate': 'ASOFDATE'})
            for col in size_list + size_rank_list:
                compare_size[col] = compare_size[col].apply(lambda x: '{0}%'.format(round(x * 100.0, 2)))
            compare_size['FUND_NAME'] = compare_size['FUND_CODE'].apply(lambda x: self.compare_fund_name_dic[x])
            compare_size = compare_size[['FUND_NAME'] + size_list + size_rank_list].set_index('FUND_NAME')
            compare_size = compare_size.loc[[self.compare_fund_name_dic[code] for code in [self.target_fund_code] + self.compare_fund_code_list]]
            compare_size.columns = ['大盘绝对暴露', '中盘绝对暴露', '小盘绝对暴露', '大盘暴露排名', '中盘暴露排名', '小盘暴露排名']
            compare_size = compare_size.replace(np.nan, '0.00%')
            compare_size = compare_size.T.reset_index().T.reset_index()
            compare_size.iloc[0, 0] = ''
        return fund_size_disp, fund_size_text, compare_size

    def get_fund_style(self):
        """
        生成基金风格数据
        """
        style_list = ['成长', '价值']
        style_rank_list = ['成长_rank', '价值_rank']
        fund_style = FEDB().read_hbs_style_property_given_code(self.target_fund_code)
        fund_style = fund_style.rename(columns={'jjdm': 'FUND_CODE', 'asofdate': 'ASOFDATE'})

        fund_style_shift = round(fund_style['shift_lv_rank'].values[0] * 100.0, 2)
        fund_style_cen = round(fund_style['cen_lv_rank'].values[0] * 100.0, 2)
        style_type = '配置型' if fund_style_shift <= 50.0 and fund_style_cen <= 50.0 else '专注型' if fund_style_shift <= 50.0 and fund_style_cen > 50.0 else '轮动型' if fund_style_shift > 50.0 and fund_style_cen <= 50.0 else '博弈型'

        largest_style_df = fund_style[['FUND_CODE'] + style_list].set_index('FUND_CODE').T.sort_values(self.target_fund_code, ascending=False)
        largest_style_data = largest_style_df[self.target_fund_code].iloc[0]
        largest_style = '、'.join(list(largest_style_df[largest_style_df[self.target_fund_code] == largest_style_data].index))
        largest_style_data = round(largest_style_data * 100.0, 2)
        largest_style_rank_df = fund_style[['FUND_CODE'] + style_rank_list].set_index('FUND_CODE').T.sort_values(self.target_fund_code, ascending=False)
        largest_style_rank_df.index = map(lambda x: x.split('_')[0], largest_style_rank_df.index)
        largest_style_rank_data = largest_style_rank_df[self.target_fund_code].iloc[0]
        largest_style_rank = '、'.join(list(largest_style_rank_df[largest_style_rank_df[self.target_fund_code] == largest_style_rank_data].index))
        largest_style_rank_data = round(largest_style_rank_data * 100.0, 2)
        fund_style_disp = fund_style.copy(deep=True)
        fund_style_disp['FUND_NAME'] = self.target_fund_name
        for col in ['shift_lv', 'cen_lv'] + style_list + ['shift_lv_rank', 'cen_lv_rank'] + style_rank_list:
            fund_style_disp[col].iloc[0] = '{0}%'.format(round(fund_style_disp[col].iloc[0] * 100.0, 2))
        fund_style_disp = fund_style_disp[['FUND_NAME', 'FUND_CODE'] + ['shift_lv', 'cen_lv'] + style_list + ['shift_lv_rank', 'cen_lv_rank'] + style_rank_list]
        fund_style_disp.columns = ['基金名称', '基金代码'] + ['风格换手率', '风格集中度'] + style_list + ['风格换手率排名', '风格集中度排名'] + [style_rank.replace('_rank', '排名') for style_rank in style_rank_list]
        fund_style_disp = fund_style_disp.T.reset_index().T.reset_index().drop('index', axis=1)

        fund_style_ts = FEDB().read_style_exposure_given_code(self.target_fund_code)
        fund_style_ts = fund_style_ts.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'style_type': 'STYLE_TYPE', 'zjbl': 'MV_IN_NA'})
        fund_style_ts = fund_style_ts[fund_style_ts['REPORT_DATE'].isin(sorted(fund_style_ts['REPORT_DATE'].unique().tolist())[-12:])]
        fund_style_ts = fund_style_ts[['REPORT_DATE', 'STYLE_TYPE', 'MV_IN_NA']]
        fund_style_ts.columns = ['报告日期', '风格类型', '风格暴露水平（%）']

        fund_style_ts['报告日期'] = fund_style_ts['报告日期'].astype(str)
        change_dates = [date for date in self.change_dates if date >= min(fund_style_ts['报告日期']) and date <= max(fund_style_ts['报告日期'])]
        change_dates_loc = get_change_dates_loc(change_dates, sorted(fund_style_ts['报告日期'].unique().tolist()))
        fig, ax = plt.subplots(figsize=(6, 3))
        sns.barplot(ax=ax, x='报告日期', y='风格暴露水平（%）', data=fund_style_ts, hue='风格类型', hue_order=style_list, palette=['#C94649', '#8588B7'])
        ax.vlines(x=change_dates_loc, ymin=0.0, ymax=max(fund_style_ts['风格暴露水平（%）']), linestyles='dashed', color='#959595')
        ax.set_xticklabels(labels=sorted(fund_style_ts['报告日期'].unique().tolist()), rotation=90)
        plt.legend(loc=2)
        plt.tight_layout()
        plt.savefig('{0}{1}_style.png'.format(self.file_path, self.target_fund_code))

        fund_style_text = '该产品在统计区间内（近三年）风格换手率在所有偏股型基金池中排名{0}%，风格集中度在所有偏股型基金池中排名{1}%，属于风格{2}基金。从风格绝对暴露看，其在{3}上暴露最大，绝对暴露度为{4}%，从风格相对暴露看，其在{5}上暴露最大，相对暴露度为{6}%。' \
            .format(fund_style_shift, fund_style_cen, style_type, largest_style, largest_style_data, largest_style_rank, largest_style_rank_data)

        compare_style = pd.DataFrame()
        if len(self.compare_fund_code_list) > 0:
            compare_style_list = []
            for compare_fund_code in [self.target_fund_code] + self.compare_fund_code_list:
                compare_style_code = FEDB().read_hbs_style_property_given_code(compare_fund_code)
                compare_style_list.append(compare_style_code)
            compare_style = pd.concat(compare_style_list)
            compare_style = compare_style.rename(columns={'jjdm': 'FUND_CODE', 'asofdate': 'ASOFDATE'})
            for col in style_list + style_rank_list:
                compare_style[col] = compare_style[col].apply(lambda x: '{0}%'.format(round(x * 100.0, 2)))
            compare_style['FUND_NAME'] = compare_style['FUND_CODE'].apply(lambda x: self.compare_fund_name_dic[x])
            compare_style = compare_style[['FUND_NAME'] + style_list + style_rank_list].set_index('FUND_NAME')
            compare_style = compare_style.loc[[self.compare_fund_name_dic[code] for code in [self.target_fund_code] + self.compare_fund_code_list]]
            compare_style.columns = ['成长绝对暴露', '价值绝对暴露', '成长暴露排名', '价值暴露排名']
            compare_style = compare_style.replace(np.nan, '0.00%')
            compare_style = compare_style.T.reset_index().T.reset_index()
            compare_style.iloc[0, 0] = ''
        return fund_style_disp, fund_style_text, compare_style

    def get_fund_theme(self):
        """
        生成基金主题数据
        """
        theme_list = ['大金融', '消费', 'TMT', '制造', '周期']
        fund_theme_shift_cen = FEDB().read_hbs_theme_industry_property_given_code(self.target_fund_code)
        fund_theme = FEDB().read_hbs_theme_property_given_code(self.target_fund_code)
        fund_theme = fund_theme_shift_cen.drop('asofdate', axis=1).merge(fund_theme.drop('asofdate', axis=1), on=['jjdm'], how='left')
        fund_theme = fund_theme[['jjdm', 'ratio_theme', 'cen_theme'] + theme_list + ['ratio_theme_rank', 'cen_theme_rank']]
        fund_theme = fund_theme.rename(columns={'jjdm': 'FUND_CODE', 'ratio_theme': 'THEME_TURNOVER', 'cen_theme': 'THEME_CONCENTRATION', 'ratio_theme_rank': 'THEME_TURNOVER_RANK', 'cen_theme_rank': 'THEME_CONCENTRATION_RANK'})

        fund_theme_ratio = round(fund_theme['THEME_TURNOVER_RANK'].values[0] * 100.0, 2)
        fund_theme_cen = round(fund_theme['THEME_CONCENTRATION_RANK'].values[0] * 100.0, 2)
        theme_type = '配置型' if fund_theme_ratio <= 50.0 and fund_theme_cen <= 50.0 else '专注型' if fund_theme_ratio <= 50.0and fund_theme_cen > 50.0 else '轮动型' if fund_theme_ratio > 50.0 and fund_theme_cen <= 50.0 else '博弈型'

        fund_theme_disp = fund_theme.set_index('FUND_CODE')[theme_list].T.sort_values(self.target_fund_code, ascending=False)
        fund_theme_rank = list(fund_theme_disp.index)
        fund_theme_data_rank = fund_theme_disp[self.target_fund_code].unique().tolist()
        fund_theme_data_rank = ['{0}%'.format(round(data * 100.0, 2)) for data in fund_theme_data_rank]
        fund_theme_disp = fund_theme_disp.T.reset_index()
        fund_theme_disp['FUND_NAME'] = self.target_fund_name
        fund_theme_disp = fund_theme_disp.merge(fund_theme[['FUND_CODE', 'THEME_TURNOVER', 'THEME_CONCENTRATION', 'THEME_TURNOVER_RANK', 'THEME_CONCENTRATION_RANK']], on=['FUND_CODE'], how='left')
        for col in ['THEME_TURNOVER', 'THEME_CONCENTRATION'] + fund_theme_rank + ['THEME_TURNOVER_RANK', 'THEME_CONCENTRATION_RANK']:
            fund_theme_disp[col].iloc[0] = '{0}%'.format(round(fund_theme_disp[col].iloc[0] * 100.0, 2))
        fund_theme_disp = fund_theme_disp[['FUND_NAME', 'FUND_CODE', 'THEME_TURNOVER', 'THEME_CONCENTRATION'] + fund_theme_rank + ['THEME_TURNOVER_RANK', 'THEME_CONCENTRATION_RANK']]
        fund_theme_disp.columns = ['基金名称', '基金代码', '主题换手率', '主题集中度'] + fund_theme_rank + ['主题换手率排名', '主题集中度排名']
        fund_theme_disp = fund_theme_disp.T.reset_index().T.reset_index().drop('index', axis=1)

        fund_theme_ts = HBDB().read_fund_theme_given_codes([self.target_fund_code])
        fund_theme_ts = fund_theme_ts.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'zclb': 'IS_ZC', 'zblx': 'THEME', 'zgpszb': 'MV_IN_NA', 'jsqbd': 'MV_IN_NA_DIFF', 'tlpj': 'HOMO_AVG'})
        fund_theme_ts['THEME'] = fund_theme_ts['THEME'].replace({'TMT': 'TMT', 'ZZ': '制造', 'ZQ': '周期', 'DJR': '大金融', 'XF': '消费', 'QT': '其他'})
        fund_theme_ts = fund_theme_ts[fund_theme_ts['IS_ZC'] == '1']
        fund_theme_ts = fund_theme_ts.pivot(index='REPORT_DATE', columns='THEME', values='MV_IN_NA').reset_index().fillna(0.0)
        theme_list = [theme for theme in theme_list if theme in list(fund_theme_ts.columns)]
        for i in range(1, len(theme_list)):
            fund_theme_ts[theme_list[i]] = fund_theme_ts[theme_list[i]] + fund_theme_ts[theme_list[i - 1]]
        fund_theme_ts = fund_theme_ts.iloc[-12:]
        fund_theme_ts = fund_theme_ts[['REPORT_DATE'] + theme_list]
        fund_theme_ts.columns = ['报告日期'] + theme_list

        fund_theme_ts['报告日期'] = fund_theme_ts['报告日期'].astype(str)
        change_dates = [date for date in self.change_dates if date >= min(fund_theme_ts['报告日期']) and date <= max(fund_theme_ts['报告日期'])]
        change_dates_loc = get_change_dates_loc(change_dates, sorted(fund_theme_ts['报告日期'].unique().tolist()))
        fig, ax = plt.subplots(figsize=(8, 4))
        color_list = [bar_color_list[0], bar_color_list[1], bar_color_list[7], bar_color_list[8], bar_color_list[14]]
        for i in range(len(theme_list) - 1, -1, -1):
            sns.barplot(ax=ax, x='报告日期', y=theme_list[i], data=fund_theme_ts, label=theme_list[i], color=color_list[i])
        ax.vlines(x=change_dates_loc, ymin=0.0, ymax=max(fund_theme_ts[theme_list[-1]]), linestyles='dashed', color='#959595')
        plt.legend(loc='upper right', bbox_to_anchor=(1.2, 1.02))
        plt.xticks(rotation=90)
        plt.ylabel('主题占比（%）')
        plt.tight_layout()
        plt.savefig('{0}{1}_theme.png'.format(self.file_path, self.target_fund_code))

        fund_theme_text = '该产品在统计区间内（近三年）主题换手率在所有偏股型基金池中排名{0}%，主题集中度在所有偏股型基金池中排名{1}%，属于主题{2}基金，其主题暴露排序为{3}，相应的暴露占比为{4}。' \
            .format(fund_theme_ratio, fund_theme_cen, theme_type, '、'.join(fund_theme_rank), '、'.join(fund_theme_data_rank))

        compare_theme = pd.DataFrame()
        if len(self.compare_fund_code_list) > 0:
            compare_theme_list = []
            for compare_fund_code in [self.target_fund_code] + self.compare_fund_code_list:
                compare_theme_code = FEDB().read_hbs_theme_property_given_code(compare_fund_code)
                compare_theme_list.append(compare_theme_code)
            compare_theme = pd.concat(compare_theme_list)
            compare_theme = compare_theme.rename(columns={'jjdm': 'FUND_CODE', 'asofdate': 'ASOFDATE'})
            for col in theme_list:
                compare_theme[col] = compare_theme[col].apply(lambda x: '{0}%'.format(round(x * 100.0, 2)))
            compare_theme['FUND_NAME'] = compare_theme['FUND_CODE'].apply(lambda x: self.compare_fund_name_dic[x])
            compare_theme = compare_theme[['FUND_NAME'] +theme_list].set_index('FUND_NAME')
            compare_theme = compare_theme.loc[[self.compare_fund_name_dic[code] for code in [self.target_fund_code] + self.compare_fund_code_list]]
            compare_theme = compare_theme.replace(np.nan, '0.00%')
            compare_theme = compare_theme.T.reset_index().T.reset_index()
            compare_theme.iloc[0, 0] = ''
        return fund_theme_disp, fund_theme_text, compare_theme

    def get_fund_industry(self):
        """
        生成基金行业数据
        """
        fund_industry_shift_cen = FEDB().read_hbs_theme_industry_property_given_code(self.target_fund_code)
        fund_industry = FEDB().read_hbs_industry_property_given_code(self.target_fund_code)
        fund_industry = fund_industry.pivot(index='jjdm', columns='yjxymc', values='zsbl_mean')
        industry_list = list(fund_industry.columns)
        fund_industry = fund_industry_shift_cen.drop('asofdate', axis=1).merge(fund_industry.reset_index(), on=['jjdm'], how='left')
        fund_industry = fund_industry[['jjdm', 'ratio_ind', 'cen_ind'] + industry_list + ['ratio_ind_rank', 'cen_ind_rank']]
        fund_industry = fund_industry.rename(columns={'jjdm': 'FUND_CODE', 'ratio_ind': 'INDUSTRY_TURNOVER', 'cen_ind': 'INDUSTRY_CONCENTRATION', 'ratio_ind_rank': 'INDUSTRY_TURNOVER_RANK', 'cen_ind_rank': 'INDUSTRY_CONCENTRATION_RANK'})

        fund_industry_ratio = round(fund_industry['INDUSTRY_TURNOVER_RANK'].values[0] * 100.0, 2)
        fund_industry_cen = round(fund_industry['INDUSTRY_CONCENTRATION_RANK'].values[0] * 100.0, 2)
        industry_type = '配置型' if fund_industry_ratio <= 50.0 and fund_industry_cen <= 50.0 else '专注型' if fund_industry_ratio <= 50.0and fund_industry_cen > 50.0 else '轮动型' if fund_industry_ratio > 50.0 and fund_industry_cen <= 50.0 else '博弈型'

        fund_industry_disp = fund_industry.set_index('FUND_CODE')[industry_list].T.sort_values(self.target_fund_code, ascending=False)
        fund_industry_rank = list(fund_industry_disp.index)[:5]
        fund_industry_data_rank = fund_industry_disp[self.target_fund_code].unique().tolist()[:5]
        fund_industry_data_rank = ['{0}%'.format(round(data * 100.0, 2)) for data in fund_industry_data_rank]
        fund_industry_disp = fund_industry_disp.T.reset_index()
        fund_industry_disp['FUND_NAME'] = self.target_fund_name
        fund_industry_disp = fund_industry_disp.merge(fund_industry[['FUND_CODE', 'INDUSTRY_TURNOVER', 'INDUSTRY_CONCENTRATION', 'INDUSTRY_TURNOVER_RANK', 'INDUSTRY_CONCENTRATION_RANK']], on=['FUND_CODE'], how='left')
        for col in ['INDUSTRY_TURNOVER', 'INDUSTRY_CONCENTRATION'] + fund_industry_rank + ['INDUSTRY_TURNOVER_RANK', 'INDUSTRY_CONCENTRATION_RANK']:
            fund_industry_disp[col].iloc[0] = '{0}%'.format(round(fund_industry_disp[col].iloc[0] * 100.0, 2))
        fund_industry_disp = fund_industry_disp[['FUND_NAME', 'FUND_CODE', 'INDUSTRY_TURNOVER', 'INDUSTRY_CONCENTRATION'] + fund_industry_rank + ['INDUSTRY_TURNOVER_RANK', 'INDUSTRY_CONCENTRATION_RANK']]
        fund_industry_disp.columns = ['基金名称', '基金代码', '行业换手率', '行业集中度'] + fund_industry_rank + ['行业换手率排名', '行业集中度排名']
        fund_industry_disp = fund_industry_disp.T.reset_index().T.reset_index().drop('index', axis=1)

        industry_new_list = ['电力设备', '商贸零售', '食品饮料', '农林牧渔', '有色金属', '银行', '非银金融', '汽车', '石油石化', '建筑材料', '房地产', '钢铁', '建筑装饰', '公用事业', '医药生物', '家用电器', '电子', '通信', '轻工制造', '机械设备', '传媒', '基础化工', '计算机', '煤炭', '交通运输', '纺织服装', '环保', '社会服务', '美容护理', '国防军工', '综合']
        fund_holding = HBDB().read_fund_holding_given_codes([self.target_fund_code])
        fund_holding = fund_holding.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'zqdm': 'TICKER_SYMBOL', 'zqmc': 'SEC_SHORT_NAME', 'ccsz': 'HOLDING_MARKET_VALUE', 'ccsl': 'HOLDING_AMOUNT', 'zjbl': 'MV_IN_NA'})
        fund_holding = fund_holding.sort_values(['FUND_CODE', 'REPORT_DATE', 'MV_IN_NA'], ascending=[True, True, False]).groupby(['FUND_CODE', 'REPORT_DATE']).head(10)
        stock_industry = HBDB().read_stock_industry()
        stock_industry = stock_industry.rename(columns={'zqdm': 'TICKER_SYMBOL', 'flmc': 'INDUSTRY_NAME', 'fldm': 'INDUSTRY_ID', 'fljb': 'INDUSTRY_TYPE', 'hyhfbz': 'INDUSTRY_VERSION', 'qsrq': 'BEGIN_DATE', 'jsrq': 'END_DATE', 'sfyx': 'IS_NEW'})
        stock_industry = stock_industry[stock_industry['IS_NEW'] == 1]
        stock_industry = stock_industry[stock_industry['INDUSTRY_VERSION'] == 2]
        stock_industry = stock_industry[stock_industry['INDUSTRY_TYPE'] == '1']
        stock_industry = stock_industry.sort_values(['TICKER_SYMBOL', 'credt_etl']).drop_duplicates('TICKER_SYMBOL', keep='last')
        fund_holding = fund_holding.merge(stock_industry[['TICKER_SYMBOL', 'INDUSTRY_NAME']], on=['TICKER_SYMBOL'], how='left')
        fund_industry_ts = fund_holding[['REPORT_DATE', 'INDUSTRY_NAME', 'MV_IN_NA']].groupby(['REPORT_DATE', 'INDUSTRY_NAME']).sum().reset_index()
        fund_industry_ts = fund_industry_ts.pivot(index='REPORT_DATE', columns='INDUSTRY_NAME', values='MV_IN_NA').reset_index().fillna(0.0)
        industry_ts_list = [indu for indu in industry_new_list if indu in list(fund_industry_ts.columns)[1:]]
        for i in range(1, len(industry_ts_list)):
            fund_industry_ts[industry_ts_list[i]] = fund_industry_ts[industry_ts_list[i]] + fund_industry_ts[industry_ts_list[i - 1]]
        fund_industry_ts = fund_industry_ts.iloc[-12:]
        fund_industry_ts = fund_industry_ts[['REPORT_DATE'] + industry_ts_list]
        fund_industry_ts.columns = ['报告日期'] + industry_ts_list


        # fund_industry_ts = HBDB().read_fund_industry_given_codes([fund_code])
        # fund_industry_ts = fund_industry_ts.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'zclb': 'IS_ZC', 'hyhfbz': 'INDUSTRY_VERSION', 'fldm': 'INDUSTRY_ID', 'flmc': 'INDUSTRY_NAME', 'zzjbl': 'MV_IN_NA', 'hyzjzbjbd': 'MV_IN_NA_DIFF', 'hyzjzbltlpj': 'HOMO_AVG'})
        # fund_industry_ts = fund_industry_ts[fund_industry_ts['INDUSTRY_VERSION'] == '2']
        # fund_industry_ts = fund_industry_ts[fund_industry_ts['IS_ZC'] == '1']
        # fund_industry_ts = fund_industry_ts.pivot(index='REPORT_DATE', columns='INDUSTRY_NAME', values='MV_IN_NA').reset_index().fillna(0.0)
        # industry_ts_list = list(fund_industry_ts.columns)[1:]
        # for i in range(1, len(industry_ts_list)):
        #     fund_industry_ts[industry_ts_list[i]] = fund_industry_ts[industry_ts_list[i]] + fund_industry_ts[industry_ts_list[i - 1]]
        # fund_industry_ts = fund_industry_ts.iloc[-12:]
        # fund_industry_ts = fund_industry_ts[['REPORT_DATE'] + industry_ts_list]
        # fund_industry_ts.columns = ['报告日期'] + industry_ts_list

        fund_industry_ts['报告日期'] = fund_industry_ts['报告日期'].astype(str)
        change_dates = [date for date in self.change_dates if date >= min(fund_industry_ts['报告日期']) and date <= max(fund_industry_ts['报告日期'])]
        change_dates_loc = get_change_dates_loc(change_dates, sorted(fund_industry_ts['报告日期'].unique().tolist()))
        fig, ax = plt.subplots(figsize=(10, 6))
        palette = bar_color_list + bar_color_list
        for i in range(len(industry_ts_list) - 1, -1, -1):
            sns.barplot(ax=ax, x='报告日期', y=industry_ts_list[i], data=fund_industry_ts, label=industry_ts_list[i], color=palette[i])
        ax.vlines(x=change_dates_loc, ymin=0.0, ymax=max(fund_industry_ts[industry_ts_list[-1]]), linestyles='dashed', color='#959595')
        plt.legend(loc='upper right', bbox_to_anchor=(1.2, 1.03))
        plt.xticks(rotation=90)
        plt.ylabel('行业占比（%）')
        plt.tight_layout()
        plt.savefig('{0}{1}_industry.png'.format(self.file_path, self.target_fund_code))

        fund_industry_text = '该产品在统计区间内（近三年）行业换手率在所有偏股型基金池中排名{0}%，行业集中度在所有偏股型基金池中排名{1}%，属于行业{2}基金，其前五大行业为{3}，相应的暴露占比为{4}。' \
            .format(fund_industry_ratio, fund_industry_cen, industry_type, '、'.join(fund_industry_rank), '、'.join(fund_industry_data_rank))

        compare_industry = pd.DataFrame()
        if len(self.compare_fund_code_list) > 0:
            compare_industry_list = []
            for compare_fund_code in [self.target_fund_code] + self.compare_fund_code_list:
                compare_industry_code = FEDB().read_hbs_industry_property_given_code(compare_fund_code)
                compare_industry_code = compare_industry_code[['jjdm', 'yjxymc', 'zsbl_mean']].sort_values('zsbl_mean', ascending=False).groupby(['jjdm']).head(5)
                compare_industry_code['MARK'] = range(1, 6)
                compare_industry_code['MARK'] = compare_industry_code['MARK'].apply(lambda x: 'TOP{0}'.format(x))
                compare_industry_list.append(compare_industry_code)
            compare_industry = pd.concat(compare_industry_list)
            compare_industry['INDUSTRY_DISP'] = compare_industry.apply(lambda x: '{0}（{1}%）'.format(x['yjxymc'], round(x['zsbl_mean'] * 100.0, 2)), axis=1)
            compare_industry['FUND_NAME'] = compare_industry['jjdm'].apply(lambda x: self.compare_fund_name_dic[x])
            compare_industry = compare_industry.pivot(index='FUND_NAME', columns='MARK', values='INDUSTRY_DISP')
            compare_industry = compare_industry.loc[[self.compare_fund_name_dic[code] for code in [self.target_fund_code] + self.compare_fund_code_list]]
            compare_industry = compare_industry.T.reset_index().T.reset_index()
            compare_industry.iloc[0, 0] = ''
        return fund_industry_disp, fund_industry_text, compare_industry

    def get_fund_industry_change(self):
        """
        生成基金行业变动数据
        """
        fund_holding = HBDB().read_fund_holding_given_codes([self.target_fund_code])
        fund_holding = fund_holding.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'zqdm': 'TICKER_SYMBOL', 'zqmc': 'SEC_SHORT_NAME', 'ccsz': 'HOLDING_MARKET_VALUE', 'ccsl': 'HOLDING_AMOUNT', 'zjbl': 'MV_IN_NA'})
        fund_holding['REPORT_DATE'] = fund_holding['REPORT_DATE'].astype(str)
        stock_industry = HBDB().read_stock_industry()
        stock_industry = stock_industry.rename(columns={'zqdm': 'TICKER_SYMBOL', 'flmc': 'INDUSTRY_NAME', 'fldm': 'INDUSTRY_ID', 'fljb': 'INDUSTRY_TYPE', 'hyhfbz': 'INDUSTRY_VERSION', 'qsrq': 'BEGIN_DATE', 'jsrq': 'END_DATE', 'sfyx': 'IS_NEW'})
        stock_industry = stock_industry[stock_industry['IS_NEW'] == 1]
        stock_industry = stock_industry[stock_industry['INDUSTRY_VERSION'] == 2]
        stock_industry = stock_industry[stock_industry['INDUSTRY_TYPE'] == '1']
        stock_industry = stock_industry.sort_values(['TICKER_SYMBOL', 'credt_etl']).drop_duplicates('TICKER_SYMBOL', keep='last')
        fund_holding = fund_holding.merge(stock_industry[['TICKER_SYMBOL', 'INDUSTRY_NAME']], on=['TICKER_SYMBOL'], how='left')
        fund_holding = fund_holding[['REPORT_DATE', 'INDUSTRY_NAME', 'MV_IN_NA']].groupby(['REPORT_DATE', 'INDUSTRY_NAME']).sum().reset_index()
        fund_industry = fund_holding.pivot(index='REPORT_DATE', columns='INDUSTRY_NAME', values='MV_IN_NA').fillna(0.0)
        fund_industry = fund_industry.loc[fund_industry.index.str.slice(4, 6).isin(['06', '12'])]
        fund_industry_change = fund_industry.diff().dropna()
        fund_industry_change = fund_industry_change.unstack().reset_index()
        fund_industry_change.columns = ['INDUSTRY_NAME', 'REPORT_DATE', 'MV_IN_NA_DIFF']
        # fund_industry_change = HBDB().read_fund_industry_given_codes([self.target_fund_code])
        # fund_industry_change = fund_industry_change.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'zclb': 'IS_ZC', 'hyhfbz': 'INDUSTRY_VERSION', 'fldm': 'INDUSTRY_ID', 'flmc': 'INDUSTRY_NAME', 'zzjbl': 'MV_IN_NA', 'hyzjzbjbd': 'MV_IN_NA_DIFF', 'hyzjzbltlpj': 'HOMO_AVG'})
        # fund_industry_change = fund_industry_change[fund_industry_change['INDUSTRY_VERSION'] == '2']
        # fund_industry_change = fund_industry_change[fund_industry_change['IS_ZC'] == '2']
        # fund_industry_change = fund_industry_change[['REPORT_DATE', 'INDUSTRY_NAME', 'MV_IN_NA_DIFF']]
        fund_industry_change = fund_industry_change.sort_values(['REPORT_DATE', 'MV_IN_NA_DIFF'], ascending=[True, False])
        fund_industry_change = fund_industry_change[fund_industry_change['REPORT_DATE'].isin(sorted(fund_industry_change['REPORT_DATE'].unique().tolist())[-6:])]
        fund_industry_up = fund_industry_change.groupby('REPORT_DATE').head(5).sort_values(['REPORT_DATE', 'MV_IN_NA_DIFF'], ascending=[False, False])
        fund_industry_down = fund_industry_change.groupby('REPORT_DATE').tail(5).sort_values(['REPORT_DATE', 'MV_IN_NA_DIFF'], ascending=[False, True])
        fund_industry_up['MV_IN_NA_DIFF'] = fund_industry_up['MV_IN_NA_DIFF'].apply(lambda x: '{0}%'.format(round(x, 2)))
        fund_industry_down['MV_IN_NA_DIFF'] = fund_industry_down['MV_IN_NA_DIFF'].apply(lambda x: '{0}%'.format(round(x, 2)))
        fund_industry_up_list, fund_industry_down_list = [], []
        date_list = fund_industry_up['REPORT_DATE'].unique().tolist()
        for date in date_list:
            fund_industry_up_date = fund_industry_up[fund_industry_up['REPORT_DATE'] == date]
            fund_industry_up_date.index = range(1, 6)
            fund_industry_up_list.append(fund_industry_up_date[['INDUSTRY_NAME', 'MV_IN_NA_DIFF']])
            fund_industry_down_date = fund_industry_down[fund_industry_down['REPORT_DATE'] == date]
            fund_industry_down_date.index = range(1, 6)
            fund_industry_down_list.append(fund_industry_down_date[['INDUSTRY_NAME', 'MV_IN_NA_DIFF']])
        fund_industry_up = pd.concat(fund_industry_up_list, axis=1)
        fund_industry_down = pd.concat(fund_industry_down_list, axis=1)
        fund_industry_up.columns = [sorted(date_list * 2)[len(sorted(date_list * 2)) - 1 - i] for i, date in enumerate(sorted(date_list * 2))]
        fund_industry_up = fund_industry_up.T.reset_index().T.reset_index().replace({'index': '时间', 1: '第一名', 2: '第二名', 3: '第三名', 4: '第四名', 5: '第五名'})
        fund_industry_down.columns = [sorted(date_list * 2)[len(sorted(date_list * 2)) - 1 - i] for i, date in enumerate(sorted(date_list * 2))]
        fund_industry_down = fund_industry_down.T.reset_index().T.reset_index().replace({'index': '时间', 1: '第一名', 2: '第二名', 3: '第三名', 4: '第四名', 5: '第五名'})

        fund_industry_up_text = '增持前五大行业（净值占比）如下：'
        fund_industry_down_text = '减持前五大行业（净值占比）如下：'
        return fund_industry_up, fund_industry_up_text, fund_industry_down, fund_industry_down_text

    def get_fund_cr(self):
        """
        生成基金持仓集中度数据
        """
        fund_cr = HBDB().read_fund_cr_given_code(self.target_fund_code)
        fund_cr = fund_cr.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'qsdzb': 'CR10', 'qsdzbtlpj': 'CR10_MEAN', 'qwdzb': 'CR5', 'qwdzbtlpj': 'CR5_MEAN'})
        fund_cr = fund_cr.sort_values('REPORT_DATE').iloc[-12:]
        fund_cr = fund_cr[['REPORT_DATE', 'CR5', 'CR5_MEAN', 'CR10', 'CR10_MEAN']]
        fund_cr.columns = ['报告日期', '前五大股票占比（%）', '前五大股票占比同类平均（%）', '前十大股票占比（%）', '前十大股票占比同类平均（%）']

        cr5 = fund_cr[['报告日期', '前五大股票占比（%）']].rename(columns={'前五大股票占比（%）': '占净资产比（%）'})
        cr5['前五大占比'] = '前五大股票占比'
        cr5_mean = fund_cr[['报告日期', '前五大股票占比同类平均（%）']].rename(columns={'前五大股票占比同类平均（%）': '占净资产比（%）'})
        cr5_mean['前五大占比'] = '前五大股票占比同类平均'
        cr5_mean = pd.concat([cr5, cr5_mean]).reset_index().drop('index', axis=1)
        cr10 = fund_cr[['报告日期', '前十大股票占比（%）']].rename(columns={'前十大股票占比（%）': '占净资产比（%）'})
        cr10['前十大占比'] = '前十大股票占比'
        cr10_mean = fund_cr[['报告日期', '前十大股票占比同类平均（%）']].rename(columns={'前十大股票占比同类平均（%）': '占净资产比（%）'})
        cr10_mean['前十大占比'] = '前十大股票占比同类平均'
        cr10_mean = pd.concat([cr10, cr10_mean]).reset_index().drop('index', axis=1)

        cr5_mean['报告日期'] = cr5_mean['报告日期'].astype(str)
        change_dates = [date for date in self.change_dates if date >= min(cr5_mean['报告日期']) and date <= max(cr5_mean['报告日期'])]
        change_dates_loc = get_change_dates_loc(change_dates, sorted(cr5_mean['报告日期'].unique().tolist()))
        fig, ax = plt.subplots(figsize=(6, 3))
        sns.barplot(ax=ax, x='报告日期', y='占净资产比（%）', data=cr5_mean, hue='前五大占比', palette=['#C94649', '#8588B7'])
        ax.vlines(x=change_dates_loc, ymin=0.0, ymax=max(cr5_mean['占净资产比（%）']), linestyles='dashed', color='#959595')
        ax.legend(loc=2)
        ax.set_xticklabels(labels=fund_cr['报告日期'], rotation=90)
        plt.tight_layout()
        plt.savefig('{0}{1}_cr5.png'.format(self.file_path, self.target_fund_code))

        fig, ax = plt.subplots(figsize=(6, 3))
        sns.barplot(ax=ax, x='报告日期', y='占净资产比（%）', data=cr10_mean, hue='前十大占比', palette=['#C94649', '#8588B7'])
        ax.vlines(x=change_dates_loc, ymin=0.0, ymax=max(cr10_mean['占净资产比（%）']), linestyles='dashed', color='#959595')
        ax.legend(loc=2)
        ax.set_xticklabels(labels=fund_cr['报告日期'], rotation=90)
        plt.tight_layout()
        plt.savefig('{0}{1}_cr10.png'.format(self.file_path, self.target_fund_code))

        fund_cr5_mean = round(fund_cr['前五大股票占比（%）'].mean(), 2)
        fund_cr5_mean_mean = round(fund_cr['前五大股票占比同类平均（%）'].mean(), 2)
        fund_cr10_mean = round(fund_cr['前十大股票占比（%）'].mean(), 2)
        fund_cr10_mean_mean = round(fund_cr['前十大股票占比同类平均（%）'].mean(), 2)
        cr5_text = '低' if fund_cr5_mean < fund_cr5_mean_mean else '高'
        cr10_text = '低' if fund_cr10_mean < fund_cr10_mean_mean else '高'
        text = '，持股集中度低'if cr5_text == '低' and cr10_text == '低' else ''

        fund_cr_text = '该产品持股集中度情况如下，近三年前五大权重保持在{0}%左右，整体{1}于同类平均水平，前十大权重保持在{2}%左右，整体{3}于同类平均水平{4}。'\
            .format(fund_cr5_mean, cr5_text, fund_cr10_mean, cr10_text, text)
        return fund_cr_text

    def get_fund_finance(self):
        """
        生成基金持仓财务数据
        """
        fund_finance = HBDB().read_fund_valuation_given_codes([self.target_fund_code])
        fund_finance = fund_finance.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'zclb': 'IS_ZC', 'pe': 'PE', 'pb': 'PB', 'roe': 'ROE', 'dividend': 'DIVIDEND'})
        fund_finance['REPORT_DATE'] = fund_finance['REPORT_DATE'].astype(int)
        fund_finance = fund_finance[fund_finance['IS_ZC'] == 1]
        fund_finance = fund_finance.sort_values('REPORT_DATE').iloc[-12:].reset_index().drop('index', axis=1)
        fund_finance = fund_finance[['REPORT_DATE', 'PE', 'PB', 'ROE', 'DIVIDEND']]
        fund_finance = fund_finance[(fund_finance['PE'] != 99999.0) & (fund_finance['PB'] != 99999.0) & (fund_finance['ROE'] != 99999.0) & (fund_finance['DIVIDEND'] != 99999.0)]
        windA_finance = HBDB().read_index_valuation_given_date_and_indexs((datetime.today() - timedelta(365 * 4)).strftime('%Y%m%d'), ['881001'])
        windA_finance = windA_finance.rename(columns={'zqdm': 'INDEX_SYMBOL', 'zqmc': 'INDEX_NAME', 'jyrq': 'REPORT_DATE', 'pe': 'PE_windA', 'pb': 'PB_windA', 'roe': 'ROE_windA', 'gxl': 'DIVIDEND_windA'})
        windA_finance['REPORT_DATE'] = windA_finance['REPORT_DATE'].astype(int)
        windA_finance = windA_finance[['REPORT_DATE', 'PE_windA', 'PB_windA', 'ROE_windA', 'DIVIDEND_windA']]
        fund_finance = pd.merge_asof(fund_finance, windA_finance, on='REPORT_DATE')
        fund_finance['REPORT_DATE'] = fund_finance['REPORT_DATE'].astype(str)
        fund_finance.columns = ['报告日期', 'PE', 'PB', 'ROE（%）', '股息率（%）', '万得全A_PE', '万得全A_PB', '万得全A_ROE（%）', '万得全A_股息率（%）']

        pe_mean = round(fund_finance['PE'].mean(), 2)
        pb_mean = round(fund_finance['PB'].mean(), 2)
        roe_mean = round(fund_finance['ROE（%）'].mean(), 2)
        dividend_mean = round(fund_finance['股息率（%）'].mean(), 2)

        fund_finance['报告日期'] = fund_finance['报告日期'].astype(str)
        change_dates = [date for date in self.change_dates if date >= min(fund_finance['报告日期']) and date <= max(fund_finance['报告日期'])]
        change_dates_loc = get_change_dates_loc(change_dates, sorted(fund_finance['报告日期'].unique().tolist()))
        fig, ax = plt.subplots(2, 2, figsize=(6, 6))
        pic_list = [['PE', 'PB'], ['ROE（%）', '股息率（%）']]
        for i in range(2):
            for j in range(2):
                axij = sns.lineplot(ax=ax[i][j], x='报告日期', y=pic_list[i][j], data=fund_finance, color='#F04950', label=self.target_fund_code)
                axij = sns.lineplot(ax=ax[i][j], x='报告日期', y='万得全A_' + pic_list[i][j], data=fund_finance, color='#6268A2', label='万得全A')
                axij.vlines(x=change_dates_loc, ymin=0.0, ymax=max(max(fund_finance[pic_list[i][j]]), max(fund_finance['万得全A_' + pic_list[i][j]])), linestyles='dashed', color='#959595')
                axij.set_title('{0}走势'.format(pic_list[i][j].replace('（%）', '')))
                axij.set_xticklabels(labels=fund_finance['报告日期'], rotation=90)
                axij.legend(loc=2)
        plt.tight_layout()
        plt.savefig('{0}{1}_finance.png'.format(self.file_path, self.target_fund_code))

        fund_finance_text = '该产品PE、PB、ROE、股息率走势如下，在统计区间内（近三年），PE维持在{0}倍左右，PB维持在{1}倍左右，ROE维持在{2}%左右，股息率维持在{3}%左右。'\
            .format(pe_mean, pb_mean, roe_mean, dividend_mean)

        compare_finance = pd.DataFrame()
        if len(self.compare_fund_code_list) > 0:
            compare_finance_list = []
            for compare_fund_code in [self.target_fund_code] + self.compare_fund_code_list:
                compare_finance_code = HBDB().read_fund_valuation_given_codes([compare_fund_code])
                compare_finance_code = compare_finance_code.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'zclb': 'IS_ZC', 'pe': 'PE', 'pb': 'PB', 'roe': 'ROE', 'dividend': 'DIVIDEND'})
                compare_finance_code['REPORT_DATE'] = compare_finance_code['REPORT_DATE'].astype(int)
                compare_finance_code = compare_finance_code[compare_finance_code['IS_ZC'] == 1]
                compare_finance_code = compare_finance_code.sort_values('REPORT_DATE').iloc[-12:].reset_index().drop('index', axis=1)
                compare_finance_code = compare_finance_code[(compare_finance_code['PE'] != 99999.0) & (compare_finance_code['PB'] != 99999.0) & (compare_finance_code['ROE'] != 99999.0) & (compare_finance_code['DIVIDEND'] != 99999.0)]
                compare_finance_code = pd.DataFrame(compare_finance_code[['PE', 'PB', 'ROE', 'DIVIDEND']].mean()).T
                compare_finance_code['FUND_CODE'] = compare_fund_code
                compare_finance_list.append(compare_finance_code)
            compare_finance = pd.concat(compare_finance_list)
            compare_finance['FUND_NAME'] = compare_finance['FUND_CODE'].apply(lambda x: self.compare_fund_name_dic[x])
            compare_finance = compare_finance[['FUND_NAME', 'PE', 'PB', 'ROE', 'DIVIDEND']].set_index('FUND_NAME')
            compare_finance['PE'] = compare_finance['PE'].apply(lambda x: '{0}'.format(round(x, 2)))
            compare_finance['PB'] = compare_finance['PB'].apply(lambda x: '{0}'.format(round(x, 2)))
            compare_finance['ROE'] = compare_finance['ROE'].apply(lambda x: '{0}%'.format(round(x, 2)))
            compare_finance['DIVIDEND'] = compare_finance['DIVIDEND'].apply(lambda x: '{0}%'.format(round(x, 2)))
            compare_finance.columns = ['PE', 'PB', 'ROE', '股息率']
            compare_finance = compare_finance.loc[[self.compare_fund_name_dic[code] for code in [self.target_fund_code] + self.compare_fund_code_list]]
            compare_finance = compare_finance.T.reset_index().T.reset_index()
            compare_finance.iloc[0, 0] = ''
        return fund_finance_text, compare_finance

    def get_fund_stock_change(self):
        """
        获取基金个股变动数据
        """
        fund_holding = HBDB().read_fund_holding_given_codes([self.target_fund_code])
        fund_holding = fund_holding.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'zqdm': 'TICKER_SYMBOL', 'zqmc': 'SEC_SHORT_NAME', 'ccsz': 'HOLDING_MARKET_VALUE', 'ccsl': 'HOLDING_AMOUNT', 'zjbl': 'MV_IN_NA'})
        fund_holding_diff = HBDB().read_fund_holding_diff_given_codes([self.target_fund_code])
        fund_holding_diff = fund_holding_diff.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'zqdm': 'TICKER_SYMBOL', 'zqmc': 'SEC_SHORT_NAME', 'zclb': 'IS_ZC', 'zgblbd': 'MV_IN_NA_DIFF', 'sfsqzcg': 'IS_LAST_ZC'})
        stock_industry = HBDB().read_stock_industry()
        stock_industry = stock_industry.rename(columns={'zqdm': 'TICKER_SYMBOL', 'flmc': 'INDUSTRY_NAME', 'fldm': 'INDUSTRY_ID', 'fljb': 'INDUSTRY_TYPE', 'hyhfbz': 'INDUSTRY_VERSION', 'qsrq': 'BEGIN_DATE', 'jsrq': 'END_DATE', 'sfyx': 'IS_NEW'})
        stock_industry = stock_industry[stock_industry['IS_NEW'] == 1]
        stock_industry = stock_industry[stock_industry['INDUSTRY_VERSION'] == 2]
        stock_industry = stock_industry[stock_industry['INDUSTRY_TYPE'] == '1']
        stock_industry = stock_industry.sort_values(['TICKER_SYMBOL', 'credt_etl']).drop_duplicates('TICKER_SYMBOL', keep='last')
        fund_holding = fund_holding.sort_values(['REPORT_DATE', 'MV_IN_NA'], ascending=[True, False]).reset_index().drop('index', axis=1)
        fund_holding = fund_holding.groupby('REPORT_DATE').head(10)
        fund_holding_diff = fund_holding_diff[fund_holding_diff['IS_ZC'] == '1']
        fund_holding = fund_holding.merge(fund_holding_diff[['REPORT_DATE', 'TICKER_SYMBOL', 'MV_IN_NA_DIFF']], on=['REPORT_DATE', 'TICKER_SYMBOL'], how='left')
        fund_holding = fund_holding.merge(stock_industry[['TICKER_SYMBOL', 'INDUSTRY_NAME']], on=['TICKER_SYMBOL'], how='left')

        fund_holding_latest = fund_holding[fund_holding['REPORT_DATE'] == fund_holding['REPORT_DATE'].max()]
        fund_holding_latest = fund_holding_latest[['SEC_SHORT_NAME', 'INDUSTRY_NAME', 'MV_IN_NA_DIFF', 'HOLDING_AMOUNT', 'HOLDING_MARKET_VALUE', 'MV_IN_NA']]
        fund_holding_latest[['MV_IN_NA_DIFF', 'HOLDING_AMOUNT', 'HOLDING_MARKET_VALUE', 'MV_IN_NA']] = round(fund_holding_latest[['MV_IN_NA_DIFF', 'HOLDING_AMOUNT', 'HOLDING_MARKET_VALUE', 'MV_IN_NA']], 2)
        fund_holding_latest['MV_IN_NA_DIFF'] = fund_holding_latest['MV_IN_NA_DIFF'].apply(lambda x: '{0}%'.format(x))
        fund_holding_latest['MV_IN_NA'] = fund_holding_latest['MV_IN_NA'].apply(lambda x: '{0}%'.format(x))
        fund_holding_latest.columns = ['重仓股名称', '所属行业', '占股票市值比较上期变动', '持仓数量（股）', '持仓市值（元）', '占净值比例']
        fund_holding_latest = fund_holding_latest.T.reset_index().T.reset_index().drop('index', axis=1)

        need_dates = sorted(fund_holding['REPORT_DATE'].unique().tolist())[-6:]
        disp_dates = [need_dates[len(need_dates) - 1 - i] for i, date in enumerate(need_dates)]
        fund_holding_disp = fund_holding[fund_holding['REPORT_DATE'].isin(need_dates)]
        fund_holding_disp = fund_holding_disp.pivot(index='REPORT_DATE', columns='SEC_SHORT_NAME', values='MV_IN_NA').T
        fund_holding_disp['平均占净值比例'] = fund_holding_disp.mean(axis=1)
        fund_holding_disp = fund_holding_disp.sort_values(['平均占净值比例'], ascending=False)
        fund_holding_disp_name = fund_holding_disp.index[0]
        fund_holding_disp_data = round(fund_holding_disp['平均占净值比例'].iloc[0], 2)
        fund_holding_disp = fund_holding_disp[disp_dates + ['平均占净值比例']]
        for col in disp_dates + ['平均占净值比例']:
            fund_holding_disp[col] = fund_holding_disp[col].apply(lambda x: '{0}%'.format(round(x, 2)) if not np.isnan(x) else '-')
        fund_holding_disp = fund_holding_disp.T.reset_index().T.reset_index()
        fund_holding_disp.iloc[0, 0] = '股票名称'

        fund_holding_dist = fund_holding.copy(deep=True)
        fund_holding_dist = fund_holding_dist[fund_holding_dist['REPORT_DATE'].isin(sorted(fund_holding['REPORT_DATE'].unique().tolist())[-12:])]
        fund_holding_dist = fund_holding_dist.rename(columns={'MV_IN_NA': '占净值比例'})
        fig, ax = plt.subplots(figsize=(6, 3))
        sns.distplot(fund_holding_dist['占净值比例'], ax=ax, bins=21, hist=True, kde=True, color='#F04950')
        plt.gca().xaxis.set_major_formatter(FuncFormatter(to_percent))
        plt.ylabel('概率分布')
        plt.tight_layout()
        plt.savefig('{0}{1}_dist.png'.format(self.file_path, self.target_fund_code))

        dic = {}
        for i in range(40):
            dic[i] = len(fund_holding_dist[(fund_holding_dist['占净值比例'] >= i) & (fund_holding_dist['占净值比例'] < i + 1)])
        disp = pd.DataFrame([dic]).T.sort_values(0, ascending=False)
        period = '[{0}%, {1}%]'.format(disp.index[0], disp.index[0] + 1)

        fund_holding_latest_text = '该产品最新一期前十大重仓情况如下：'
        fund_holding_disp_text = '该产品近三年前十大重仓分布如下，从概率密度分布图可以看出，该产品持仓权重分布最多的区间是{0}，近六期平均占净值比例最大的股票为{1}，占比为{2}%。'.format(period, fund_holding_disp_name, fund_holding_disp_data)
        return fund_holding_latest, fund_holding_latest_text, fund_holding_disp, fund_holding_disp_text

    def get_fund_stock_compare(self):
        """
        获取基金同细分行业个股比较数据
        """
        fund_holding = HBDB().read_fund_holding_given_codes([self.target_fund_code])
        fund_holding = fund_holding.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'zqdm': 'TICKER_SYMBOL', 'zqmc': 'SEC_SHORT_NAME', 'ccsz': 'HOLDING_MARKET_VALUE', 'ccsl': 'HOLDING_AMOUNT', 'zjbl': 'MV_IN_NA'})
        stock_industry = HBDB().read_stock_industry()
        stock_industry = stock_industry.rename(columns={'zqdm': 'TICKER_SYMBOL', 'flmc': 'INDUSTRY_NAME', 'fldm': 'INDUSTRY_ID', 'fljb': 'INDUSTRY_TYPE', 'hyhfbz': 'INDUSTRY_VERSION', 'qsrq': 'BEGIN_DATE', 'jsrq': 'END_DATE', 'sfyx': 'IS_NEW'})
        stock_industry = stock_industry[stock_industry['IS_NEW'] == 1]
        stock_industry = stock_industry[stock_industry['INDUSTRY_VERSION'] == 2]
        stock_industry = stock_industry[stock_industry['INDUSTRY_TYPE'] == '2']
        stock_industry = stock_industry.sort_values(['TICKER_SYMBOL', 'credt_etl']).drop_duplicates('TICKER_SYMBOL', keep='last')
        industry_type = '二级' if stock_industry['INDUSTRY_TYPE'].iloc[0] == '2' else '三级' if stock_industry['INDUSTRY_TYPE'].iloc[0] == '3' else '一级'
        fund_holding = fund_holding.merge(stock_industry[['TICKER_SYMBOL', 'INDUSTRY_NAME']], on=['TICKER_SYMBOL'], how='left')
        fund_holding = fund_holding[fund_holding['REPORT_DATE'] == fund_holding['REPORT_DATE'].max()].sort_values('MV_IN_NA', ascending=False).reset_index().drop('index', axis=1)
        fund_holding_top5 = fund_holding.iloc[:5]
        fund_holding_top5.index = range(1, 6)
        fund_holding_top5 = fund_holding_top5.reset_index()
        fund_holding_top5['SEC_SHORT_NAME_TOP'] = fund_holding_top5.apply(lambda x: '{0}（TOP{1}）'.format(x['SEC_SHORT_NAME'], x['index']), axis=1)
        fund_holding_indu_list = []
        for indu in fund_holding_top5['INDUSTRY_NAME'].tolist():
            fund_holding_indu = fund_holding[fund_holding['INDUSTRY_NAME'] == indu].sort_values('MV_IN_NA', ascending=False)
            fund_holding_indu = fund_holding_indu[['INDUSTRY_NAME', 'SEC_SHORT_NAME', 'HOLDING_AMOUNT', 'HOLDING_MARKET_VALUE', 'MV_IN_NA']]
            fund_holding_indu_list.append(fund_holding_indu)
        fund_holding_indu = pd.concat(fund_holding_indu_list)
        fund_holding_top5 = fund_holding_top5[['SEC_SHORT_NAME_TOP', 'INDUSTRY_NAME']].merge(fund_holding_indu, on=['INDUSTRY_NAME'], how='left')
        fund_holding_top5[['HOLDING_AMOUNT', 'HOLDING_MARKET_VALUE', 'MV_IN_NA']] = round(fund_holding_top5[['HOLDING_AMOUNT', 'HOLDING_MARKET_VALUE', 'MV_IN_NA']], 2)
        fund_holding_top5['MV_IN_NA'] = fund_holding_top5['MV_IN_NA'].apply(lambda x: '{0}%'.format(x))
        fund_holding_top5.columns = ['前五大持仓', '所属细分行业', '同细分行业个股', '持仓数量（股）', '持仓市值（元）', '占净值比例']
        fund_holding_top5 = fund_holding_top5.T.reset_index().T.reset_index().drop('index', axis=1)
        fund_holding_top5 = fund_holding_top5.dropna(axis=0, how='any').reset_index().drop('index', axis=1)

        fund_holding_top5_text = '该基金中与前五大持仓同细分行业（申万{0}行业）的个股如下：'.format(industry_type)
        return fund_holding_top5, fund_holding_top5_text

    def get_fund_turnover(self):
        """
        生成基金换手数据
        """
        fund_turnover = HBDB().read_fund_turnover_given_code(self.target_fund_code)
        fund_turnover = fund_turnover.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'tjqj': 'TYPE', 'hsl': 'TURNOVER'})
        fund_turnover = fund_turnover[fund_turnover['TYPE'].isin(['1', '3'])]
        fund_turnover = fund_turnover.sort_values('REPORT_DATE').iloc[-12:]
        fund_turnover = fund_turnover[['REPORT_DATE', 'TURNOVER']]
        fund_turnover.columns = ['报告日期', '换手率（%）']

        fund_turnover['报告日期'] = fund_turnover['报告日期'].astype(str)
        change_dates = [date for date in self.change_dates if date >= min(fund_turnover['报告日期']) and date <= max(fund_turnover['报告日期'])]
        change_dates_loc = get_change_dates_loc(change_dates, sorted(fund_turnover['报告日期'].unique().tolist()))
        fig, ax = plt.subplots(figsize=(6, 3))
        sns.barplot(ax=ax, x='报告日期', y='换手率（%）', data=fund_turnover, palette=['#C94649'])
        ax.vlines(x=change_dates_loc, ymin=0.0, ymax=max(fund_turnover['换手率（%）']), linestyles='dashed', color='#959595')
        ax.set_xticklabels(labels=fund_turnover['报告日期'], rotation=90)
        plt.tight_layout()
        plt.savefig('{0}{1}_turnover.png'.format(self.file_path, self.target_fund_code))

        fund_turnover_mean = round(fund_turnover.iloc[-6:]['换手率（%）'].mean() / 100.0, 2)
        latest_turnover = round(fund_turnover.iloc[-1]['换手率（%）'] / 100.0, 2)
        fund_turnover_text = '该产品换手率情况如下，近三年单边年化换手倍数整体稳定在{0}倍左右，最新报告期披露的单边年化换手率为{1}倍。'.format(fund_turnover_mean, latest_turnover)

        compare_turnover = pd.DataFrame()
        if len(self.compare_fund_code_list) > 0:
            compare_turnover_list = []
            for compare_fund_code in [self.target_fund_code] + self.compare_fund_code_list:
                compare_turnover_code = HBDB().read_fund_turnover_given_code(compare_fund_code)
                compare_turnover_list.append(compare_turnover_code)
            compare_turnover = pd.concat(compare_turnover_list)
            compare_turnover = compare_turnover.rename(columns={'jjdm': 'FUND_CODE', 'jsrq': 'REPORT_DATE', 'tjqj': 'TYPE', 'hsl': 'TURNOVER'})
            compare_turnover = compare_turnover[compare_turnover['TYPE'].isin(['1', '3'])]
            compare_turnover = compare_turnover[['FUND_CODE', 'REPORT_DATE', 'TURNOVER']]
            compare_turnover['TURNOVER'] = compare_turnover['TURNOVER'].apply(lambda x: '{0}%'.format(round(x, 2)))
            compare_turnover = compare_turnover.pivot(index='REPORT_DATE', columns='FUND_CODE', values='TURNOVER')
            compare_turnover = compare_turnover.dropna(subset=[self.target_fund_code], axis=0).sort_index().iloc[-12:]
            compare_turnover = compare_turnover[[self.target_fund_code] + self.compare_fund_code_list]
            compare_turnover.columns = [self.compare_fund_name_dic[col] for col in compare_turnover.columns]
            compare_turnover = compare_turnover.replace(np.nan, '-')
            compare_turnover = compare_turnover.T.reset_index().T.reset_index()
            compare_turnover.iloc[0, 0] = '换手率'
        return fund_turnover_text, compare_turnover

    def get_fund_performace(self):
        establish_date = str(int(self.target_fund['BEGIN_DATE'].values[0]))

        fund_nav = HBDB().read_fund_cumret_given_code(self.target_fund_code, establish_date, datetime.today().strftime('%Y%m%d'))
        fund_nav = fund_nav.rename(columns={'JJDM': 'FUND_CODE', 'JZRQ': 'TRADE_DATE', 'HBCL': 'CUM_RET'})
        fund_nav['TRADE_DATE'] = fund_nav['TRADE_DATE'].astype(str)
        fund_nav = fund_nav.sort_values('TRADE_DATE')
        fund_nav['FUND_NAME'] = self.target_fund_name
        fund_nav['CUM_RET'] = 0.01 * fund_nav['CUM_RET']
        fund_cumret = fund_nav.pivot(index='TRADE_DATE', columns='FUND_NAME', values='CUM_RET')
        fund_nav['NAV'] = fund_nav['CUM_RET'] + 1
        fund_nav['NAV'] = fund_nav['NAV'] / fund_nav['NAV'].iloc[0]
        fund_nav['IDX'] = range(len(fund_nav))
        fund_nav['DRAWDOWN'] = fund_nav['IDX'].apply(lambda x: fund_nav['NAV'].iloc[x] / max(fund_nav['NAV'].iloc[:x]) - 1.0 if x != 0 else 0.0)
        fund_d = fund_nav.pivot(index='TRADE_DATE', columns='FUND_NAME', values='DRAWDOWN')

        index = '000300'
        index_name = '沪深300'
        index_daily_k = HBDB().read_index_daily_k_given_date_and_indexs(establish_date, [index])
        index_daily_k = index_daily_k[['zqmc', 'jyrq', 'spjg']].rename(columns={'zqmc': 'INDEX_NAME', 'jyrq': 'TRADE_DATE', 'spjg': 'CLOSE_INDEX'})
        index_daily_k['TRADE_DATE'] = index_daily_k['TRADE_DATE'].astype(str)
        index_daily_k = index_daily_k.sort_values('TRADE_DATE')
        index_daily_k['CUM_RET'] = index_daily_k['CLOSE_INDEX'] / index_daily_k['CLOSE_INDEX'].iloc[0] - 1.0
        index_cumret = index_daily_k.pivot(index='TRADE_DATE', columns='INDEX_NAME', values='CUM_RET')
        index_daily_k['NAV'] = index_daily_k['CLOSE_INDEX'] / index_daily_k['CLOSE_INDEX'].iloc[0]
        index_daily_k['IDX'] = range(len(index_daily_k))
        index_daily_k['DRAWDOWN'] = index_daily_k['IDX'].apply(lambda x: index_daily_k['CLOSE_INDEX'].iloc[x] / max(index_daily_k['CLOSE_INDEX'].iloc[:x]) - 1.0 if x != 0 else 0.0)
        index_d = index_daily_k.pivot(index='TRADE_DATE', columns='INDEX_NAME', values='DRAWDOWN')

        fund_index_cumret = fund_cumret.merge(index_cumret, left_index=True, right_index=True, how='left').reset_index()
        fund_index_cumret['TRADE_DATE'] = fund_index_cumret['TRADE_DATE'].astype(str)
        change_dates = [date for date in self.change_dates if date >= min(fund_index_cumret['TRADE_DATE']) and date <= max(fund_index_cumret['TRADE_DATE'])]
        change_dates_loc = get_change_dates_loc(change_dates, sorted(fund_index_cumret['TRADE_DATE'].unique().tolist()))
        fig, ax = plt.subplots(figsize=(6, 3))
        sns.lineplot(x='TRADE_DATE', y=self.target_fund_name, data=fund_index_cumret, color='#F04950', label=self.target_fund_name)
        sns.lineplot(x='TRADE_DATE', y=index_name, data=fund_index_cumret, color='#6268A2', label=index_name)
        ax.vlines(x=change_dates_loc, ymin=0.0, ymax=max(max(fund_index_cumret[self.target_fund_name]), max(fund_index_cumret[index_name])), linestyles='dashed', color='#959595')
        ax.xaxis.set_major_locator(ticker.MultipleLocator(90))
        ax.yaxis.set_major_formatter(FuncFormatter(to_100percent))
        plt.xlabel('')
        plt.ylabel('累计收益率')
        plt.xticks(rotation=90)
        plt.title('收益走势图')
        plt.legend(loc=2)
        plt.tight_layout()
        plt.savefig('{0}{1}_return.png'.format(self.file_path, self.target_fund_code))

        fund_index_d = fund_d.merge(index_d, left_index=True, right_index=True, how='left').reset_index()
        fig, ax = plt.subplots(figsize=(6, 3))
        sns.lineplot(x='TRADE_DATE', y=self.target_fund_name, data=fund_index_d, color='#F04950', label=self.target_fund_name)
        sns.lineplot(x='TRADE_DATE', y=index_name, data=fund_index_d, color='#6268A2', label=index_name)
        ax.vlines(x=change_dates_loc, ymin=min(min(fund_index_d[self.target_fund_name]), min(fund_index_d[index_name])), ymax=0.0, linestyles='dashed', color='#959595')
        ax.xaxis.set_major_locator(ticker.MultipleLocator(90))
        ax.yaxis.set_major_formatter(FuncFormatter(to_100percent))
        plt.xlabel('')
        plt.ylabel('回撤')
        plt.xticks(rotation=90)
        plt.title('回撤走势图')
        plt.legend(loc=2)
        plt.tight_layout()
        plt.savefig('{0}{1}_drawdown.png'.format(self.file_path, self.target_fund_code))

        fund_return_period = HBDB().read_fund_return_given_code_and_date(self.target_fund_code, (datetime.today() - timedelta(30)).strftime('%Y%m%d'))
        fund_return_period = fund_return_period.rename(columns={'jjdm': 'FUND_CODE', 'zblb': 'RETURN_TYPE', 'rqnp': 'START_DATE', 'jzrq': 'END_DATE', 'zbnp': 'RETURN', 'zbnhnp': 'ANNUAL_RETURN'})
        fund_return_period['END_DATE'] = fund_return_period['END_DATE'].astype(str)
        fund_return_period = fund_return_period[fund_return_period['END_DATE'] == max(fund_return_period['END_DATE'])]
        fund_return_period['RETURN_TYPE'] = fund_return_period['RETURN_TYPE'].astype(int)
        fund_return_period['RETURN_TYPE'] = fund_return_period['RETURN_TYPE'].replace({2998: '今年以来', 2101: '近1月', 2103: '近3月', 2106: '近6月', 2201: '近1年', 2202: '近2年', 2203: '近3年'})
        fund_return_period['RETURN'] = fund_return_period['RETURN'].apply(lambda x: '{0}%'.format(round(x, 2)))
        fund_return_period = fund_return_period[fund_return_period['RETURN_TYPE'].isin(['今年以来', '近1月', '近3月', '近6月', '近1年', '近2年', '近3年'])]
        fund_return_period = fund_return_period.pivot(index='FUND_CODE', columns='RETURN_TYPE', values='RETURN')
        fund_return_period = fund_return_period[['今年以来', '近1月', '近3月', '近6月', '近1年', '近2年', '近3年']]
        fund_samekind_return_period = HBDB().read_fund_samekind_return_given_code_and_date(self.target_fund_code, (datetime.today() - timedelta(30)).strftime('%Y%m%d'))
        fund_samekind_return_period = fund_samekind_return_period.rename(columns={'jjdm': 'FUND_CODE', 'zblb': 'RETURN_TYPE', 'jzrq': 'END_DATE'})
        fund_samekind_return_period['END_DATE'] = fund_samekind_return_period['END_DATE'].astype(str)
        fund_samekind_return_period = fund_samekind_return_period[fund_samekind_return_period['END_DATE'] == max(fund_samekind_return_period['END_DATE'])]
        fund_samekind_return_period['RETURN_TYPE'] = fund_samekind_return_period['RETURN_TYPE'].astype(int)
        fund_samekind_return_period['RETURN_TYPE'] = fund_samekind_return_period['RETURN_TYPE'].replace({2998: '今年以来', 2101: '近1月', 2103: '近3月', 2106: '近6月', 2201: '近1年', 2202: '近2年', 2203: '近3年'})
        fund_samekind_return_period = fund_samekind_return_period[fund_samekind_return_period['RETURN_TYPE'].isin(['今年以来', '近1月', '近3月', '近6月', '近1年', '近2年', '近3年'])]
        fund_samekind_return_period['RANK'] = fund_samekind_return_period.apply(lambda x: '{0}/{1}'.format(x['pmnpej'], x['slnpej']), axis=1)
        fund_samekind_return_period = fund_samekind_return_period.pivot(index='FUND_CODE', columns='RETURN_TYPE', values='RANK')
        fund_samekind_return_period = fund_samekind_return_period[['今年以来', '近1月', '近3月', '近6月', '近1年', '近2年', '近3年']]
        fund_return_period = pd.concat([fund_return_period, fund_samekind_return_period])
        fund_return_period.index = ['产品收益率', '同策略排名']
        fund_return_period = fund_return_period.T.reset_index().T.reset_index()
        fund_return_period.iloc[0, 0] = ''

        fund_year_return = HBDB().read_fund_year_return_given_code(self.target_fund_code)
        fund_year_return = fund_year_return.rename(columns={'jjdm': 'FUND_CODE', 'tjnf': 'YEAR', 'hb1n': 'RETURN'})
        fund_year_return = fund_year_return[fund_year_return['RETURN'] != 99999]
        fund_year_return['RETURN'] = fund_year_return['RETURN'].apply(lambda x: '{0}%'.format(round(x, 2)))
        fund_year_return['RETURN_RANK'] = fund_year_return.apply(lambda x: '{0}/{1}'.format(int(x['hb1npmej']), int(x['hb1npmslej'])), axis=1)
        fund_year_return = fund_year_return[['YEAR', 'RETURN', 'RETURN_RANK']].sort_values('YEAR', ascending=False)

        fund_year_volatility = HBDB().read_fund_year_volatility_given_code(self.target_fund_code)
        fund_year_volatility = fund_year_volatility.rename(columns={'jjdm': 'FUND_CODE', 'tjnf': 'YEAR', 'znhzbnp1n': 'ANNUAL_VOLATILITY'})
        fund_year_volatility = fund_year_volatility[fund_year_volatility['ANNUAL_VOLATILITY'] != 99999]
        fund_year_volatility['ANNUAL_VOLATILITY'] = fund_year_volatility['ANNUAL_VOLATILITY'].apply(lambda x: '{0}%'.format(round(x, 2)))
        fund_year_volatility['ANNUAL_VOLATILITY_RANK'] = fund_year_volatility.apply(lambda x: '{0}/{1}'.format(int(x['zbdpm1nej']), int(x['tlzbdsl1nej'])), axis=1)
        fund_year_volatility = fund_year_volatility[['YEAR', 'ANNUAL_VOLATILITY', 'ANNUAL_VOLATILITY_RANK']].sort_values('YEAR', ascending=False)

        fund_year_sharpratio = HBDB().read_fund_year_sharpratio_given_code(self.target_fund_code)
        fund_year_sharpratio = fund_year_sharpratio.rename(columns={'jjdm': 'FUND_CODE', 'tjnf': 'YEAR', 'znhzbnp1n': 'ANNUAL_SHARPRATIO'})
        fund_year_sharpratio = fund_year_sharpratio[fund_year_sharpratio['ANNUAL_SHARPRATIO'] != 99999]
        fund_year_sharpratio['ANNUAL_SHARPRATIO'] = fund_year_sharpratio['ANNUAL_SHARPRATIO'].apply(lambda x: '{0}'.format(round(x, 2)))
        fund_year_sharpratio['ANNUAL_SHARPRATIO_RANK'] = fund_year_sharpratio.apply(lambda x: '{0}/{1}'.format(int(x['zxppm1nej']), int(x['tlzxpsl1nej'])), axis=1)
        fund_year_sharpratio = fund_year_sharpratio[['YEAR', 'ANNUAL_SHARPRATIO', 'ANNUAL_SHARPRATIO_RANK']].sort_values('YEAR', ascending=False)

        fund_year_maxdrawdown= HBDB().read_fund_year_maxdrawdown_given_code(self.target_fund_code)
        fund_year_maxdrawdown = fund_year_maxdrawdown.rename(columns={'jjdm': 'FUND_CODE', 'tjnf': 'YEAR', 'zbnp1n': 'MAX_DRAWDOWN'})
        fund_year_maxdrawdown = fund_year_maxdrawdown[fund_year_maxdrawdown['MAX_DRAWDOWN'] != 99999]
        fund_year_maxdrawdown['MAX_DRAWDOWN'] = fund_year_maxdrawdown['MAX_DRAWDOWN'].apply(lambda x: '{0}%'.format(round(x, 2)))
        fund_year_maxdrawdown['MAX_DRAWDOWN_RANK'] = fund_year_maxdrawdown.apply(lambda x: '{0}/{1}'.format(int(x['hczdpm1nej']), int(x['tlhczdsl1nej'])), axis=1)
        fund_year_maxdrawdown = fund_year_maxdrawdown[['YEAR', 'MAX_DRAWDOWN', 'MAX_DRAWDOWN_RANK']].sort_values('YEAR', ascending=False)

        fund_year_performance = fund_year_return.merge(fund_year_volatility, on=['YEAR'], how='left').merge(fund_year_sharpratio, on=['YEAR'], how='left').merge(fund_year_maxdrawdown, on=['YEAR'], how='left')
        fund_year_performance.columns = ['年份', '收益率', '同策略排名', '年化波动率', '同策略排名', '年化夏普比率', '同策略排名', '最大回撤', '同策略排名']
        fund_year_performance = fund_year_performance.T.reset_index().T.reset_index().drop('index', axis=1)

        if len(self.compare_fund_code_list) > 0:
            compare_return_list = []
            for compare_fund_code in [self.target_fund_code] + self.compare_fund_code_list:
                compare_return_code = HBDB().read_fund_cumret_given_code(compare_fund_code, str(int(max(self.target_fund['BEGIN_DATE'].values[0], self.compare_fund['BEGIN_DATE'].max()))), datetime.today().strftime('%Y%m%d'))
                compare_return_list.append(compare_return_code)
            compare_return = pd.concat(compare_return_list)
            compare_return = compare_return.rename(columns={'JJDM': 'FUND_CODE', 'JZRQ': 'TRADE_DATE', 'HBCL': 'CUM_RET'})
            compare_return['TRADE_DATE'] = compare_return['TRADE_DATE'].astype(str)
            compare_return = compare_return.pivot(index='TRADE_DATE', columns='FUND_CODE', values='CUM_RET')
            compare_return = compare_return.sort_index().interpolate()
            compare_return = compare_return * 0.01 + 1
            compare_return = compare_return / compare_return.iloc[0] - 1.0
            compare_return = compare_return[[self.target_fund_code] + self.compare_fund_code_list]
            compare_return.columns = [self.compare_fund_name_dic[col] for col in compare_return.columns]
            compare_return = compare_return.reset_index()

            change_dates = [date for date in self.change_dates if date >= min(compare_return['TRADE_DATE']) and date <= max(compare_return['TRADE_DATE'])]
            change_dates_loc = get_change_dates_loc(change_dates, sorted(compare_return['TRADE_DATE'].unique().tolist()))
            fig, ax = plt.subplots(figsize=(6, 3))
            max_value = 0.0
            for i, col in enumerate(list(compare_return.columns)[1:]):
                sns.lineplot(x='TRADE_DATE', y=col, data=compare_return, color=line_color_list[i], label=col)
                max_value = max(max(compare_return[col]), max_value)
            ax.vlines(x=change_dates_loc, ymin=0.0, ymax=max_value, linestyles='dashed', color='#959595')
            ax.xaxis.set_major_locator(ticker.MultipleLocator(90))
            ax.yaxis.set_major_formatter(FuncFormatter(to_100percent))
            plt.xlabel('')
            plt.ylabel('累计收益率')
            plt.xticks(rotation=90)
            plt.title('收益走势图')
            plt.legend(loc=2)
            plt.tight_layout()
            plt.savefig('{0}{1}_compare_return.png'.format(self.file_path, self.target_fund_code))
        return fund_return_period, fund_year_performance

    def get_fund_achievement(self):
        """
        生成基金业绩数据
        """
        three_years_ago = (datetime.today() - timedelta(365 * 3)).strftime('%Y%m%d')
        three_years_ago = three_years_ago if str(self.target_fund['BEGIN_DATE'].values[0]) < three_years_ago else str(self.target_fund['BEGIN_DATE'].values[0])

        fund_nav = HBDB().read_fund_cumret_given_code(self.target_fund_code, three_years_ago, datetime.today().strftime('%Y%m%d'))
        fund_nav = fund_nav.rename(columns={'JJDM': 'FUND_CODE', 'JZRQ': 'TRADE_DATE', 'HBCL': 'CUM_RET'})
        fund_nav['TRADE_DATE'] = fund_nav['TRADE_DATE'].astype(str)
        fund_nav = fund_nav.sort_values('TRADE_DATE')
        fund_nav['FUND_NAME'] = self.target_fund_name
        fund_nav['ADJ_NAV'] = 0.01 * fund_nav['CUM_RET'] + 1
        fund_nav = fund_nav.pivot(index='TRADE_DATE', columns='FUND_NAME', values='ADJ_NAV')

        fund_benchmark_return = HBDB().read_fund_benchmark_nav_given_code(self.target_fund_code)
        fund_benchmark_return = fund_benchmark_return.rename(columns={'jjdm': 'FUND_CODE', 'jzrq': 'TRADE_DATE', 'jzhbdr': 'FUND_BENCKMARK_RETURN'})
        fund_benchmark_return['TRADE_DATE'] = fund_benchmark_return['TRADE_DATE'].astype(str)
        fund_benchmark_return['FUND_BENCKMARK_RETURN'] = fund_benchmark_return['FUND_BENCKMARK_RETURN'] / 100.0
        fund_benchmark_nav = fund_benchmark_return.copy(deep=True)
        fund_benchmark_nav['FUND_BENCKMARK_NAV'] = (fund_benchmark_nav['FUND_BENCKMARK_RETURN'] + 1.0).cumprod()
        fund_benchmark_nav['FUND_BENCKMARK_NAME'] = self.target_fund_name + '基准'
        fund_benchmark_nav = fund_benchmark_nav[fund_benchmark_nav['TRADE_DATE'] >= three_years_ago]
        fund_benchmark_nav = fund_benchmark_nav.pivot(index='TRADE_DATE', columns='FUND_BENCKMARK_NAME', values='FUND_BENCKMARK_NAV')

        index_type = HBDB().read_fund_wind_size_given_code(self.target_fund_code)
        index_type = index_type.rename(columns={'jjdm': 'FUND_CODE', 'wdszsx': 'INDEX_TYPE'})
        index_type['INDEX_TYPE'] = index_type['INDEX_TYPE'].replace({'1': '小盘', '2': '中盘', '3': '大盘'})
        index_type['INDEX_TYPE'] = index_type['INDEX_TYPE'].replace({'小盘': '000852', '中盘': '000905', '大盘': '000300'})
        index = index_type['INDEX_TYPE'].values[0]
        index = '000300'
        index_daily_k = HBDB().read_index_daily_k_given_date_and_indexs(three_years_ago, [index])
        index_daily_k = index_daily_k[['zqmc', 'jyrq', 'spjg']].rename(columns={'zqmc': 'INDEX_NAME', 'jyrq': 'TRADE_DATE', 'spjg': 'CLOSE_INDEX'})
        index_daily_k['TRADE_DATE'] = index_daily_k['TRADE_DATE'].astype(str)
        index_daily_k = index_daily_k.pivot(index='TRADE_DATE', columns='INDEX_NAME', values='CLOSE_INDEX')

        stock_fund_index_daily_k = HBDB().read_index_daily_k_given_date_and_indexs(three_years_ago, ['885001'])
        stock_fund_index_daily_k = stock_fund_index_daily_k[['zqmc', 'jyrq', 'spjg']].rename(columns={'zqmc': 'INDEX_NAME', 'jyrq': 'TRADE_DATE', 'spjg': 'CLOSE_INDEX'})
        stock_fund_index_daily_k['TRADE_DATE'] = stock_fund_index_daily_k['TRADE_DATE'].astype(str)
        stock_fund_index_daily_k['INDEX_NAME'] = '偏股混合型基金指数'
        stock_fund_index_daily_k = stock_fund_index_daily_k.pivot(index='TRADE_DATE', columns='INDEX_NAME', values='CLOSE_INDEX')

        # fund_benchmark = HBDB().read_fund_benchmark_given_code(fund_code)
        # fund_benchmark = fund_benchmark.rename(columns={'JJDM': 'FUND_CODE', 'BDRQ': 'CHANGE_DATE', 'BJJZ': 'FUND_BENCHMARK', 'SYZT': 'IS_NEW'})
        # fund_benchmark = fund_benchmark[fund_benchmark['IS_NEW'] == 0]
        # fund_benchmark = fund_benchmark['FUND_BENCHMARK'].values[0]
        # fund_benchmark = fund_benchmark.split('+')
        # fund_benchmark = {b.split('×')[1]: int(b.split('×')[0][:-1]) / 100.0 for b in fund_benchmark}

        nav_df = fund_nav.merge(fund_benchmark_nav, left_index=True, right_index=True, how='left').merge(index_daily_k, left_index=True, right_index=True, how='left').merge(stock_fund_index_daily_k, left_index=True, right_index=True, how='left')
        nav_df = nav_df.replace(0.0, np.nan)
        nav_df = nav_df.fillna(method='ffill')

        achievement = pd.DataFrame(index=['业绩统计（近三年）', '年化收益率', '年化波动率', 'Sharpe比率', '最大回撤', 'Calmar比率', '投资胜率', '平均损益比'], columns=range(len(list(nav_df.columns))))
        for idx, col in enumerate(list(nav_df.columns)):
            col_nav_df = nav_df[col]
            col_ret_df = col_nav_df.pct_change().dropna()
            achievement.loc['业绩统计（近三年）', idx] = col
            achievement.loc['年化收益率', idx] = (col_nav_df.iloc[-1] / col_nav_df.iloc[0]) ** (250.0 / len(col_nav_df)) - 1.0
            achievement.loc['年化波动率', idx] = np.std(col_ret_df, ddof=1) * np.sqrt(250.0)
            achievement.loc['Sharpe比率', idx] = (achievement.loc['年化收益率', idx] - 0.03) / achievement.loc['年化波动率', idx]
            achievement.loc['最大回撤', idx] = -1.0 * max([(min(col_nav_df.iloc[i:]) / col_nav_df.iloc[i] - 1.0) * (-1.0) for i in range(len(col_nav_df))])
            achievement.loc['Calmar比率', idx] = achievement.loc['年化收益率', idx] / achievement.loc['最大回撤', idx]
            achievement.loc['投资胜率', idx] = len(col_ret_df[col_ret_df >= 0]) / float(len(col_ret_df))
            achievement.loc['平均损益比', idx] = col_ret_df[col_ret_df >= 0].mean() / col_ret_df[col_ret_df < 0].mean() * (-1.0)

            achievement.loc['年化收益率', idx] = '{0}%'.format(round(achievement.loc['年化收益率', idx] * 100.0, 2))
            achievement.loc['年化波动率', idx] = '{0}%'.format(round(achievement.loc['年化波动率', idx] * 100.0, 2))
            achievement.loc['Sharpe比率', idx] = '{0}'.format(round(achievement.loc['Sharpe比率', idx], 2))
            achievement.loc['最大回撤', idx] = '{0}%'.format(round(achievement.loc['最大回撤', idx] * 100.0, 2))
            achievement.loc['Calmar比率', idx] = '{0}'.format(round(achievement.loc['Calmar比率', idx], 2))
            achievement.loc['投资胜率', idx] = '{0}%'.format(round(achievement.loc['投资胜率', idx] * 100.0, 2))
            achievement.loc['平均损益比', idx] = '{0}'.format(round(achievement.loc['平均损益比', idx], 2))

        achievement = achievement.reset_index()

        compare_achievement = pd.DataFrame()
        compare_corr = pd.DataFrame()
        if len(self.compare_fund_code_list) > 0:
            compare_nav_list = []
            for compare_fund_code in [self.target_fund_code] + self.compare_fund_code_list:
                compare_nav_code = HBDB().read_fund_cumret_given_code(compare_fund_code, str(int(max(self.target_fund['BEGIN_DATE'].values[0], self.compare_fund['BEGIN_DATE'].max()))), datetime.today().strftime('%Y%m%d'))
                compare_nav_list.append(compare_nav_code)
            compare_nav = pd.concat(compare_nav_list)
            compare_nav = compare_nav.rename(columns={'JJDM': 'FUND_CODE', 'JZRQ': 'TRADE_DATE', 'HBCL': 'CUM_RET'})
            compare_nav['TRADE_DATE'] = compare_nav['TRADE_DATE'].astype(str)
            compare_nav = compare_nav.pivot(index='TRADE_DATE', columns='FUND_CODE', values='CUM_RET')
            compare_nav = compare_nav.sort_index().interpolate()
            compare_nav = compare_nav * 0.01 + 1
            compare_nav = compare_nav / compare_nav.iloc[0]
            compare_nav = compare_nav[[self.target_fund_code] + self.compare_fund_code_list]
            compare_nav.columns = [self.compare_fund_name_dic[col] for col in compare_nav.columns]
            compare_corr = compare_nav.pct_change().dropna().corr()
            for col in list(compare_corr):
                compare_corr[col] = compare_corr[col].apply(lambda x: '{0}%'.format(round(x * 100.0, 2)))
            for i in range(len(compare_corr)):
                for j in range(len(compare_corr)):
                    if i > j:
                        compare_corr.iloc[i, j] = '-'
            compare_corr = compare_corr.T.reset_index().T.reset_index()
            compare_corr.iloc[0, 0] = ''

            compare_achievement = pd.DataFrame(index=['业绩统计（同时段）', '年化收益率', '年化波动率', 'Sharpe比率', '最大回撤', 'Calmar比率', '投资胜率', '平均损益比'], columns=range(len([self.target_fund_code] + self.compare_fund_code_list)))
            for idx, col in enumerate(list(compare_nav.columns)):
                col_nav_df = compare_nav[col]
                col_ret_df = col_nav_df.pct_change().dropna()
                compare_achievement.loc['业绩统计（同时段）', idx] = col
                compare_achievement.loc['年化收益率', idx] = (col_nav_df.iloc[-1] / col_nav_df.iloc[0]) ** (50.0 / len(col_nav_df)) - 1.0
                compare_achievement.loc['年化波动率', idx] = np.std(col_ret_df, ddof=1) * np.sqrt(250.0)
                compare_achievement.loc['Sharpe比率', idx] = (compare_achievement.loc['年化收益率', idx] - 0.03) / compare_achievement.loc['年化波动率', idx]
                compare_achievement.loc['最大回撤', idx] = -1.0 * max([(min(col_nav_df.iloc[i:]) / col_nav_df.iloc[i] - 1.0) * (-1.0) for i in range(len(col_nav_df))])
                compare_achievement.loc['Calmar比率', idx] = compare_achievement.loc['年化收益率', idx] / compare_achievement.loc['最大回撤', idx]
                compare_achievement.loc['投资胜率', idx] = len(col_ret_df[col_ret_df >= 0]) / float(len(col_ret_df))
                compare_achievement.loc['平均损益比', idx] = col_ret_df[col_ret_df >= 0].mean() / col_ret_df[col_ret_df < 0].mean() * (-1.0)

                compare_achievement.loc['年化收益率', idx] = '{0}%'.format(round(compare_achievement.loc['年化收益率', idx] * 100.0, 2))
                compare_achievement.loc['年化波动率', idx] = '{0}%'.format(round(compare_achievement.loc['年化波动率', idx] * 100.0, 2))
                compare_achievement.loc['Sharpe比率', idx] = '{0}'.format(round(compare_achievement.loc['Sharpe比率', idx], 2))
                compare_achievement.loc['最大回撤', idx] = '{0}%'.format(round(compare_achievement.loc['最大回撤', idx] * 100.0, 2))
                compare_achievement.loc['Calmar比率', idx] = '{0}'.format(round(compare_achievement.loc['Calmar比率', idx], 2))
                compare_achievement.loc['投资胜率', idx] = '{0}%'.format(round(compare_achievement.loc['投资胜率', idx] * 100.0, 2))
                compare_achievement.loc['平均损益比', idx] = '{0}'.format(round(compare_achievement.loc['平均损益比', idx], 2))

            compare_achievement = compare_achievement.reset_index()

        return achievement, compare_achievement, compare_corr

    def get_fund_brison_attribution(self):
        fund_brison_attribution = HBDB().read_fund_brinson_attribution_given_code(self.target_fund_code)
        fund_brison_attribution = fund_brison_attribution.rename(columns={'jjdm': 'FUND_CODE', 'tjrq': 'REPORT_DATE', 'asset_allo': '大类资产配置收益', 'sector_allo': '行业配置收益', 'equity_selection': '行业选股收益', 'trading': '交易收益'})
        fund_brison_attribution['REPORT_DATE'] = fund_brison_attribution['REPORT_DATE'].apply(lambda x: str(x)[:6] + '30' if str(x)[4:6] == '06' else str(x)[:6] + '31')
        fund_brison_attribution = fund_brison_attribution.sort_values('REPORT_DATE')
        fund_brison_attribution['总超额收益'] = fund_brison_attribution['portfolio_return'] - fund_brison_attribution['benchmark_return']
        attr_list = ['大类资产配置收益', '行业配置收益', '行业选股收益', '交易收益']
        fund_brison_attribution = fund_brison_attribution[['REPORT_DATE', '总超额收益'] + attr_list]
        fund_brison_attribution.columns = ['报告日期', '总超额收益'] + attr_list
        fund_brison_attribution_disp = fund_brison_attribution.copy(deep=True)
        for i in range(1, len(attr_list)):
            fund_brison_attribution_disp[attr_list[i]] = fund_brison_attribution_disp[attr_list[i]] + fund_brison_attribution_disp[attr_list[i - 1]]
        fund_brison_attribution_disp = fund_brison_attribution_disp.iloc[-12:]

        fund_brison_attribution_disp['报告日期'] = fund_brison_attribution_disp['报告日期'].astype(str)
        change_dates = [date for date in self.change_dates if date >= min(fund_brison_attribution_disp['报告日期']) and date <= max(fund_brison_attribution_disp['报告日期'])]
        change_dates_loc = get_change_dates_loc(change_dates, sorted(fund_brison_attribution_disp['报告日期'].unique().tolist()))
        fig, ax = plt.subplots(figsize=(8, 3))
        color_list = [bar_color_list[0], bar_color_list[1], bar_color_list[7], bar_color_list[14]]
        for i in range(len(attr_list) - 1, -1, -1):
            sns.barplot(ax=ax, x='报告日期', y=attr_list[i], data=fund_brison_attribution_disp, label=attr_list[i], color=color_list[i])
        sns.lineplot(ax=ax, x='报告日期', y='总超额收益', data=fund_brison_attribution_disp, label='总超额收益', color='#F04950')
        ax.vlines(x=change_dates_loc, ymin=min([min(fund_brison_attribution_disp[col]) for col in attr_list + ['总超额收益']]), ymax=max([max(fund_brison_attribution_disp[col]) for col in attr_list + ['总超额收益']]), linestyles='dashed', color='#959595')
        ax.yaxis.set_major_formatter(FuncFormatter(to_100percent))
        plt.legend(loc='upper right', bbox_to_anchor=(1.4, 1.02))
        plt.xticks(rotation=90)
        plt.ylabel('Brison归因时序图')
        plt.tight_layout()
        plt.savefig('{0}{1}_brison_attribution.png'.format(self.file_path, self.target_fund_code))

        max_date = max(fund_brison_attribution['报告日期'])
        fund_brison_attribution_latest = fund_brison_attribution[fund_brison_attribution['报告日期'] == max_date].set_index('报告日期')
        excess_return = '{0}%'.format(round(fund_brison_attribution_latest['总超额收益'].values[0] * 100.0, 2))
        fund_brison_attribution_latest = fund_brison_attribution_latest.drop('总超额收益', axis=1).T
        fund_brison_attribution_lateste_sorted = fund_brison_attribution_latest.sort_values(max_date, ascending=False)
        fund_brison_attribution_latest[max_date] = fund_brison_attribution_latest[max_date].apply(lambda x: '{0}%'.format(round(x * 100.0, 2)))
        fund_brison_attribution_lateste_sorted[max_date] = fund_brison_attribution_lateste_sorted[max_date].apply(lambda x: '{0}%'.format(round(x * 100.0, 2)))
        brison_attribution_ret = '，'.join(fund_brison_attribution_latest[max_date].unique().tolist())
        max_attribution = fund_brison_attribution_lateste_sorted.index[0]

        fund_brison_attribution_text = '该产品Brison归因结果如下，从最新一期看，总超额收益为{0}，大类资产配置收益、行业配置收益、行业选股收益、交易收益分别为{1}，其中{2}贡献最大。'.format(excess_return, brison_attribution_ret, max_attribution)
        return fund_brison_attribution_text

    def get_fund_barra_attribution(self):
        fund_barra_attribution = HBDB().read_fund_barra_attribution_given_code(self.target_fund_code)
        fund_barra_attribution = fund_barra_attribution.rename(columns={'jjdm': 'FUND_CODE', 'tjrq': 'REPORT_DATE'})
        fund_barra_attribution['REPORT_DATE'] = fund_barra_attribution['REPORT_DATE'].astype(int)
        max_date = max(fund_barra_attribution['REPORT_DATE'])
        fund_barra_attribution = fund_barra_attribution[fund_barra_attribution['REPORT_DATE'] == max_date]
        fund_barra_attribution = fund_barra_attribution[fund_barra_attribution['attr_type'] == 'style']
        fund_barra_exposure = fund_barra_attribution[fund_barra_attribution['data_type'] == 'exposure']
        fund_barra_exposure = fund_barra_exposure[['style_factor', 'data_value', 'data_type']]
        fund_barra_exposure = fund_barra_exposure.replace({'beta': 'Beta', 'btop': '估值', 'earnyield': '盈利', 'growth': '成长性', 'leverage': '杠杆', 'liquidity': '流动性', 'momentum': '动量', 'resvol': '波动性', 'size': '规模', 'sizenl': '非线性市值'})
        fund_barra_return = fund_barra_attribution[fund_barra_attribution['data_type'] == 'return']
        fund_barra_return = fund_barra_return[['style_factor', 'data_value', 'data_type']]
        fund_barra_return = fund_barra_return.replace({'beta': 'Beta', 'btop': '估值', 'earnyield': '盈利', 'growth': '成长性', 'leverage': '杠杆', 'liquidity': '流动性', 'momentum': '动量', 'resvol': '波动性', 'size': '规模', 'sizenl': '非线性市值'})

        fig, ax = plt.subplots(figsize=(6, 3))
        sns.barplot(x='style_factor', y='data_value', data=fund_barra_exposure, color='#C94649')
        plt.xlabel('')
        plt.ylabel('风格暴露')
        plt.tight_layout()
        plt.savefig('{0}{1}_barra_exposure.png'.format(self.file_path, self.target_fund_code))

        fig, ax = plt.subplots(figsize=(6, 3))
        sns.barplot(x='style_factor', y='data_value', data=fund_barra_return, color='#C94649')
        plt.xlabel('')
        plt.ylabel('风格收益')
        plt.gca().yaxis.set_major_formatter(FuncFormatter(to_100percent))
        plt.tight_layout()
        plt.savefig('{0}{1}_barra_return.png'.format(self.file_path, self.target_fund_code))

        fund_barra_exposure = fund_barra_exposure.sort_values('data_value', ascending=False)
        fund_barra_return = fund_barra_return.sort_values('data_value', ascending=False)
        fund_barra_text = '该产品Barra风格暴露和风格收益如下，最新报告期在{0}风格上暴露最大，在{1}风格上贡献收益最大。'.format(fund_barra_exposure['style_factor'].iloc[0], fund_barra_return['style_factor'].iloc[0])
        return fund_barra_text

    def get_quantitative_report(self):
        """
        生成公募基金量化分析报告
        """
        document = Document()
        document.styles['Normal'].font.size = Pt(8)
        head = document.add_heading(text='', level=0)
        run_head = head.add_run('{0}（{1}）定量分析报告'.format(self.target_fund_name, self.target_fund_code))
        run_head.font.color.rgb = RGBColor(201, 70, 73)

        risk, statement1, statement2, statement3 = self.report_info()
        document.add_paragraph(text='风险提示').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=risk)
        document.add_paragraph(text='重要声明').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=statement1)
        document.add_paragraph(text=statement2)
        document.add_paragraph(text=statement3)
        document.add_page_break()

        head_1 = document.add_heading(text='', level=1)
        run_head_1 = head_1.add_run('一. 基金概况')
        run_head_1.font.color.rgb = RGBColor(201, 70, 73)
        head_1_1 = document.add_heading(text='', level=2)
        run_head_1_1 = head_1_1.add_run('1. 基金及基金公司简介')
        run_head_1_1.font.color.rgb = RGBColor(201, 70, 73)
        fund_info_text = self.get_fund_info()
        document.add_paragraph(text=fund_info_text)
        fund_company_info_text, company_fund_1st, company_fund_2nd = self.get_fund_company_info()
        document.add_paragraph(text=fund_company_info_text)
        rowc, colc = company_fund_1st.shape[0], company_fund_1st.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(company_fund_1st.iloc[i, j])
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')
        rowc, colc = company_fund_2nd.shape[0], company_fund_2nd.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(company_fund_2nd.iloc[i, j])
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')

        head_1_2 = document.add_heading(text='', level=2)
        run_head_1_2 = head_1_2.add_run('2. 基金经理简介')
        run_head_1_2.font.color.rgb = RGBColor(201, 70, 73)
        fund_manager_info_text, manager_all_ori, manager_all = self.get_fund_manager_info()
        document.add_paragraph(text=fund_manager_info_text)
        rowc, colc = manager_all.shape[0], manager_all.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(manager_all.iloc[i, j])
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')

        head_1_3 = document.add_heading(text='', level=2)
        run_head_1_3 = head_1_3.add_run('3. 规模变动')
        run_head_1_3.font.color.rgb = RGBColor(201, 70, 73)
        fund_scale_text, compare_scale = self.get_fund_scale()
        document.add_paragraph(text=fund_scale_text)
        document.add_picture('{0}{1}_scale.png'.format(self.file_path, self.target_fund_code))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        vlines_appendix = '注：本报告中垂直虚线为基金经理变更时间点，若无垂直虚线则代表在定量分析统计期间（近三年）未发生基金经理变更，下同。'
        document.add_paragraph(text=vlines_appendix)
        if len(self.compare_fund_code_list) > 0:
            document.add_paragraph(text='与同类型基金规模比较情况如下：')
            rowc, colc = compare_scale.shape[0], compare_scale.shape[1]
            table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
            for i in range(rowc):
                for j in range(colc):
                    table.cell(i, j).text = str(compare_scale.iloc[i, j])
            document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            document.add_paragraph(text=' ')

        head_1_4 = document.add_heading(text='', level=2)
        run_head_1_4 = head_1_4.add_run('4. 持有人结构')
        run_head_1_4.font.color.rgb = RGBColor(201, 70, 73)
        fund_holder_text, compare_holder = self.get_fund_holder()
        document.add_paragraph(text=fund_holder_text)
        document.add_picture('{0}{1}_holder.png'.format(self.file_path, self.target_fund_code))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        if len(self.compare_fund_code_list) > 0:
            document.add_paragraph(text='与同类型基金机构持有比较情况如下：')
            rowc, colc = compare_holder.shape[0], compare_holder.shape[1]
            table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
            for i in range(rowc):
                for j in range(colc):
                    table.cell(i, j).text = str(compare_holder.iloc[i, j])
            document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            document.add_paragraph(text=' ')


        # fund_manager_method_text, \
        # fund_manager_method_shift_text, shift_disp, \
        # fund_manager_method_cen_text, cen_disp, \
        # fund_manager_method_select_text, industry_stock_cen_disp, \
        # fund_manager_method_holding_period_text, holding_period_disp, \
        # fund_manager_method_left_text, left_disp, \
        # fund_manager_method_new_text, new_disp, \
        # compare_shift, compare_cen, compare_industry_stock_cen, \
        # compare_holding_period, compare_left, compare_new = self.get_fund_manager_method()
        # document.add_paragraph(text=fund_manager_method_text)
        #
        # document.add_heading(text='1.1 换手率水平', level=3)
        # document.add_paragraph(text=fund_manager_method_shift_text)
        # rowc, colc = shift_disp.shape[0], shift_disp.shape[1]
        # table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        # for i in range(rowc):
        #     for j in range(colc):
        #         table.cell(i, j).text = str(shift_disp.iloc[i, j])
        # document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        # document.add_paragraph(text=' ')
        # document.add_picture('{0}{1}_shift.png'.format(self.file_path, self.target_fund_code))
        # document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        # shift_appendix = '注：本报告规模、风格、主题、行业换手率定义为统计区间内规模、风格、主题、行业前后两期暴露变化绝对值之和的均值，规模、风格、主题、行业换手率水平定义为规模、风格、主题、行业换手率在偏股型基金整体中的排名，整体换手率水平定义为规模、风格、主题、行业换手率水平的均值。'
        # document.add_paragraph(text=shift_appendix)
        # if len(self.compare_fund_code_list) > 0:
        #     document.add_paragraph(text='与同类型基金换手率比较情况如下：')
        #     rowc, colc = compare_shift.shape[0], compare_shift.shape[1]
        #     table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        #     for i in range(rowc):
        #         for j in range(colc):
        #             table.cell(i, j).text = str(compare_shift.iloc[i, j])
        #     document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        #     document.add_paragraph(text=' ')
        #
        # document.add_heading(text='1.2 集中度水平', level=3)
        # document.add_paragraph(text=fund_manager_method_cen_text)
        # rowc, colc = cen_disp.shape[0], cen_disp.shape[1]
        # table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        # for i in range(rowc):
        #     for j in range(colc):
        #         table.cell(i, j).text = str(cen_disp.iloc[i, j])
        # document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        # document.add_paragraph(text=' ')
        # document.add_picture('{0}{1}_cen.png'.format(self.file_path, self.target_fund_code))
        # document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        # cen_appendix = '注：本报告规模、风格、主题、行业集中度定义为统计区间内规模、风格、主题、行业前n大暴露占比之和的均值，规模、风格、主题、行业集中度水平定义为规模、风格、主题、行业集中度在偏股型基金整体中的排名，整体集中度水平定义为规模、风格、主题、行业集中度水平的均值。'
        # document.add_paragraph(text=cen_appendix)
        # if len(self.compare_fund_code_list) > 0:
        #     document.add_paragraph(text='与同类型基金集中度比较情况如下：')
        #     rowc, colc = compare_cen.shape[0], compare_cen.shape[1]
        #     table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        #     for i in range(rowc):
        #         for j in range(colc):
        #             table.cell(i, j).text = str(compare_cen.iloc[i, j])
        #     document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        #     document.add_paragraph(text=' ')
        #
        # document.add_heading(text='1.3 选股方法', level=3)
        # document.add_paragraph(text=fund_manager_method_select_text)
        # rowc, colc = industry_stock_cen_disp.shape[0], industry_stock_cen_disp.shape[1]
        # table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        # for i in range(rowc):
        #     for j in range(colc):
        #         table.cell(i, j).text = str(industry_stock_cen_disp.iloc[i, j])
        # document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        # document.add_paragraph(text=' ')
        # document.add_picture('{0}{1}_indu_stock_cen.png'.format(self.file_path, self.target_fund_code))
        # document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        # industry_stock_cen_appendix = '注：本报告个股集中度定义为统计区间内前n大个股持仓权重之和的均值，个股集中度水平定义为个股集中度在偏股型基金整体中的排名。若行业集中度大于70%，个股集中度小于50%，则定义选股方法为自上而下；若行业集中度小于50%、个股集中度大于70%，则定义选股方法为自下而上；否则定义为无明显的自上而下或者自下而上选股特征。'
        # document.add_paragraph(text=industry_stock_cen_appendix)
        # if len(self.compare_fund_code_list) > 0:
        #     document.add_paragraph(text='与同类型基金选股方法比较情况如下：')
        #     rowc, colc = compare_industry_stock_cen.shape[0], compare_industry_stock_cen.shape[1]
        #     table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        #     for i in range(rowc):
        #         for j in range(colc):
        #             table.cell(i, j).text = str(compare_industry_stock_cen.iloc[i, j])
        #     document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        #     document.add_paragraph(text=' ')
        #
        # document.add_heading(text='1.4 持仓周期', level=3)
        # document.add_paragraph(text=fund_manager_method_holding_period_text)
        # rowc, colc = holding_period_disp.shape[0], holding_period_disp.shape[1]
        # table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        # for i in range(rowc):
        #     for j in range(colc):
        #         table.cell(i, j).text = str(holding_period_disp.iloc[i, j])
        # document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        # document.add_paragraph(text=' ')
        # holding_period_appendix = '注：本报告平均持有时间定义为个股进入持仓至离开持仓的时间间隔，持有期内平均收益率定义为个股被持有期间的收益率的均值，平均持有时间水平、持有期内平均收益率水平定义为平均持有时间、持有期内平均收益率在偏股型基金整体中的排名。'
        # document.add_paragraph(text=holding_period_appendix)
        # if len(self.compare_fund_code_list) > 0:
        #     document.add_paragraph(text='与同类型基金持仓周期比较情况如下：')
        #     rowc, colc = compare_holding_period.shape[0], compare_holding_period.shape[1]
        #     table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        #     for i in range(rowc):
        #         for j in range(colc):
        #             table.cell(i, j).text = str(compare_holding_period.iloc[i, j])
        #     document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        #     document.add_paragraph(text=' ')
        #
        # document.add_heading(text='1.5 左侧特征', level=3)
        # document.add_paragraph(text=fund_manager_method_left_text)
        # rowc, colc = left_disp.shape[0], left_disp.shape[1]
        # table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        # for i in range(rowc):
        #     for j in range(colc):
        #         table.cell(i, j).text = str(left_disp.iloc[i, j])
        # document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        # document.add_paragraph(text=' ')
        # left_appendix = '注：本报告左侧概率定义为个股进入持仓时股价低于半年线的概率，左侧程度定义为左侧进入时个股半年线/进入价格的均值，左侧概率水平、左侧程度水平定义为左侧概率、左侧程度在偏股型基金整体中的排名。若左侧概率水平大于50%、左侧程度水平大于50%，则定义为深度左侧；若左侧概率水平大于50%，左侧程度水平小于等于50%，则定义为左侧；否则定义为无明显的左侧特征。'
        # document.add_paragraph(text=left_appendix)
        # if len(self.compare_fund_code_list) > 0:
        #     document.add_paragraph(text='与同类型基金左侧特征比较情况如下：')
        #     rowc, colc = compare_left.shape[0], compare_left.shape[1]
        #     table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        #     for i in range(rowc):
        #         for j in range(colc):
        #             table.cell(i, j).text = str(compare_left.iloc[i, j])
        #     document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        #     document.add_paragraph(text=' ')
        #
        # document.add_heading(text='1.6 新股/次新股偏好', level=3)
        # document.add_paragraph(text=fund_manager_method_new_text)
        # rowc, colc = new_disp.shape[0], new_disp.shape[1]
        # table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        # for i in range(rowc):
        #     for j in range(colc):
        #         table.cell(i, j).text = str(new_disp.iloc[i, j])
        # document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        # document.add_paragraph(text=' ')
        # new_appendix = '注：本报告新股概率定义为个股进入持仓时成立是否满半年的概率，次新股概率定义为个股进入持仓时成立是否满一年的概率，新股概率水平、次新股概率水平定义为新股概率、次新股概率在偏股型基金整体中的排名。若新股概率水平大于75%，则定义为偏好新股；若次新股概率水平大于75%，则定义为偏好次新股；否则定义为无明显的新股/次新股偏好。'
        # document.add_paragraph(text=new_appendix)
        # if len(self.compare_fund_code_list) > 0:
        #     document.add_paragraph(text='与同类型基金新股/次新股偏好比较情况如下：')
        #     rowc, colc = compare_new.shape[0], compare_new.shape[1]
        #     table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        #     for i in range(rowc):
        #         for j in range(colc):
        #             table.cell(i, j).text = str(compare_new.iloc[i, j])
        #     document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        #     document.add_paragraph(text=' ')

        head_2 = document.add_heading(text='', level=1)
        run_head_2 = head_2.add_run('二. 基金定量分析')
        run_head_2.font.color.rgb = RGBColor(201, 70, 73)
        head_2_1 = document.add_heading(text='', level=2)
        run_head_2_1 = head_2_1.add_run('1. 仓位分析')
        run_head_2_1.font.color.rgb = RGBColor(201, 70, 73)
        fund_position_text, compare_position = self.get_fund_position()
        document.add_paragraph(text=fund_position_text)
        document.add_picture('{0}{1}_position.png'.format(self.file_path, self.target_fund_code))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        if len(self.compare_fund_code_list) > 0:
            document.add_paragraph(text='与同类型基金股票仓位比较情况如下：')
            rowc, colc = compare_position.shape[0], compare_position.shape[1]
            table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
            for i in range(rowc):
                for j in range(colc):
                    table.cell(i, j).text = str(compare_position.iloc[i, j])
            document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            document.add_paragraph(text=' ')

        head_2_2 = document.add_heading(text='', level=2)
        run_head_2_2 = head_2_2.add_run('2. 规模分析')
        run_head_2_2.font.color.rgb = RGBColor(201, 70, 73)
        fund_size_disp, fund_size_text, compare_size = self.get_fund_size()
        document.add_paragraph(text=fund_size_text)
        rowc, colc = fund_size_disp.shape[0], fund_size_disp.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(fund_size_disp.iloc[i, j])
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')
        document.add_picture('{0}{1}_size.png'.format(self.file_path, self.target_fund_code))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        fund_size_appendix = '注：本报告大中小盘根据持仓情况进行划分，对标巨潮大中小盘指数成分股确定大中小盘划分及暴露。'
        document.add_paragraph(text=fund_size_appendix)
        if len(self.compare_fund_code_list) > 0:
            document.add_paragraph(text='与同类型基金大中小盘暴露比较情况如下：')
            rowc, colc = compare_size.shape[0], compare_size.shape[1]
            table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
            for i in range(rowc):
                for j in range(colc):
                    table.cell(i, j).text = str(compare_size.iloc[i, j])
            document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            document.add_paragraph(text=' ')

        head_2_3 = document.add_heading(text='', level=2)
        run_head_2_3 = head_2_3.add_run('3. 风格分析')
        run_head_2_3.font.color.rgb = RGBColor(201, 70, 73)
        fund_style_disp, fund_style_text, compare_style = self.get_fund_style()
        document.add_paragraph(text=fund_style_text)
        rowc, colc = fund_style_disp.shape[0], fund_style_disp.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(fund_style_disp.iloc[i, j])
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')
        document.add_picture('{0}{1}_style.png'.format(self.file_path, self.target_fund_code))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        fund_style_appendix = '注：本报告成长价值风格根据持仓情况进行划分，对标巨潮成长价值指数成分股确定成长价值划分及暴露。'
        document.add_paragraph(text=fund_style_appendix)
        if len(self.compare_fund_code_list) > 0:
            document.add_paragraph(text='与同类型基金成长价值暴露比较情况如下：')
            rowc, colc = compare_style.shape[0], compare_style.shape[1]
            table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
            for i in range(rowc):
                for j in range(colc):
                    table.cell(i, j).text = str(compare_style.iloc[i, j])
            document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            document.add_paragraph(text=' ')

        head_2_4 = document.add_heading(text='', level=2)
        run_head_2_4 = head_2_4.add_run('4. 主题分析')
        run_head_2_4.font.color.rgb = RGBColor(201, 70, 73)
        fund_theme_disp, fund_theme_text, compare_theme = self.get_fund_theme()
        document.add_paragraph(text=fund_theme_text)
        rowc, colc = fund_theme_disp.shape[0], fund_theme_disp.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(fund_theme_disp.iloc[i, j])
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')
        document.add_picture('{0}{1}_theme.png'.format(self.file_path, self.target_fund_code), width=Cm(15), height=Cm(8))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        fund_theme_appendix = '注：本报告主题分类如下：大金融：银行、非银金融、房地产；消费：食品饮料、家用电器、医药生物、社会服务、农林牧渔、商贸零售，美容护理；TMT：通信、计算机、电子、传媒、国防军工；制造：交通运输、机械设备、汽车、纺织服饰、轻工制造，电力设备；周期：钢铁、有色金属、建筑装饰、建筑材料、基础化工，石油石化，煤炭，公用事业，环保。'
        document.add_paragraph(text=fund_theme_appendix)
        if len(self.compare_fund_code_list) > 0:
            document.add_paragraph(text='与同类型基金主题暴露比较情况如下：')
            rowc, colc = compare_theme.shape[0], compare_theme.shape[1]
            table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
            for i in range(rowc):
                for j in range(colc):
                    table.cell(i, j).text = str(compare_theme.iloc[i, j])
            document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            document.add_paragraph(text=' ')

        head_2_5 = document.add_heading(text='', level=2)
        run_head_2_5 = head_2_5.add_run('5. 行业分析')
        run_head_2_5.font.color.rgb = RGBColor(201, 70, 73)
        fund_industry_disp, fund_industry_text, compare_industry = self.get_fund_industry()
        document.add_paragraph(text=fund_industry_text)
        rowc, colc = fund_industry_disp.shape[0], fund_industry_disp.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(fund_industry_disp.iloc[i, j])
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')
        document.add_picture('{0}{1}_industry.png'.format(self.file_path, self.target_fund_code), width=Cm(15), height=Cm(8))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        if len(self.compare_fund_code_list) > 0:
            document.add_paragraph(text='与同类型基金前五大行业暴露比较情况如下：')
            rowc, colc = compare_industry.shape[0], compare_industry.shape[1]
            table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
            for i in range(rowc):
                for j in range(colc):
                    table.cell(i, j).text = str(compare_industry.iloc[i, j])
            document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            document.add_paragraph(text=' ')
        fund_industry_up, fund_industry_up_text, fund_industry_down, fund_industry_down_text = self.get_fund_industry_change()
        document.add_paragraph(text=fund_industry_up_text)
        rowc, colc = fund_industry_up.shape[0], fund_industry_disp.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(fund_industry_up.iloc[i, j])
                if j % 2 == 1:
                    table.cell(0, j).merge(table.cell(0, j + 1))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')
        document.add_paragraph(text=fund_industry_down_text)
        rowc, colc = fund_industry_down.shape[0], fund_industry_disp.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(fund_industry_down.iloc[i, j])
                if j % 2 == 1:
                    table.cell(0, j).merge(table.cell(0, j + 1))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')
        fund_industry_appendix = '注：本报告行业采用2021版申万一级行业。'
        document.add_paragraph(text=fund_industry_appendix)

        head_2_6 = document.add_heading(text='', level=2)
        run_head_2_6 = head_2_6.add_run('6. 持股分析')
        run_head_2_6.font.color.rgb = RGBColor(201, 70, 73)
        fund_cr_text = self.get_fund_cr()
        document.add_paragraph(text=fund_cr_text)
        document.add_picture('{0}{1}_cr5.png'.format(self.file_path, self.target_fund_code))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_picture('{0}{1}_cr10.png'.format(self.file_path, self.target_fund_code))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        fund_finance_text, compare_finance = self.get_fund_finance()
        document.add_paragraph(text=fund_finance_text)
        document.add_picture('{0}{1}_finance.png'.format(self.file_path, self.target_fund_code))
        document.add_paragraph(text='数据来源：wind，好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        if len(self.compare_fund_code_list) > 0:
            document.add_paragraph(text='与同类型基金估值盈利比较情况如下：')
            rowc, colc = compare_finance.shape[0], compare_finance.shape[1]
            table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
            for i in range(rowc):
                for j in range(colc):
                    table.cell(i, j).text = str(compare_finance.iloc[i, j])
            document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            document.add_paragraph(text=' ')

        fund_holding_latest, fund_holding_latest_text, fund_holding_disp, fund_holding_disp_text = self.get_fund_stock_change()
        document.add_paragraph(text=fund_holding_latest_text)
        rowc, colc = fund_holding_latest.shape[0], fund_holding_latest.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(fund_holding_latest.iloc[i, j])
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')
        document.add_paragraph(text=fund_holding_disp_text)
        document.add_picture('{0}{1}_dist.png'.format(self.file_path, self.target_fund_code))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        rowc, colc = fund_holding_disp.shape[0], fund_holding_disp.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(fund_holding_disp.iloc[i, j])
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')
        fund_stock_compare, fund_stock_compare_text = self.get_fund_stock_compare()
        document.add_paragraph(text=fund_stock_compare_text)
        rowc, colc = fund_stock_compare.shape[0], fund_stock_compare.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(fund_stock_compare.iloc[i, j])
        for i, stock in enumerate(fund_stock_compare[0].unique().tolist()[1:]):
            stock_index = list(fund_stock_compare[fund_stock_compare[0] == stock].index)
            table.cell(stock_index[0], 0).merge(table.cell(stock_index[-1], 0))
            table.cell(stock_index[0], 1).merge(table.cell(stock_index[-1], 1))
            table.rows[stock_index[0]].cells[0].text = stock
            table.rows[stock_index[0]].cells[1].text = fund_stock_compare[fund_stock_compare[0] == stock][1].values[0]
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')
        # HoldingCompare(self.target_fund_code, self.compare_fund_code_list).get_holding_compare()
        # p = document.add_paragraph(text='该产品持仓情况以及与其他同类型产品持仓比较情况（若有）见此excel：', style=None)
        # add_hyperlink(p, 'D:/Git/hbshare/hbshare/fe/xwq/data/quantitative_report/{0}/持仓情况.xlsx'.format(self.target_fund_code), '持仓情况。', '#3FAEEE', True)

        head_2_7 = document.add_heading(text='', level=2)
        run_head_2_7 = head_2_7.add_run('7. 换手率分析')
        run_head_2_7.font.color.rgb = RGBColor(201, 70, 73)
        fund_turnover_text, compare_turnover = self.get_fund_turnover()
        document.add_paragraph(text=fund_turnover_text)
        document.add_picture('{0}{1}_turnover.png'.format(self.file_path, self.target_fund_code))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        if len(self.compare_fund_code_list) > 0:
            document.add_paragraph(text='与同类型基金换手率比较情况如下：')
            rowc, colc = compare_turnover.shape[0], compare_turnover.shape[1]
            table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
            for i in range(rowc):
                for j in range(colc):
                    table.cell(i, j).text = str(compare_turnover.iloc[i, j])
            document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            document.add_paragraph(text=' ')

        head_3 = document.add_heading(text='', level=1)
        run_head_3 = head_3.add_run('三. 业绩概况')
        run_head_3.font.color.rgb = RGBColor(201, 70, 73)
        fund_return_period, fund_year_performance = self.get_fund_performace()
        document.add_paragraph(text='该产品业绩收益和回撤情况统计如下：', style=None)
        document.add_picture('{0}{1}_return.png'.format(self.file_path, self.target_fund_code))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_picture('{0}{1}_drawdown.png'.format(self.file_path, self.target_fund_code))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        achievement, compare_achievement, compare_corr =self.get_fund_achievement()
        rowc, colc = achievement.shape[0], achievement.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(achievement.iloc[i, j])
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')
        document.add_paragraph(text='从不同时间区间和不同年度对该产品业绩进行统计，情况如下：', style=None)
        rowc, colc = fund_return_period.shape[0], fund_return_period.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(fund_return_period.iloc[i, j])
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')
        rowc, colc = fund_year_performance.shape[0], fund_year_performance.shape[1]
        table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
        for i in range(rowc):
            for j in range(colc):
                table.cell(i, j).text = str(fund_year_performance.iloc[i, j])
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_paragraph(text=' ')
        if len(self.compare_fund_code_list) > 0:
            document.add_paragraph(text='与同类型基金业绩比较情况如下：')
            document.add_picture('{0}{1}_compare_return.png'.format(self.file_path, self.target_fund_code))
            document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            rowc, colc = compare_achievement.shape[0], compare_achievement.shape[1]
            table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
            for i in range(rowc):
                for j in range(colc):
                    table.cell(i, j).text = str(compare_achievement.iloc[i, j])
            document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            document.add_paragraph(text=' ')
            document.add_paragraph(text='与同类型基金业绩相关性如下：')
            rowc, colc = compare_corr.shape[0], compare_corr.shape[1]
            table = document.add_table(rows=rowc, cols=colc, style='Medium Shading 1 Accent 2')
            for i in range(rowc):
                for j in range(colc):
                    table.cell(i, j).text = str(compare_corr.iloc[i, j])
            document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            document.add_paragraph(text=' ')

        head_4 = document.add_heading(text='', level=1)
        run_head_4 = head_4.add_run('四. 绩效归因')
        run_head_4.font.color.rgb = RGBColor(201, 70, 73)
        fund_brison_attribution_text = self.get_fund_brison_attribution()
        document.add_paragraph(text=fund_brison_attribution_text, style=None)
        document.add_picture('{0}{1}_brison_attribution.png'.format(self.file_path, self.target_fund_code), width=Cm(18), height=Cm(10))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        fund_barra_text = self.get_fund_barra_attribution()
        document.add_paragraph(text=fund_barra_text, style=None)
        document.add_picture('{0}{1}_barra_exposure.png'.format(self.file_path, self.target_fund_code))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        document.add_picture('{0}{1}_barra_return.png'.format(self.file_path, self.target_fund_code))
        document.add_paragraph(text='数据来源：好买基金研究中心').alignment = WD_PARAGRAPH_ALIGNMENT.CENTER

        head_5 = document.add_heading(text='', level=1)
        run_head_5 = head_5.add_run('五. 总结')
        run_head_5.font.color.rgb = RGBColor(201, 70, 73)
        fund_summary = '1. 规模：该产品最新规模为{0}'.format(fund_scale_text.split('最新规模为')[1])
        document.add_paragraph(text=fund_summary)
        fund_summary = '2. 持有人结构：该产品目前{0}。'.format(fund_holder_text.split('从最新持有人结构看，')[1].split('，占比分别为')[0])
        document.add_paragraph(text=fund_summary)
        # fund_summary = '※ 投资理念：该产品{0}，{1}，{2}，{3}（{4}），{5}，{6}。'.format(fund_manager_method_shift_text.split('该产品')[1].split('，规模')[0],
        #                                                 fund_manager_method_cen_text.split('该产品')[1].split('，规模')[0],
        #                                                 fund_manager_method_select_text.split('，')[2].split('。')[0],
        #                                                 fund_manager_method_holding_period_text.split('该产品')[1].split('（')[0],
        #                                                 fund_manager_method_holding_period_text.split('（')[1].split('）')[0],
        #                                                 fund_manager_method_left_text.split('，')[2].split('。')[0],
        #                                                 fund_manager_method_new_text.split('，')[2].split('。')[0])
        # document.add_paragraph(text=fund_summary)
        fund_summary = '3. 仓位：近三年股票平均仓位为{0}。'.format(fund_position_text.split('其中股票资产近三年平均配置比例为')[1].split('，最新一期')[0])
        document.add_paragraph(text=fund_summary)
        fund_summary = '4. 投资风格：从产品规模看，该产品属于{0}从产品风格看，该产品属于{1}'.format(fund_size_text.split('属于')[1], fund_style_text.split('属于')[1])
        document.add_paragraph(text=fund_summary)
        fund_summary = '5. 主题配置：该产品属于{0}'.format(fund_theme_text.split('属于')[1])
        document.add_paragraph(text=fund_summary)
        fund_summary = '6. 行业配置：该产品属于{0}'.format(fund_industry_text.split('属于')[1])
        document.add_paragraph(text=fund_summary)
        fund_summary = '7. 持股：该产品{0}，{1}。'.format(fund_cr_text.split('该产品持股集中度情况如下，')[1].split('。')[0],
                                                    fund_holding_disp_text.split('从概率密度分布图可以看出，该产品')[1].split('，近六期')[0])
        document.add_paragraph(text=fund_summary)
        fund_summary = '8. 换手率：该产品{0}'.format(fund_turnover_text.split('该产品换手率情况如下，')[1])
        document.add_paragraph(text=fund_summary)
        fund_summary = '9. 绩效归因：从Brison绩效归因看，该产品最新一期{0}；从Barra风格归因看，该产品最新一期{1}'.format(fund_brison_attribution_text.split('从最新一期看，')[1].split('。')[0], fund_barra_text.split('如下，')[1])
        document.add_paragraph(text=fund_summary)

        for paragraph in document.paragraphs:
            if paragraph.style.name == 'Normal' and paragraph.text != '':
                paragraph.paragraph_format.first_line_indent = paragraph.style.font.size * 2

        document.save('{0}{1}（{2}）定量分析报告.docx'.format(self.file_path, self.target_fund_name, self.target_fund_code))

if __name__ == "__main__":
    file_path = 'D:/Git/hbshare/hbshare/fe/xwq/data/quantitative_report/'
    target_fund_code = '001476'  # 001476 270028 377240 008657
    compare_fund_code_list = ['001856', '005968', '001975', '001410', '688888']
    QuantitativeReport(file_path, target_fund_code, compare_fund_code_list).get_quantitative_report()
