#!/usr/bin/python3.6 # -*- coding: utf-8 -*- # @Time : 2020/11/23 15:29 # @Author : Jie. Z # @Email : zhaojiestudy@163.com # @File : result_service.py # @Software: PyCharm import pandas as pd import numpy as np import datetime from decimal import Decimal from app.service.data_service import UserCustomerDataAdaptor from app.utils.week_evaluation import * class UserCustomerResultAdaptor(UserCustomerDataAdaptor): total_result_data = {} group_result_data = {} def __init__(self, user_id, customer_id, end_date=str(datetime.date.today())): super().__init__(user_id, customer_id, end_date) # 组合结果数据 def calculate_group_result_data(self): for folio in self.group_data.keys(): folio_report_data = {} cur_folio_result_cnav_data = self.group_data[folio]["result_cnav_data"] cur_folio_order_data = self.group_data[folio]["order_df"] freq_max = cur_folio_order_data["freq"].max() first_trade_date = cur_folio_order_data["confirm_share_date"].min() fund_id_list = list(cur_folio_order_data["fund_id"].unique()) fund_id_list_earn = [i + "_earn" for i in fund_id_list] # fund_id_list_amount = [i + "_amount" for i in fund_id_list] profit_df = cur_folio_result_cnav_data[fund_id_list_earn] folio_report_data["fund_id_list"] = fund_id_list # 组合收益率数组 # return_ratio_df, contribution_decomposition= self.combination_yield(cur_folio_result_cnav_data, fund_id_list) # resample_df = resample(return_ratio_df, self.trade_cal_date, freq_max) resample_cur_folio_result_cnav_data = resample(cur_folio_result_cnav_data, self.trade_cal_date, freq_max) resample_cur_folio_result_cnav_data = resample_cur_folio_result_cnav_data[resample_cur_folio_result_cnav_data.index <= self.end_date] return_ratio_df, month_return_ratio_df, contribution_decomposition = self.combination_yield(resample_cur_folio_result_cnav_data, fund_id_list) resample_df = resample(return_ratio_df, self.trade_cal_date, freq_max) resample_df = resample_df[resample_df.index <= self.end_date] # 收益分解df contribution_decomposition_df = contribution_decomposition.fillna(0)*100 p_plot_data = [] for a_fund_id in list(contribution_decomposition_df.columns): a_name = cur_folio_order_data[cur_folio_order_data["fund_id"]==a_fund_id]["fund_name"].values[0] plot_data = {'name': a_name, 'data': contribution_decomposition_df[a_fund_id].astype(np.float64).values} p_plot_data.append(plot_data) x_lables_data = list(contribution_decomposition_df.index) cumulative_data = {'name': '总收益', 'data': ((month_return_ratio_df["cum_return_ratio"] - 1)*100).values} folio_report_data["contribution_decomposition"] = {"xlabels": x_lables_data, "product_list": p_plot_data, "cumulative": cumulative_data} # 总成本 total_cost = float(cur_folio_order_data[cur_folio_order_data["order_type"] == 1]["confirm_amount"].sum() - \ cur_folio_order_data[cur_folio_order_data["order_type"] == 2]["confirm_amount"].sum()) folio_report_data["total_cost"] = total_cost # 累积盈利 cumulative_profit = profit_df.sum().sum() folio_report_data["cumulative_profit"] = float(cumulative_profit) # 区间年化收益率 n_freq = freq_days(int(freq_max)) return_ratio_year = annual_return((resample_df["cum_return_ratio"].values[-1]-1), resample_df, n_freq) folio_report_data["return_ratio_year"] = float(return_ratio_year) # 波动率 volatility_ = volatility(resample_df["cum_return_ratio"], n_freq) folio_report_data["volatility"] = float(volatility_) # 最大回撤 drawdown = max_drawdown(resample_df["cum_return_ratio"]) folio_report_data["max_drawdown"] = drawdown # 夏普比率 sim = simple_return(resample_df["cum_return_ratio"]) exc = excess_return(sim, BANK_RATE, n_freq) sharpe = sharpe_ratio(exc, sim, n_freq) folio_report_data["sharpe"] = float(sharpe) # 期末资产 ending_assets = cumulative_profit + total_cost folio_report_data["ending_assets"] = float(ending_assets) # 本月收益 cur_month_profit_df = profit_df.loc[self.month_start_date:self.end_date+datetime.timedelta(days=1), fund_id_list_earn] cur_month_profit = cur_month_profit_df.sum().sum() folio_report_data["cur_month_profit"] = float(cur_month_profit) # 本月累积收益率 last_profit_ratio = return_ratio_df.loc[:self.month_start_date, "cum_return_ratio"].values cur_profit_ratio = return_ratio_df.loc[self.month_start_date:, "cum_return_ratio"].values if len(last_profit_ratio) <= 0: cur_month_profit_ratio = cur_profit_ratio[-1] - 1 else: cur_month_profit_ratio = (cur_profit_ratio[-1] - last_profit_ratio[-1]) / last_profit_ratio[-1] folio_report_data["cur_month_profit_ratio"] = float(cur_month_profit_ratio) # 今年累积收益 cur_year_date = pd.to_datetime(str(datetime.date(year=self.end_date.year, month=1, day=1))) cur_year_profit_df = profit_df.loc[cur_year_date:self.end_date + datetime.timedelta(days=1), fund_id_list_earn] cur_year_profit = cur_year_profit_df.sum().sum() folio_report_data["cur_year_profit"] = float(cur_year_profit) # 今年累积收益率 last_profit_ratio = return_ratio_df.loc[:cur_year_date, "cum_return_ratio"].values cur_profit_ratio = return_ratio_df.loc[cur_year_date:, "cum_return_ratio"].values if len(last_profit_ratio) <= 0: cur_year_profit_ratio = cur_profit_ratio[-1] - 1 else: cur_year_profit_ratio = (cur_profit_ratio[-1] - last_profit_ratio[-1]) / last_profit_ratio[-1] folio_report_data["cur_year_profit_ratio"] = float(cur_year_profit_ratio) # 累积收益率 cumulative_return= return_ratio_df["cum_return_ratio"].values[-1] folio_report_data["cumulative_return"] = float(cumulative_return) # 月度分组 def year_month(x): a = x.year b = x.month return str(a) + "/" + str(b) profit_df_cp = profit_df.copy() profit_df_cp["date"] = profit_df_cp.index grouped = profit_df_cp.groupby(profit_df_cp["date"].apply(year_month)) sum_group = grouped.agg(np.sum) month_sum = sum_group.sum(axis=1) # 贡献分解 month_earn = sum_group.div(month_sum, axis='rows') month_earn["datetime"] = pd.to_datetime(month_earn.index) month_earn.sort_values(by="datetime", inplace=True) del month_earn["datetime"] col = list(month_earn.columns) col_ = {x: x.replace('_earn', '') for x in list(col)} month_earn.rename(columns=col_, inplace=True) # folio_report_data["contribution_decomposition"] = month_earn # 组合内单个基金净值数据 组合内基金持仓数据 result_fund_nav_info, result_fund_hoding_info = self.group_fund_basic_info_data(cur_folio_order_data, cur_folio_result_cnav_data, cumulative_profit, total_cost) # 拼接组合以及综合结果数据 folio_report_data["group_nav_info"] = result_fund_nav_info folio_report_data["group_hoding_info"] = result_fund_hoding_info folio_report_data["group_hoding_info_total"] = \ {"total_cost": round(total_cost/10000.0, 2), "cur_month_profit": round(cur_month_profit/10000.0, 2), "cur_month_profit_ratio": round(cur_month_profit_ratio*100, 2), "ending_assets": round(ending_assets/10000.0, 2), "weight": 100, "cumulative_profit": round(cumulative_profit/10000.0, 2), "cumulative_return": round((cumulative_return-1)*100, 2), "return_ratio_year": round(return_ratio_year*100, 2)} # 对应指数数据 index_df = self.get_customer_index_nav_data() index_result = self.signal_fund_profit_result(index_df[index_df.index >= pd.to_datetime(first_trade_date)], "index") folio_report_data["index_result"] = index_result folio_report_data["return_df"] = resample_df self.group_result_data[folio] = folio_report_data return self.group_result_data # 综述数据 def calculate_total_data(self): report_data = {} cur_folio_result_cnav_data = self.total_customer_order_cnav_df cur_folio_order_data = self.user_customer_order_df freq_max = cur_folio_order_data["freq"].max() # fund_id_list = list(cur_folio_order_data["fund_id"].unique()) fund_id_list_earn = [i + "_earn" for i in fund_id_list] fund_id_list_amount = [i + "_net_amount" for i in fund_id_list] profit_df = cur_folio_result_cnav_data[fund_id_list_earn] amount_df = cur_folio_result_cnav_data[fund_id_list_amount].copy() # 持仓周期 first_trade_date = cur_folio_order_data["confirm_share_date"].min() hold_days = (self.end_date - pd.to_datetime(first_trade_date)).days report_data["hold_days"] = hold_days # 组合收益率数组 # return_ratio_df = self.combination_yield(cur_folio_result_cnav_data, fund_id_list) # resample_df = resample(return_ratio_df, self.trade_cal_date, freq_max) resample_cur_folio_result_cnav_data = resample(cur_folio_result_cnav_data, self.trade_cal_date, freq_max) resample_cur_folio_result_cnav_data = resample_cur_folio_result_cnav_data[resample_cur_folio_result_cnav_data.index <=self.end_date] return_ratio_df, month_return_ratio_df, contribution_decomposition = self.combination_yield(resample_cur_folio_result_cnav_data, fund_id_list) resample_df = resample(return_ratio_df, self.trade_cal_date, freq_max) resample_df = resample_df[resample_df.index <= self.end_date] # 总成本 total_cost = float(cur_folio_order_data[cur_folio_order_data["order_type"] == 1]["confirm_amount"].sum() - \ cur_folio_order_data[cur_folio_order_data["order_type"] == 2]["confirm_amount"].sum()) report_data["total_cost"] = total_cost # 累积盈利 cumulative_profit = profit_df.sum().sum() report_data["cumulative_profit"] = float(cumulative_profit) # 区间年化收益 n_freq = freq_days(int(freq_max)) return_ratio_year = annual_return((resample_df["cum_return_ratio"].values[-1] - 1), resample_df, n_freq) report_data["return_ratio_year"] = float(return_ratio_year) # # 波动率 # volatility_ = volatility(resample_df["cum_return_ratio"], n_freq) # report_data["volatility"] = float(volatility_) # 最大回撤 drawdown = max_drawdown(resample_df["cum_return_ratio"]) report_data["max_drawdown"] = drawdown # # # 夏普比率 # sim = simple_return(resample_df["cum_return_ratio"]) # exc = excess_return(sim, BANK_RATE, n_freq) # sharpe = sharpe_ratio(exc, sim, n_freq) # report_data["sharpe"] = float(sharpe) # 期末资产 ending_assets = cumulative_profit + total_cost report_data["ending_assets"] = float(ending_assets) # 本月收益 cur_month_profit_df = profit_df.loc[self.month_start_date:self.end_date + datetime.timedelta(days=1), fund_id_list_earn] cur_month_profit = cur_month_profit_df.sum().sum() report_data["cur_month_profit"] = float(cur_month_profit) # 本月累积收益率 last_profit_ratio = return_ratio_df.loc[:self.month_start_date, "cum_return_ratio"].values cur_profit_ratio = return_ratio_df.loc[self.month_start_date:, "cum_return_ratio"].values if len(last_profit_ratio) <= 0: cur_month_profit_ratio = cur_profit_ratio[-1] - 1 else: cur_month_profit_ratio = (cur_profit_ratio[-1] - last_profit_ratio[-1]) / last_profit_ratio[-1] report_data["cur_month_profit_ratio"] = float(cur_month_profit_ratio) # 今年累积收益 cur_year_date = pd.to_datetime(str(datetime.date(year=self.end_date.year, month=1, day=1))) cur_year_profit_df = profit_df.loc[cur_year_date:self.end_date + datetime.timedelta(days=1), fund_id_list_earn] cur_year_profit = cur_year_profit_df.sum().sum() report_data["cur_year_profit"] = float(cur_year_profit) # 今年累积收益率 last_profit_ratio = return_ratio_df.loc[:cur_year_date, "cum_return_ratio"].values cur_profit_ratio = return_ratio_df.loc[cur_year_date:, "cum_return_ratio"].values if len(last_profit_ratio) <= 0: cur_year_profit_ratio = cur_profit_ratio[-1] - 1 else: cur_year_profit_ratio = (cur_profit_ratio[-1] - last_profit_ratio[-1]) / last_profit_ratio[-1] report_data["cur_year_profit_ratio"] = float(cur_year_profit_ratio) # 月度回报 def year_month(x): a = x.year b = x.month return str(a) + "/" + str(b) profit_df_cp = profit_df.copy() profit_df_cp["date"] = profit_df_cp.index grouped = profit_df_cp.groupby(profit_df_cp["date"].apply(year_month)) sum_group = grouped.agg(np.sum) month_sum = sum_group.sum(axis=1) return_ratio_df["date"] = return_ratio_df.index return_group = return_ratio_df.groupby(return_ratio_df["date"].apply(year_month)) month_last_return_ratio = return_group.last()["cum_return_ratio"] month_sum = month_sum[month_sum.index.isin(month_last_return_ratio.index.values)] month_result = pd.DataFrame({"date": month_sum.index, "profit": month_sum.values, "ratio": month_last_return_ratio.values}) month_result["datetime"] = pd.to_datetime(month_result["date"]) month_result.sort_values(by="datetime", inplace=True) report_data["month_return"] = month_result # amount_df["date"] = amount_df.index grouped_amount = amount_df.groupby(amount_df["date"].apply(year_month)) month_amount = grouped_amount.last() del month_amount["date"] month_amount_sum = month_amount.sum(axis=1) # 月度回报表格数据 start_year = self.start_date.year now_year = datetime.datetime.now().year month_return_data_dict = {} for i in range(now_year-start_year+1): month_return_data_dict[start_year+i] = {j+1: {"profit": "-", "net_amount": "-"} for j in range(12)} for d_index, d_row in month_sum.items(): cur_year = int(d_index[:4]) cur_month = int(d_index[5:]) cur_profit = round(d_row/10000.0, 2) cur_net_amount = round(month_amount_sum.loc[d_index]/10000, 2) month_return_data_dict[cur_year][cur_month]["profit"] = cur_profit month_return_data_dict[cur_year][cur_month]["net_amount"] = cur_net_amount # 组合月度回报表 report_data["month_return_data_dict"] = month_return_data_dict # # 贡献分解 # month_earn = sum_group.div(month_sum, axis='rows') # report_data["contribution_decomposition"] = month_earn # 累积收益率 cumulative_return = return_ratio_df["cum_return_ratio"].values[-1] report_data["cumulative_return"] = float(cumulative_return) # 对应指数数据 index_df = self.get_customer_index_nav_data() index_result = self.signal_fund_profit_result(index_df[index_df.index >= pd.to_datetime(first_trade_date)], "index") report_data["index_result"] = index_result # self.__month_return(cur_folio_result_cnav_data, fund_id_list) self.total_result_data = report_data return report_data # 基金净值数据,持仓数据 def group_fund_basic_info_data(self, p_order_df, p_result_cnav_data, p_sum_profit, p_total_amount): group_fund_basic_info = [] group_fund_hoding_info = [] freq_max = p_order_df["freq"].max() n_freq = freq_days(int(freq_max)) resample_df = resample(p_result_cnav_data, self.trade_cal_date, freq_max) for index, row in p_order_df.iterrows(): cur_fund_id = str(row["fund_id"]) cur_fund_performance = self.all_fund_performance[cur_fund_id] cur_fund_info_series = cur_fund_performance.iloc[-1] # 基金净值数据 fund_basic_info = {"fund_name": row["fund_name"], "confirm_nav": round(row["nav"],4)} fund_basic_info["cur_nav"] = round(float(self.fund_nav_total[cur_fund_id].dropna().values[-1]), 4) fund_basic_info["cur_cnav"] = round(float(self.fund_cnav_total[cur_fund_id].dropna().values[-1]), 4) fund_basic_info["ret_1w"] = round(cur_fund_info_series["ret_1w"]*100, 2) if cur_fund_info_series["ret_1w"] is not None else "-" # 上周 fund_basic_info["ret_cum_1m"] = round(cur_fund_info_series["ret_cum_1m"]*100, 2) if cur_fund_info_series["ret_cum_1m"] is not None else "-" # 最近一个月 fund_basic_info["ret_cum_6m"] = round(cur_fund_info_series["ret_cum_6m"]*100, 2) if cur_fund_info_series["ret_cum_6m"] is not None else "-" # 最近半年 fund_basic_info["ret_cum_1y"] = round(cur_fund_info_series["ret_cum_1y"]*100, 2) if cur_fund_info_series["ret_cum_1y"] is not None else "-" # 最近一年 fund_basic_info["ret_cum_ytd"] = round(cur_fund_info_series["ret_cum_ytd"]*100, 2) if cur_fund_info_series["ret_cum_ytd"] is not None else "-" # 今年以来 fund_basic_info["ret_cum_incep"] = round(cur_fund_info_series["ret_cum_incep"]*100, 2) if cur_fund_info_series["ret_cum_incep"] is not None else "-" # 成立以来 # 申购以来 confirm_date = pd.to_datetime(row["confirm_share_date"]) confirm_cnav = float(p_result_cnav_data.loc[confirm_date, cur_fund_id]) fund_basic_info["ret_after_confirm"] = round((fund_basic_info["cur_cnav"] - confirm_cnav)/confirm_cnav*100, 2) # 分红 distribution_df = self.all_fund_distribution[cur_fund_id] if distribution_df.empty: fund_basic_info["distribution"] = "-" else: distribution_df["price_date"] = pd.to_datetime(distribution_df["price_date"]) distribution = float(distribution_df[distribution_df["price_date"] > confirm_date]["distribution"].sum()) fund_basic_info["distribution"] = round(distribution, 4) if distribution != 0 else "-" group_fund_basic_info.append(fund_basic_info) # 基金持仓数据 total_market_values = p_sum_profit + p_total_amount # 月末总市值 fund_hoding_info = {"fund_strategy_name": dict_substrategy[int(row["substrategy"])], "fund_name": row["fund_name"]} fund_hoding_info["confirm_date"] = row["confirm_share_date"] fund_hoding_info["hold_year"] = round((self.end_date - pd.to_datetime(row["confirm_share_date"])).days/365.0, 2) # 存续年数 fund_hoding_info["weight"] = round(float(row["confirm_amount"]) / total_market_values * 100, 2) # 月末占比 fund_hoding_info["market_values"] = round((float(row["confirm_share"]) * (fund_basic_info["cur_cnav"] - confirm_cnav) + float(row["confirm_amount"]))/10000, 2) fund_hoding_info["cost"] = round(float(row["confirm_amount"])/10000, 2) # 投资本金 # 当月收益 last_month_cnav_serise = p_result_cnav_data[p_result_cnav_data.index