import json import sys import time import uuid from jinja2 import PackageLoader, Environment from app.api.engine import work_dir, pdf_folder, template_folder, pdf_save_folder from app.config.default_template_params import hold_default_template, diagnose_default_template from app.service.fund_service import fund_index_compare 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, draw_index_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_INFO15917850824287', customer_id='6716613802534121472', pdf_name=str(uuid.uuid4()) + '.pdf', type=1): self.user_customer = UserCustomerResultAdaptor(ifa_id, customer_id) self.customer_name = self.user_customer.customer_real_name self.ifa_name = self.user_customer.ifa_real_name # self.pdf_name = self.ifa_name + "_" + self.customer_name + "_" + '.pdf' self.pdf_name = pdf_name # 1持仓报告2诊断报告 self.type = type # 全部数据 self.df = self.user_customer.calculate_total_data() # 组合结果数据 self.d = self.user_customer.calculate_group_result_data() self.all_folio_result = {} # 分组合拼接结果数据 self.get_group_result() # 投资总览 self.get_summarize() # 月度回报 self.get_month_return() # 月度回报表格 self.get_month_table_return() # 分组和计算个基点评以及新增基金等结果 def get_group_result(self): for group_name, group_result in self.d.items(): portfolio_diagnose = self.get_portfolio_diagnose(group_result["fund_id_list"], invest_amount=group_result["total_cost"]) cur_group_portfolio_result = { 'new_correlation': [], 'propose_fund_data_list': [], 'suggestions_result': {}, 'suggestions_result_asset': {}, 'return_compare_pic': [], 'indicator_compare': [], 'new_group_evaluation': [], "correlation": group_result["correlation"] } # 旧持仓组合点评 self.comments_on_position_portfolio(portfolio_diagnose, group_name, cur_group_portfolio_result) # 贡献分解 self.contribution_deco(group_result, cur_group_portfolio_result) # 目标与业绩 self.objectives_performance(group_result, cur_group_portfolio_result) # 个基点评 self.single_fund_comment(portfolio_diagnose, cur_group_portfolio_result) # 旧收益比较 self.get_old_compare_pic(cur_group_portfolio_result) # 旧相关性 self.get_old_correlation(portfolio_diagnose, cur_group_portfolio_result) if self.type == 2: # 新增基金 self.propose_fund(portfolio_diagnose, cur_group_portfolio_result) # 新收益比较 self.get_transfer_suggestions(portfolio_diagnose, group_name, cur_group_portfolio_result) # 新相关性 self.get_new_correlation(portfolio_diagnose, cur_group_portfolio_result) self.all_folio_result[group_name] = cur_group_portfolio_result def get_portfolio_diagnose(self, portfolio, client_type=1, invest_amount=10000000): if invest_amount < 10000000: invest_amount = 10000000 folio_fund_dict = {} for fd in portfolio: folio_fund_dict[fd] = self.user_customer.all_fund_type_dict[fd] portfolio_diagnose = PortfolioDiagnose(client_type=client_type, portfolio=folio_fund_dict, invest_amount=float(invest_amount), start_date=self.user_customer.start_date) if self.type == 2: portfolio_diagnose.optimize() return portfolio_diagnose # 全部数据综述结果 def get_summarize(self): """投资总览.""" self.total_cost = int(self.df["total_cost"]) # 投资成本 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, portfolio_diagnose, folio, cur_group_portfolio_result): """旧持仓组合点评. 旧贡献分解数据""" cur_group_portfolio_result["old_evaluation"], cur_group_portfolio_result["old_return_compare_data"],\ cur_group_portfolio_result["old_indicator_compare"] = portfolio_diagnose.old_evaluation(folio, self.d, self.user_customer) def contribution_deco(self, group_result, cur_group_portfolio_result): """贡献分解.""" g_data = group_result["contribution_decomposition"] cur_group_portfolio_result["contribution_decomposition"] = draw_contribution_chart(g_data['xlabels'], g_data['product_list'], g_data['cumulative']) def single_fund_comment(self, portfolio_diagnose, cur_group_portfolio_result): """个基点评.""" single_fund_data_list = [] portfolio_evaluation = portfolio_diagnose.old_portfolio_evaluation() index_compare_chart_data = portfolio_diagnose.original_fund_index_compare(self.user_customer.fund_cnav_total) # with futures.ProcessPoolExecutor(os.cpu_count()) as executor: # res = executor.map(draw_index_combination_chart, index_compare_chart_data) # res = list(res) res = [] for chart_data in index_compare_chart_data: r = draw_index_combination_chart(chart_data) res.append(r) for i in range(len(portfolio_evaluation)): 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] }) cur_group_portfolio_result["single_fund_data_list"] = single_fund_data_list def get_old_compare_pic(self, cur_group_portfolio_result): """旧收益比较""" cur_group_portfolio_result["old_return_compare_pic"] = draw_old_combination_chart(cur_group_portfolio_result["old_return_compare_data"]["xlabels"], cur_group_portfolio_result["old_return_compare_data"]["origin_combination"], cur_group_portfolio_result["old_return_compare_data"]["index"]) def get_transfer_suggestions(self, portfolio_diagnose, folio, cur_group_portfolio_result): """新收益比较,调仓建议""" cur_group_portfolio_result["suggestions_result"], cur_group_portfolio_result["suggestions_result_asset"], \ cur_group_portfolio_result["return_compare_data"], \ cur_group_portfolio_result["indicator_compare"], cur_group_portfolio_result["new_group_evaluation"] = portfolio_diagnose.new_evaluation(folio, self.d, self.user_customer) cur_group_portfolio_result["return_compare_pic"] = draw_combination_chart(cur_group_portfolio_result["return_compare_data"]["xlabels"], cur_group_portfolio_result["return_compare_data"]["new_combination"], cur_group_portfolio_result["return_compare_data"]["origin_combination"], cur_group_portfolio_result["return_compare_data"]["index"]) def get_old_correlation(self, portfolio_diagnose, cur_group_portfolio_result): """旧相关性分析.""" old_correlation = cur_group_portfolio_result["correlation"] old_correlation_columns = old_correlation.columns.tolist() old_correlation_values = old_correlation.values.tolist() cur_group_portfolio_result["old_correlation"] = list(zip(range(1, len(old_correlation_columns)+1), old_correlation_columns, old_correlation_values)) del cur_group_portfolio_result["correlation"] def get_new_correlation(self, portfolio_diagnose, cur_group_portfolio_result): """新相关性分析.""" new_correlation = portfolio_diagnose.new_correlation new_correlation_columns = new_correlation.columns.tolist() new_correlation_values = new_correlation.values.tolist() cur_group_portfolio_result["new_correlation"] = list(zip(range(1, len(new_correlation_columns)+1), new_correlation_columns, new_correlation_values)) def propose_fund(self, portfolio_diagnose, cur_group_portfolio_result): """新增基金""" # 优化组合建议1 -- 新增基金 propose_fund_data_list = [] propose_fund_evaluation = portfolio_diagnose.propose_fund_evaluation() # propose_radar_chart_data = portfolio_diagnose.propose_fund_radar() # with futures.ProcessPoolExecutor(os.cpu_count()) as executor: # res = executor.map(gen_radar_chart, propose_radar_chart_data) res = [] for fund_id in portfolio_diagnose.propose_portfolio.columns: r = fund_index_compare(fund_id) res.append(r) for i in range(len(propose_fund_evaluation)): propose_fund_data_list.append({ 'fund_name': propose_fund_evaluation[i]['name'], 'status': '增仓', 'evaluation': propose_fund_evaluation[i]['data'], 'radar_chart_path': res[i] }) cur_group_portfolio_result["propose_fund_data_list"] = propose_fund_data_list def objectives_performance(self, group_result, cur_group_portfolio_result): """目标与业绩""" cur_group_portfolio_result["totoal_rate_of_return"] = "%.2f" % round((group_result['cumulative_return']-1)*100, 2) # 成立以来累计收益率 cur_group_portfolio_result["annualised_return"] = "%.2f" % round(group_result["return_ratio_year"]*100, 2) # 年化收益率 cur_group_portfolio_result["volatility"] = "%.2f" % round(group_result["volatility"]*100, 2) cur_group_portfolio_result["max_withdrawal"] = "%.2f" % round(group_result["max_drawdown"][0]*100, 2) cur_group_portfolio_result["sharpe_ratio"] = "%.2f" % round(group_result["sharpe"], 2) cur_group_portfolio_result["cost_of_investment"] = "%.2f" % round(group_result["total_cost"]/10000.0, 2) # 投资成本 cur_group_portfolio_result["index_section_return"] = "%.2f" % round((group_result["index_result"]["return_ratio"]-1)*100, 2) cur_group_portfolio_result["index_annualised_return"] = "%.2f" % round(group_result["index_result"]["return_ratio_year"]*100, 2) # 年化收益率 cur_group_portfolio_result["index_volatility"] = "%.2f" % round(group_result["index_result"]["volatility"]*100, 2) cur_group_portfolio_result["index_max_withdrawal"] = "%.2f" % round(group_result["index_result"]["max_drawdown"][0]*100, 2) cur_group_portfolio_result["index_sharpe_ratio"] = "%.2f" % round(group_result["index_result"]["sharpe"], 2) cur_group_portfolio_result["group_nav_info"] = group_result["group_nav_info"] cur_group_portfolio_result["group_hoding_info"] = group_result["group_hoding_info"] cur_group_portfolio_result["group_hoding_info_total"] = group_result["group_hoding_info_total"] def get_template_data(self, default_template=None): """""" if self.type == 1: # 持仓报告数据 data = { # 全局数据 'customer_name': self.customer_name, 'year_month': self.user_customer.end_date.strftime("%Y-%m-%d"), 'valueSex': self.user_customer.valueSex, 'month': self.user_customer.month_start_date.strftime("%m"), 'start_date': self.user_customer.start_date.strftime("%Y-%m-%d"), 'latest_worth_day': self.user_customer.last_nav_date, 'customer_level': '平衡型', # 综述数据 'now_allocation_amount': '{:,}'.format(self.total_cost), 'now_yield': self.now_yield, 'index_yield': self.index_yield, 'now_annualised_return': self.now_annualised_return, 'now_withdrawal': self.now_withdrawal, 'index_withdrawal': self.index_withdrawal, 'expected_withdrawal': 20, 'now_year_income': '{:,}'.format(self.now_year_income), 'now_month_income': '{:,}'.format(self.now_month_income), 'final_balance': '{:,}'.format(self.final_balance), 'total_profit': '{:,}'.format(self.total_profit), 'total_profit_temp': self.total_profit, 'now_year_income_temp': self.now_year_income, 'now_month_income_temp': self.now_month_income, 'monthly_return_performance_pic': self.monthly_return_performance_pic, 'month_rise': self.month_rise, 'year_totoal_rate_of_return': self.year_totoal_rate_of_return, 'monthly_table_return': self.monthly_table_return, # 组合数据 'all_folio_result': self.all_folio_result, } if default_template: self.data = {**default_template, **data} else: self.data = {**hold_default_template, **data} elif self.type == 2: # 诊断报告数据 data = { # 全局数据 'customer_name': self.customer_name, 'year_month': self.user_customer.end_date.strftime("%Y-%m-%d"), 'valueSex': self.user_customer.valueSex, 'month': self.user_customer.month_start_date.strftime("%m"), 'start_date': self.user_customer.start_date.strftime("%Y-%m-%d"), 'latest_worth_day': self.user_customer.last_nav_date, 'customer_level': '平衡型', # 综述数据 'now_allocation_amount': '{:,}'.format(self.total_cost), 'now_yield': self.now_yield, 'index_yield': self.index_yield, 'now_annualised_return': self.now_annualised_return, 'now_withdrawal': self.now_withdrawal, 'index_withdrawal': self.index_withdrawal, 'expected_withdrawal': 20, 'now_year_income': '{:,}'.format(self.now_year_income), 'now_month_income': '{:,}'.format(self.now_month_income), 'final_balance': '{:,}'.format(self.final_balance), 'total_profit': '{:,}'.format(self.total_profit), 'total_profit_temp': self.total_profit, 'now_year_income_temp': self.now_year_income, 'now_month_income_temp': self.now_month_income, 'monthly_return_performance_pic': self.monthly_return_performance_pic, 'month_rise': self.month_rise, 'year_totoal_rate_of_return': self.year_totoal_rate_of_return, 'monthly_table_return': self.monthly_table_return, # 组合数据 'all_folio_result': self.all_folio_result, } if default_template: self.data = {**default_template, **data} else: self.data = {**hold_default_template, **data} return self.data def render_data(self, data=None): # 全部数据 if data: self.data = data # 开始渲染html模板 env = Environment(loader=PackageLoader('app', 'templates')) # 创建一个包加载器对象 # template = env.get_template('monthReport.html') # 获取一个模板文件 template = env.get_template('/v2/monthReportV2.1.html') # 获取一个模板文件 monthReport_html = template.render(self.data).replace('None', 'none') # 渲染 # 保存 monthReport_html # save_file = "app/pdf/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_save_folder + self.pdf_name) if __name__ == '__main__': start = time.time() dt = DataIntegrate(ifa_id='USER_INFO15955928945523', customer_id='67347292618078412802', type=2) data = dt.get_template_data() dt.render_data() print('耗时{}秒'.format(round(time.time()-start, 2)))