import time
import uuid
from jinja2 import PackageLoader, Environment
from app.api.engine import work_dir, pdf_folder, template_folder
from app.config.default_template_params import hold_default_template, diagnose_default_template
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': []
}
# 旧持仓组合点评
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):
portfolio_diagnose = PortfolioDiagnose(client_type=client_type, portfolio=portfolio, invest_amount=float(invest_amount),
start_date=self.user_customer.start_date)
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)):
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'] = '减仓
'
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 = portfolio_diagnose.old_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))
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 = list(res)
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):
""""""
if self.type == 1:
# 持仓报告数据
data = {
# 全局数据
'customer_name': self.customer_name,
'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"),
'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,
}
# self.data = {**hold_default_template, **data}
self.data = data
elif self.type == 2:
# 诊断报告数据
data = {
# 全局数据
'customer_name': self.customer_name,
'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"),
'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,
}
# self.data = {**diagnose_default_template, **data}
self.data = data
print(data)
return self.data
def render_data(self):
# 全部数据
# 开始渲染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) # 渲染
# 保存 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()
dt = DataIntegrate(ifa_id='USER_INFO15917850824287', customer_id='6716613802534121472', type=1)
data = dt.get_template_data()
# dt.render_data()
print('耗时{}秒'.format(round(time.time()-start, 2)))