import time import uuid from jinja2 import PackageLoader, Environment from app.api.engine import work_dir, pdf_folder, template_folder from app.service.portfolio_diagnose import PortfolioDiagnose from app.service.result_service_v2 import UserCustomerResultAdaptor import numpy as np from concurrent import futures import os # 准备数据 from app.utils.draw import draw_month_return_chart, draw_contribution_chart, draw_combination_chart, \ draw_old_combination_chart from app.utils.html_to_pdf import html_to_pdf from app.utils.radar_chart import gen_radar_chart class DataIntegrate: def __init__(self, ifa_id='USER_INFO15914346866762', customer_id='202009281545001', pdf_name=str(uuid.uuid4()) + '.pdf'): self.user_customer = UserCustomerResultAdaptor(ifa_id, customer_id) self.customer_name = self.user_customer.customer_real_name self.pdf_name = pdf_name self.df = self.user_customer.calculate_total_data() self.d = self.user_customer.calculate_group_result_data() # 组合数据 self.group_result = self.d["default"] self.get_portfolio_diagnose(self.group_result["fund_id_list"]) # 投资总览 self.get_summarize() # 月度回报 self.get_month_return() # 月度回报表格 self.get_month_table_return() # 旧持仓组合点评 self.comments_on_position_portfolio() # 贡献分解 self.contribution_deco() # 目标与业绩 self.objectives_performance(self.group_result) # 个基点评 self.single_fund_comment() # 旧收益比较 self.get_old_compare_pic() # 旧相关性 self.get_old_correlation() # # 新增基金 # self.propose_fund() # # 新收益比较 # self.get_transfer_suggestions() # # 新相关性 # self.get_new_correlation() # 渲染模版 self.render_data() def get_portfolio_diagnose(self, portfolio, client_type=1, invest_amount=10000000): self.portfolio_diagnose = PortfolioDiagnose(client_type=client_type, portfolio=portfolio, invest_amount=invest_amount) self.portfolio_diagnose.optimize() def get_summarize(self): """投资总览.""" self.total_cost = round(self.df["total_cost"], 2) # 投资成本 self.now_yield = round((self.df['cumulative_return']-1)*100, 2) # 成立以来累计收益率 self.now_annualised_return = round(self.df["return_ratio_year"] * 100, 2) # 年化收益率 self.index_yield = round((self.df["index_result"]["return_ratio"]-1)*100, 2) # 指数收益率 self.now_withdrawal = round(self.df["max_drawdown"][0]*100, 2) # 最大回撤 self.index_withdrawal = round(self.df["index_result"]["max_drawdown"][0]*100, 2) # 指数最大回撤 self.now_month_income = int(self.df["cur_month_profit"]) # 本月收益 self.month_rise = round(self.df["cur_month_profit_ratio"] * 100, 2) # 本月涨幅 self.year_totoal_rate_of_return = round(self.df["cur_year_profit_ratio"] * 100, 2) # 今年累计收益率 self.now_year_income = int(self.df["cur_year_profit"]) # 今年累计收益 self.final_balance = int(self.df["total_cost"] + self.df["cumulative_profit"]) # 期末资产 self.total_profit = int(self.df["cumulative_profit"]) # 累计盈利 def get_month_return(self): """月度回报.""" xlabels, product_list, cumulative = self.user_customer.get_month_return_chart() self.monthly_return_performance_pic = draw_month_return_chart(xlabels, product_list, cumulative) def get_month_table_return(self): self.monthly_table_return = self.df["month_return_data_dict"] def comments_on_position_portfolio(self): """旧持仓组合点评. 旧贡献分解数据""" self.old_evaluation, self.old_return_compare_data, self.old_indicator_compare = self.portfolio_diagnose.old_evaluation('default', self.d, self.user_customer) def contribution_deco(self): """贡献分解.""" g_data = self.group_result["contribution_decomposition"] self.contribution_decomposition = draw_contribution_chart(g_data['xlabels'], g_data['product_list'], g_data['cumulative']) def single_fund_comment(self): """个基点评.""" self.single_fund_data_list = [] portfolio_evaluation = self.portfolio_diagnose.old_portfolio_evaluation() radar_chart_data = self.portfolio_diagnose.single_fund_radar() with futures.ProcessPoolExecutor(os.cpu_count()) as executor: res = executor.map(gen_radar_chart, radar_chart_data) res = list(res) for i in range(len(portfolio_evaluation)): if portfolio_evaluation[i]['status'] == '保留': portfolio_evaluation[i]['status'] = '
保留
' elif portfolio_evaluation[i]['status'] == '增仓': portfolio_evaluation[i]['status'] = '
增仓
' elif portfolio_evaluation[i]['status'] == '换仓': portfolio_evaluation[i]['status'] = '
换仓
' elif portfolio_evaluation[i]['status'] == '减仓': portfolio_evaluation[i]['status'] = '
减仓
' self.single_fund_data_list.append({ 'fund_name': portfolio_evaluation[i]['name'], 'status': portfolio_evaluation[i]['status'], 'evaluation': portfolio_evaluation[i]['data'], 'radar_chart_path': res[i] }) def get_old_compare_pic(self): """旧收益比较""" self.old_return_compare_pic = draw_old_combination_chart(self.old_return_compare_data["xlabels"], self.old_return_compare_data["origin_combination"], self.old_return_compare_data["index"]) def get_transfer_suggestions(self): """新收益比较,调仓建议""" self.suggestions_result, self.suggestions_result_asset, self.return_compare_data, \ self.indicator_compare, self.new_group_evaluation = self.portfolio_diagnose.new_evaluation("default", self.d, self.user_customer) self.return_compare_pic = draw_combination_chart(self.return_compare_data["xlabels"], self.return_compare_data["new_combination"], self.return_compare_data["origin_combination"], self.return_compare_data["index"]) def get_old_correlation(self): """旧相关性分析.""" old_correlation = self.portfolio_diagnose.old_correlation old_correlation_columns = old_correlation.columns.tolist() old_correlation_values = old_correlation.values.tolist() self.old_correlation = list(zip(range(1, len(old_correlation_columns)+1), old_correlation_columns, old_correlation_values)) def get_new_correlation(self): """新相关性分析.""" new_correlation = self.portfolio_diagnose.new_correlation new_correlation_columns = new_correlation.columns.tolist() new_correlation_values = new_correlation.values.tolist() self.new_correlation = list(zip(range(1, len(new_correlation_columns)+1), new_correlation_columns, new_correlation_values)) def propose_fund(self): """新增基金""" # 优化组合建议1 -- 新增基金 self.propose_fund_data_list = [] propose_fund_evaluation = self.portfolio_diagnose.propose_fund_evaluation() propose_radar_chart_data = self.portfolio_diagnose.propose_fund_radar() with futures.ProcessPoolExecutor(os.cpu_count()) as executor: res = executor.map(gen_radar_chart, propose_radar_chart_data) res = list(res) for i in range(len(propose_fund_evaluation)): self.propose_fund_data_list.append({ 'fund_name': propose_fund_evaluation[i]['name'], 'status': '增仓', 'evaluation': propose_fund_evaluation[i]['data'], 'radar_chart_path': res[i] }) # for i in range(len(propose_fund_evaluation)): # self.propose_fund_data_list.append({ # 'fund_name': propose_fund_evaluation[i]['name'], # 'status': '增仓', # 'evaluation': propose_fund_evaluation[i]['data'], # 'radar_chart_path': gen_radar_chart(propose_radar_chart_data[i]) # }) def objectives_performance(self, group_result): """目标与业绩""" self.totoal_rate_of_return = round((group_result['cumulative_return']-1)*100, 2) # 成立以来累计收益率 self.annualised_return = round(group_result["return_ratio_year"]*100, 2) # 年化收益率 self.volatility = round(group_result["volatility"]*100, 2) self.max_withdrawal = round(group_result["max_drawdown"][0]*100, 2) self.sharpe_ratio = round(group_result["sharpe"], 2) self.cost_of_investment = round(group_result["total_cost"]/10000.0, 2) # 投资成本 self.index_section_return = round((group_result["index_result"]["return_ratio"]-1)*100, 2) self.index_annualised_return = round(group_result["index_result"]["return_ratio_year"]*100, 2) # 年化收益率 self.index_volatility = round(group_result["index_result"]["volatility"]*100, 2) self.index_max_withdrawal = round(group_result["index_result"]["max_drawdown"][0]*100, 2) self.index_sharpe_ratio = round(group_result["index_result"]["sharpe"], 2) self.group_nav_info = group_result["group_nav_info"] self.group_hoding_info = group_result["group_hoding_info"] self.group_hoding_info_total = group_result["group_hoding_info_total"] def render_data(self): # 全部数据 data = { # 封面 值为None不不显示,为block显示 'box0': 'block', # 目录 'box1': 'block', # 投资总览 'box2': 'block', # 目标与业绩 'box3': 'block', # 业绩的明细 'box4': 'block', # 个基点评 'box5': 'block', # 优化组合建议 'box6': None, # 新增基金 'box7': None, # 结尾 'box8': 'block', 'cover_back': template_folder + '/v2/img/cover-back.png', 'logo': template_folder + '/v2/img/logo.png', 'scene': template_folder + '/v2/img/scene.png', 'team': template_folder + '/v2/img/team.png', 'customer_name': self.customer_name, 'customer_gender': '女', 'year_month': self.user_customer.month_start_date.strftime("%Y-%m"), 'month': self.user_customer.month_start_date.strftime("%m"), 'start_date': self.user_customer.start_date.strftime("%Y-%m-%d"), 'ifa_company': '飞度工作室', 'title': '10月综述', 'brand_name': '资产管
理中心', 'customer_old': 42, 'customer_level': '平衡型', # 'new_evaluation': self.new_evaluation, 'position_years': '5年', 'planned_allocation_amount': 2000.00, 'now_allocation_amount': self.total_cost, 'now_yield': self.now_yield, 'index_yield': self.index_yield, 'expected_yield': 20, 'now_annualised_return': self.now_annualised_return, 'now_withdrawal': self.now_withdrawal, 'index_withdrawal': self.index_withdrawal, 'expected_withdrawal': 20, 'now_year_income': self.now_year_income, 'now_month_income': self.now_month_income, 'totoal_rate_of_return': self.totoal_rate_of_return, 'month_rise': self.month_rise, 'year_totoal_rate_of_return': self.year_totoal_rate_of_return, 'annualised_return': self.annualised_return, 'cost_of_investment': self.cost_of_investment, 'final_balance': self.final_balance, 'total_profit': self.total_profit, 'latest_worth_day': self.user_customer.last_nav_date, 'index_comparison': {'section_return': self.totoal_rate_of_return, 'annualized_returns': self.annualised_return, 'volatility': self.volatility, 'max_withdrawal': self.max_withdrawal, 'sharpe_ratio': self.sharpe_ratio}, 'index_comparison_500': {'section_return': self.index_section_return, 'annualized_returns': self.index_annualised_return, 'volatility': self.index_volatility, 'max_withdrawal': self.index_max_withdrawal, 'sharpe_ratio': self.index_sharpe_ratio}, 'monthly_return_performance_pic': self.monthly_return_performance_pic, 'monthly_table_return': self.monthly_table_return, 'group_nav_info': self.group_nav_info, 'group_hoding_info': self.group_hoding_info, 'group_hoding_info_total': self.group_hoding_info_total, 'old_evaluation': self.old_evaluation, 'old_indicator_compare': self.old_indicator_compare, 'contribution_decomposition': self.contribution_decomposition, 'single_fund_data_list': self.single_fund_data_list, 'old_correlation': self.old_correlation, 'old_return_compare_pic': self.old_return_compare_pic, # 'new_correlation': self.new_correlation, # 'propose_fund_data_list': self.propose_fund_data_list, # 'suggestions_result': self.suggestions_result, # 'suggestions_result_asset': self.suggestions_result_asset, # 'return_compare_pic': self.return_compare_pic, # 'indicator_compare': self.indicator_compare, # 'new_group_evaluation': self.new_group_evaluation 'new_correlation': [], 'propose_fund_data_list': [], 'suggestions_result': {}, 'suggestions_result_asset': {}, 'return_compare_pic': [], 'indicator_compare': [], 'new_group_evaluation': [] } # 开始渲染html模板 env = Environment(loader=PackageLoader('app', 'templates')) # 创建一个包加载器对象 # template = env.get_template('monthReport.html') # 获取一个模板文件 template = env.get_template('/v2/monthReportV2.html') # 获取一个模板文件 monthReport_html = template.render(data) # 渲染 # 保存 monthReport_html # save_file = "app/html/monthReport.html" # with open(save_file, 'w', encoding="utf-8") as f: # f.write(monthReport_html) # save_file = "app/html/v2/monthReportV2.html" # with open(save_file, 'w', encoding="utf-8") as f: # f.write(monthReport_html) html_to_pdf(monthReport_html, pdf_folder + self.pdf_name) if __name__ == '__main__': start = time.time() DataIntegrate(ifa_id='USER_INFO15916072577875', customer_id='6716613804966817792') print('耗时{}秒'.format(round(time.time()-start, 2)))