Commit e6e5d8e8 authored by 李宗熹's avatar 李宗熹

修改基金诊断

parent 742c85d2
...@@ -375,7 +375,7 @@ class EfficientFrontier(base_optimizer.BaseConvexOptimizer): ...@@ -375,7 +375,7 @@ class EfficientFrontier(base_optimizer.BaseConvexOptimizer):
return self._solve_cvxpy_opt_problem() return self._solve_cvxpy_opt_problem()
def efficient_return(self, target_return, market_neutral=False): def efficient_return(self, target_return, target_drawdown, market_neutral=False):
""" """
Calculate the 'Markowitz portfolio', minimising volatility for a given target return. Calculate the 'Markowitz portfolio', minimising volatility for a given target return.
...@@ -405,12 +405,13 @@ class EfficientFrontier(base_optimizer.BaseConvexOptimizer): ...@@ -405,12 +405,13 @@ class EfficientFrontier(base_optimizer.BaseConvexOptimizer):
self.objective = cp.quad_form(self._w, self.cov_matrix) self.objective = cp.quad_form(self._w, self.cov_matrix)
ret = self.expected_returns.T @ self._w ret = self.expected_returns.T @ self._w
drawdown = self.expected_drawdown.T @ self._w
for obj in self._additional_objectives: for obj in self._additional_objectives:
self._objective += obj self._objective += obj
self._constraints.append(ret >= target_return) self._constraints.append(ret >= target_return)
self._constraints.append(drawdown <= target_drawdown)
# The equality constraint is either "weights sum to 1" (default), or # The equality constraint is either "weights sum to 1" (default), or
# "weights sum to 0" (market neutral). # "weights sum to 0" (market neutral).
if market_neutral: if market_neutral:
......
...@@ -4,6 +4,10 @@ ...@@ -4,6 +4,10 @@
@file:portfolio_diagnose.py @file:portfolio_diagnose.py
@time:2020/12/07 @time:2020/12/07
""" """
import warnings
warnings.filterwarnings("ignore")
from app.utils.fund_rank import * from app.utils.fund_rank import *
from app.utils.risk_parity import * from app.utils.risk_parity import *
from app.pypfopt import risk_models from app.pypfopt import risk_models
...@@ -113,7 +117,7 @@ def choose_good_evaluation(evaluation): ...@@ -113,7 +117,7 @@ def choose_good_evaluation(evaluation):
if v1[0] > 1: if v1[0] > 1:
del evaluation[1] del evaluation[1]
if v2[0] > 1: if v2[0] > 1 and float(v2[1].strip('%')) <= 60:
del evaluation[2] del evaluation[2]
if v3[0] > 1: if v3[0] > 1:
del evaluation[3] del evaluation[3]
...@@ -135,6 +139,7 @@ def choose_bad_evaluation(evaluation): ...@@ -135,6 +139,7 @@ def choose_bad_evaluation(evaluation):
if v1[0] < 2: if v1[0] < 2:
del evaluation[1] del evaluation[1]
if v2[0] < 2: if v2[0] < 2:
# if v2:
del evaluation[2] del evaluation[2]
if v3[0] < 2: if v3[0] < 2:
del evaluation[3] del evaluation[3]
...@@ -331,8 +336,8 @@ tamp_fund = get_tamp_fund() ...@@ -331,8 +336,8 @@ tamp_fund = get_tamp_fund()
class PortfolioDiagnose(object): class PortfolioDiagnose(object):
def __init__(self, client_type, portfolio, invest_amount, expect_return=0.2, def __init__(self, client_type, portfolio, invest_amount, expect_return=0.1,
expect_drawdown=0.1, index_id='000905.SH', invest_type='private', start_date=None, end_date=None): expect_drawdown=0.15, index_id='000905.SH', invest_type='private', start_date=None, end_date=None):
"""基金诊断 """基金诊断
Args: Args:
...@@ -386,6 +391,7 @@ class PortfolioDiagnose(object): ...@@ -386,6 +391,7 @@ class PortfolioDiagnose(object):
prod = get_tamp_nav(self.portfolio[0], self.start_date, invest_type=self.invest_type) prod = get_tamp_nav(self.portfolio[0], self.start_date, invest_type=self.invest_type)
fund_info = get_fund_info(self.end_date, invest_type=self.invest_type) fund_info = get_fund_info(self.end_date, invest_type=self.invest_type)
# while prod is None or prod.index[-1] - prod.index[0] < 0.6 * (self.end_date - self.start_date):
while prod is None: while prod is None:
# 获取的净值表为空时首先考虑基金净值数据不足半年,查找同一基金经理下的相同二级策略的基金ID作替换 # 获取的净值表为空时首先考虑基金净值数据不足半年,查找同一基金经理下的相同二级策略的基金ID作替换
result = fund_info[fund_info['fund_id'] == self.portfolio[0]] result = fund_info[fund_info['fund_id'] == self.portfolio[0]]
...@@ -412,7 +418,8 @@ class PortfolioDiagnose(object): ...@@ -412,7 +418,8 @@ class PortfolioDiagnose(object):
for idx in range(len(self.portfolio) - 1): for idx in range(len(self.portfolio) - 1):
prod1 = get_tamp_nav(self.portfolio[idx + 1], self.start_date, invest_type=self.invest_type) prod1 = get_tamp_nav(self.portfolio[idx + 1], self.start_date, invest_type=self.invest_type)
if prod1 is None or prod1.index[-1] - prod1.index[0] < 0.6 * (self.end_date - self.start_date): # if prod1 is None or prod1.index[-1] - prod1.index[0] < 0.6 * (self.end_date - self.start_date):
if prod1 is None:
result = fund_info[fund_info['fund_id'] == self.portfolio[idx + 1]] result = fund_info[fund_info['fund_id'] == self.portfolio[idx + 1]]
if result['fund_manager_id'].count() != 0: if result['fund_manager_id'].count() != 0:
...@@ -442,6 +449,7 @@ class PortfolioDiagnose(object): ...@@ -442,6 +449,7 @@ class PortfolioDiagnose(object):
prod.sort_index(inplace=True) prod.sort_index(inplace=True)
prod.ffill(inplace=True) prod.ffill(inplace=True)
prod = resample(prod, get_trade_cal(), min(self.freq_list)) prod = resample(prod, get_trade_cal(), min(self.freq_list))
prod.dropna(how='any', inplace=True)
return prod return prod
def abandon(self, prod): def abandon(self, prod):
...@@ -454,8 +462,8 @@ class PortfolioDiagnose(object): ...@@ -454,8 +462,8 @@ class PortfolioDiagnose(object):
""" """
self.old_correlation = cal_correlation(prod) self.old_correlation = cal_correlation(prod)
for fund in prod.columns: for fund in prod.columns:
print(fund)
z_score = search_rank(fund_rank, fund, metric='z_score') z_score = search_rank(fund_rank, fund, metric='z_score')
# 建议替换得分为60或与其他基金相关度大于0.8的基金 # 建议替换得分为60或与其他基金相关度大于0.8的基金
if z_score < 60: if z_score < 60:
...@@ -465,7 +473,7 @@ class PortfolioDiagnose(object): ...@@ -465,7 +473,7 @@ class PortfolioDiagnose(object):
elif np.any(self.old_correlation[fund] > 0.8): elif np.any(self.old_correlation[fund] > 0.8):
self.abandon_fund_corr.append(fund) self.abandon_fund_corr.append(fund)
prod = prod.drop(self.abandon_fund_score + self.abandon_fund_corr, axis=1) prod.drop(self.abandon_fund_score + self.abandon_fund_corr, axis=1, inplace=True)
self.old_correlation = self.old_correlation.fillna(1).round(2) self.old_correlation = self.old_correlation.fillna(1).round(2)
self.old_correlation.columns = self.old_correlation.columns.map(lambda x: get_fund_name(x).values[0][0]) self.old_correlation.columns = self.old_correlation.columns.map(lambda x: get_fund_name(x).values[0][0])
self.old_correlation.index = self.old_correlation.index.map(lambda x: get_fund_name(x).values[0][0]) self.old_correlation.index = self.old_correlation.index.map(lambda x: get_fund_name(x).values[0][0])
...@@ -480,35 +488,36 @@ class PortfolioDiagnose(object): ...@@ -480,35 +488,36 @@ class PortfolioDiagnose(object):
Returns: 增加建议申购基金的组合净值表 Returns: 增加建议申购基金的组合净值表
""" """
# 组合内已包含的策略
# included_strategy = set()
# 按每种基金最少投资100w确定组合包含的最大基金数量 # 按每种基金最少投资100w确定组合包含的最大基金数量
max_len = self.invest_amount // 1e6 - len(prod.columns) max_len = len(self.portfolio) - len(prod.columns)
# 排名表内包含的所有策略 # 排名表内包含的所有策略
# all_strategy = set(fund_rank['substrategy'].to_list()) # all_strategy = set(fund_rank['substrategy'].to_list())
# if prod is not None: all_risk = {"H", "M", "L"}
included_risk = {}
if prod is not None:
# included_strategy = set([search_rank(fund_rank, fund, metric='substrategy') for fund in prod.columns]) # included_strategy = set([search_rank(fund_rank, fund, metric='substrategy') for fund in prod.columns])
included_risk = set([get_risk_level(search_rank(fund_rank, fund, metric='substrategy'))
for fund in prod.columns])
# 待添加策略为所有策略-组合已包含策略 # 待添加策略为所有策略-组合已包含策略
# add_strategy = all_strategy - included_strategy # add_strategy = all_strategy - included_strategy
add_risk = all_risk - included_risk
# 遍历产品池,推荐得分>80且与组合内其他基金相关度低于0.8的属于待添加策略的基金 # 遍历产品池,推荐得分>80且与组合内其他基金相关度低于0.8的属于待添加策略的基金
for proposal in tamp_fund['fund_id']: for proposal in tamp_fund['fund_id']:
if proposal in fund_rank['fund_id'].to_list() and proposal not in prod.columns:
if proposal in fund_rank['fund_id'].to_list():
proposal_z_score = search_rank(fund_rank, proposal, metric='z_score') proposal_z_score = search_rank(fund_rank, proposal, metric='z_score')
# proposal_strategy = fund_rank[fund_rank['fund_id'] == proposal]['substrategy'].values[0] proposal_strategy = fund_rank[fund_rank['fund_id'] == proposal]['substrategy'].values[0]
else: else:
continue continue
# if proposal_z_score > 80 and proposal_strategy in add_strategy: if proposal_z_score > 60 and (get_risk_level(proposal_strategy) in add_risk or not add_risk):
if proposal_z_score > 60: # if proposal_z_score > 80:
proposal_nav = get_tamp_nav(proposal, self.start_date, invest_type=self.invest_type) proposal_nav = get_tamp_nav(proposal, self.start_date, invest_type=self.invest_type)
# 忽略净值周期大于周更的产品 # 忽略净值周期大于周更的产品
if get_frequency(proposal_nav) <= 52: # if get_frequency(proposal_nav) <= 52:
continue # continue
self.freq_list.append(get_frequency(proposal_nav)) self.freq_list.append(get_frequency(proposal_nav))
proposal_nav = rename_col(proposal_nav, proposal) proposal_nav = rename_col(proposal_nav, proposal)
...@@ -517,7 +526,6 @@ class PortfolioDiagnose(object): ...@@ -517,7 +526,6 @@ class PortfolioDiagnose(object):
prod = pd.merge(prod, proposal_nav, how='outer', on='end_date').astype(float) prod = pd.merge(prod, proposal_nav, how='outer', on='end_date').astype(float)
prod.sort_index(inplace=True) prod.sort_index(inplace=True)
prod.ffill(inplace=True) prod.ffill(inplace=True)
prod.bfill(inplace=True)
prod = resample(prod, get_trade_cal(), min(self.freq_list)) prod = resample(prod, get_trade_cal(), min(self.freq_list))
self.new_correlation = cal_correlation(prod) self.new_correlation = cal_correlation(prod)
...@@ -526,14 +534,15 @@ class PortfolioDiagnose(object): ...@@ -526,14 +534,15 @@ class PortfolioDiagnose(object):
if np.all(judge_correlation < 0.8): if np.all(judge_correlation < 0.8):
self.proposal_fund.append(proposal) self.proposal_fund.append(proposal)
max_len -= 1 max_len -= 1
# add_strategy -= {proposal_strategy} # add_strategy -= {proposal_strategy}
add_risk -= {get_risk_level(proposal_strategy)}
# if len(add_strategy) == 0 or max_len == 0: # if len(add_strategy) == 0 or max_len == 0:
if max_len == 0: if max_len == 0:
break break
else: else:
prod.drop(columns=proposal, inplace=True) prod.drop(columns=proposal, inplace=True)
prod.dropna(how='all', inplace=True)
self.new_correlation = self.new_correlation.fillna(1).round(2) self.new_correlation = self.new_correlation.fillna(1).round(2)
self.new_correlation.columns = self.new_correlation.columns.map(lambda x: get_fund_name(x).values[0][0]) self.new_correlation.columns = self.new_correlation.columns.map(lambda x: get_fund_name(x).values[0][0])
self.new_correlation.index = self.new_correlation.index.map(lambda x: get_fund_name(x).values[0][0]) self.new_correlation.index = self.new_correlation.index.map(lambda x: get_fund_name(x).values[0][0])
...@@ -572,32 +581,32 @@ class PortfolioDiagnose(object): ...@@ -572,32 +581,32 @@ class PortfolioDiagnose(object):
propose_risk_mapper[fund] = str(get_risk_level(search_rank(fund_rank, fund, metric='substrategy'))) propose_risk_mapper[fund] = str(get_risk_level(search_rank(fund_rank, fund, metric='substrategy')))
if self.client_type == 1: if self.client_type == 1:
risk_upper = {"H": 1.0} risk_upper = {"L": 0.6, "M": 0.4, "H": 0.0}
risk_lower = {"L": 0.0} risk_lower = {"L": 0.6, "M": 0.4, "H": 0.0}
elif self.client_type == 2: elif self.client_type == 2:
risk_upper = {"H": 1.0} risk_upper = {"L": 0.5, "M": 0.3, "H": 0.2}
risk_lower = {"L": 0.0} risk_lower = {"L": 0.5, "M": 0.3, "H": 0.2}
elif self.client_type == 3: elif self.client_type == 3:
risk_upper = {"H": 1.0} risk_upper = {"L": 0.3, "M": 0.5, "H": 0.2}
risk_lower = {"L": 0.0} risk_lower = {"L": 0.3, "M": 0.5, "H": 0.2}
elif self.client_type == 4: elif self.client_type == 4:
risk_upper = {"H": 1.0} risk_upper = {"L": 0.3, "M": 0.4, "H": 0.3}
risk_lower = {"L": 0.0} risk_lower = {"L": 0.3, "M": 0.4, "H": 0.3}
elif self.client_type == 5: elif self.client_type == 5:
risk_upper = {"H": 1.0} risk_upper = {"L": 0.0, "M": 0.5, "H": 0.5}
risk_lower = {"L": 0.0} risk_lower = {"L": 0.0, "M": 0.5, "H": 0.5}
else: else:
risk_upper = {"H": 1.0} risk_upper = {"H": 1.0}
risk_lower = {"L": 0.0} risk_lower = {"L": 0.0}
raise ValueError raise ValueError
w_low = 1000000 / self.invest_amount w_low = 1000000 / self.invest_amount
# ef = EfficientFrontier(mu, S, weight_bounds=[w_low, 1], expected_drawdown=dd) ef = EfficientFrontier(mu, S, weight_bounds=[w_low, 1], expected_drawdown=dd)
ef = EfficientFrontier(mu, S, weight_bounds=[0, 1], expected_drawdown=dd) # ef = EfficientFrontier(mu, S, weight_bounds=[0, 1], expected_drawdown=dd)
ef.add_sector_constraints(propose_risk_mapper, risk_lower, risk_upper) ef.add_sector_constraints(propose_risk_mapper, risk_lower, risk_upper)
ef.efficient_return(target_return=self.expect_return) ef.efficient_return(target_return=self.expect_return, target_drawdown=self.expect_drawdown)
clean_weights = ef.clean_weights() clean_weights = ef.clean_weights()
# ef.portfolio_performance(verbose=True) ef.portfolio_performance(verbose=True)
self.new_weights = np.array(list(clean_weights.values())) self.new_weights = np.array(list(clean_weights.values()))
print(clean_weights) print(clean_weights)
end4 = time.time() end4 = time.time()
...@@ -769,9 +778,11 @@ class PortfolioDiagnose(object): ...@@ -769,9 +778,11 @@ class PortfolioDiagnose(object):
# 基金名称,策略分级 # 基金名称,策略分级
propose_fund_id_name_list = [propose_fund_df[propose_fund_df["fund_id"] == fund_id]["fund_name"].values[0] for propose_fund_id_name_list = [propose_fund_df[propose_fund_df["fund_id"] == fund_id]["fund_name"].values[0] for
fund_id in propose_fund_id_list] fund_id in propose_fund_id_list]
propose_fund_id_strategy_name_list = [dict_substrategy[int(propose_fund_df[propose_fund_df["fund_id"] == fund_id]["substrategy"].values[0])] for propose_fund_id_strategy_name_list = [
dict_substrategy[int(propose_fund_df[propose_fund_df["fund_id"] == fund_id]["substrategy"].values[0])] for
fund_id in propose_fund_id_list] fund_id in propose_fund_id_list]
propose_fund_asset = [round(self.new_weights[i] * total_asset, 2) for i in range(len(propose_fund_id_name_list))] propose_fund_asset = [round(self.new_weights[i] * total_asset, 2) for i in
range(len(propose_fund_id_name_list))]
propose_info = {propose_fund_id_strategy_name_list[i]: propose_info = {propose_fund_id_strategy_name_list[i]:
{"fund_name": propose_fund_id_name_list[i], {"fund_name": propose_fund_id_name_list[i],
...@@ -813,26 +824,25 @@ class PortfolioDiagnose(object): ...@@ -813,26 +824,25 @@ class PortfolioDiagnose(object):
# 新组合累积收益df # 新组合累积收益df
propose_fund_return_limit_data = propose_fund_return[propose_fund_return.index >= group_order_start_date] propose_fund_return_limit_data = propose_fund_return[propose_fund_return.index >= group_order_start_date]
start_return = propose_fund_return_limit_data['return'].values[0] start_return = propose_fund_return_limit_data['return'].values[0]
propose_fund_return_limit_data["new_return"] = (propose_fund_return_limit_data["return"] - start_return)/(1+start_return) propose_fund_return_limit_data["new_return"] = (propose_fund_return_limit_data["return"] - start_return) / (
1 + start_return)
# 新组合累积收益 # 新组合累积收益
try:
new_return_ratio = propose_fund_return_limit_data["new_return"].values[-1] new_return_ratio = propose_fund_return_limit_data["new_return"].values[-1]
except:
new_return_ratio = 0
# 新组合区间年化收益率 # 新组合区间年化收益率
freq_max = group_order_df["freq"].max() freq_max = group_order_df["freq"].max()
n_freq = freq_days(int(freq_max)) n_freq = freq_days(int(freq_max))
new_return_ratio_year = annual_return(new_return_ratio, propose_fund_return_limit_data, n_freq) new_return_ratio_year = annual_return(new_return_ratio, propose_fund_return_limit_data, n_freq)
# 新组合波动率 # 新组合波动率
new_volatility = volatility(propose_fund_return_limit_data["new_return"]+1, n_freq) new_volatility = volatility(propose_fund_return_limit_data["new_return"] + 1, n_freq)
# 新组合最大回撤 # 新组合最大回撤
new_drawdown = max_drawdown(propose_fund_return_limit_data["new_return"]+1) new_drawdown = max_drawdown(propose_fund_return_limit_data["new_return"] + 1)
# 新组合夏普比率 # 新组合夏普比率
sim = simple_return(propose_fund_return_limit_data["new_return"]+1) sim = simple_return(propose_fund_return_limit_data["new_return"] + 1)
exc = excess_return(sim, BANK_RATE, n_freq) exc = excess_return(sim, BANK_RATE, n_freq)
new_sharpe = sharpe_ratio(exc, sim, n_freq) new_sharpe = sharpe_ratio(exc, sim, n_freq)
...@@ -841,21 +851,23 @@ class PortfolioDiagnose(object): ...@@ -841,21 +851,23 @@ class PortfolioDiagnose(object):
start_index_return = index_return[" close"].values[0] start_index_return = index_return[" close"].values[0]
index_return["new_index_return"] = (index_return[" close"] - start_index_return) / (1 + start_index_return) index_return["new_index_return"] = (index_return[" close"] - start_index_return) / (1 + start_index_return)
index_return_ratio = index_return["new_index_return"].values[-1] index_return_ratio = index_return["new_index_return"].values[-1]
index_return_ratio_year = annual_return(index_return["new_index_return"].values[-1], index_return["new_index_return"], n_freq) index_return_ratio_year = annual_return(index_return["new_index_return"].values[-1],
index_volatility = volatility(index_return["new_index_return"]+1, n_freq) index_return["new_index_return"], n_freq)
index_drawdown = max_drawdown(index_return["new_index_return"]+1) index_volatility = volatility(index_return["new_index_return"] + 1, n_freq)
index_sim = simple_return(propose_fund_return_limit_data["new_return"]+1) index_drawdown = max_drawdown(index_return["new_index_return"] + 1)
index_sim = simple_return(propose_fund_return_limit_data["new_return"] + 1)
index_exc = excess_return(index_sim, BANK_RATE, n_freq) index_exc = excess_return(index_sim, BANK_RATE, n_freq)
index_sharpe = sharpe_ratio(index_exc, index_sim, n_freq) index_sharpe = sharpe_ratio(index_exc, index_sim, n_freq)
# 收益对比数据 # 收益对比数据
return_compare_df = pd.merge(index_return[["new_index_return"]], old_return_df[["cum_return_ratio"]], right_index=True, return_compare_df = pd.merge(index_return[["new_index_return"]], old_return_df[["cum_return_ratio"]],
right_index=True,
left_index=True) left_index=True)
return_compare_df = pd.merge(return_compare_df, propose_fund_return_limit_data["new_return"], right_index=True, return_compare_df = pd.merge(return_compare_df, propose_fund_return_limit_data["new_return"], right_index=True,
left_index=True) left_index=True)
return_compare_df["date"] = return_compare_df.index return_compare_df["date"] = return_compare_df.index
return_compare_df["date"] = return_compare_df["date"].apply(lambda x: x.strftime("%Y-%m-%d")) return_compare_df["date"] = return_compare_df["date"].apply(lambda x: x.strftime("%Y-%m-%d"))
return_compare_df.iloc[1:-1,:]["date"] = "" return_compare_df.iloc[1:-1, :]["date"] = ""
return_compare_result = { return_compare_result = {
"new_combination": {"name": "新组合", "data": return_compare_df["new_return"].values}, "new_combination": {"name": "新组合", "data": return_compare_df["new_return"].values},
"index": {"name": "中证500", "data": return_compare_df["new_index_return"].values}, "index": {"name": "中证500", "data": return_compare_df["new_index_return"].values},
...@@ -864,15 +876,20 @@ class PortfolioDiagnose(object): ...@@ -864,15 +876,20 @@ class PortfolioDiagnose(object):
} }
# 指标对比 # 指标对比
old_indicator = {"group_name": "现有持仓组合", "return_ratio": round((old_return-1)*100, 2), "return_ratio_year": round(old_return_ratio_year*100,2), old_indicator = {"group_name": "现有持仓组合", "return_ratio": round((old_return - 1) * 100, 2),
"volatility": round(old_volatility*100, 2), "max_drawdown": round(old_max_drawdown[0]*100, 2), "sharpe": round(old_sharpe, 2)} "return_ratio_year": round(old_return_ratio_year * 100, 2),
new_indicator = {"group_name": "建议优化组合", "return_ratio": round(new_return_ratio*100, 2), "return_ratio_year": round(new_return_ratio_year*100, 2), "volatility": round(old_volatility * 100, 2),
"volatility": round(new_volatility*100, 2), "max_drawdown": round(new_drawdown[0]*100, 2), "sharpe": round(new_sharpe, 2)} "max_drawdown": round(old_max_drawdown[0] * 100, 2), "sharpe": round(old_sharpe, 2)}
index_indicator = {"group_name": "中证500", "return_ratio": round(index_return_ratio*100, 2), "return_ratio_year": round(index_return_ratio_year*100, 2), new_indicator = {"group_name": "建议优化组合", "return_ratio": round(new_return_ratio * 100, 2),
"volatility": round(index_volatility*100, 2), "max_drawdown": round(index_drawdown[0]*100, 2), "sharpe": round(index_sharpe, 2)} "return_ratio_year": round(new_return_ratio_year * 100, 2),
"volatility": round(new_volatility * 100, 2), "max_drawdown": round(new_drawdown[0] * 100, 2),
"sharpe": round(new_sharpe, 2)}
index_indicator = {"group_name": "中证500", "return_ratio": round(index_return_ratio * 100, 2),
"return_ratio_year": round(index_return_ratio_year * 100, 2),
"volatility": round(index_volatility * 100, 2),
"max_drawdown": round(index_drawdown[0] * 100, 2), "sharpe": round(index_sharpe, 2)}
indicator_compare = [new_indicator, old_indicator, index_indicator] indicator_compare = [new_indicator, old_indicator, index_indicator]
# 在保留{}的基础上,建议赎回{},并增配{}后,整体组合波动率大幅降低,最大回撤从{}降到不足{},年化收益率提升{}个点 # 在保留{}的基础上,建议赎回{},并增配{}后,整体组合波动率大幅降低,最大回撤从{}降到不足{},年化收益率提升{}个点
hold_fund = set(self.portfolio) - set(self.abandon_fund_score + self.abandon_fund_corr + self.no_data_fund) hold_fund = set(self.portfolio) - set(self.abandon_fund_score + self.abandon_fund_corr + self.no_data_fund)
hold_fund_name = [get_fund_name(x).values[0][0] for x in hold_fund] hold_fund_name = [get_fund_name(x).values[0][0] for x in hold_fund]
...@@ -930,7 +947,7 @@ class PortfolioDiagnose(object): ...@@ -930,7 +947,7 @@ class PortfolioDiagnose(object):
index_return_monthly.index = index_return_monthly.index.strftime('%Y-%m') index_return_monthly.index = index_return_monthly.index.strftime('%Y-%m')
fund_return_monthly.index = fund_return_monthly.index.strftime('%Y-%m') fund_return_monthly.index = fund_return_monthly.index.strftime('%Y-%m')
compare = pd.merge(index_return_monthly, fund_return_monthly, how='inner', left_index=True, right_index=True) compare = pd.merge(index_return_monthly, fund_return_monthly, how='inner', left_index=True, right_index=True)
fund_win_rate = ((compare[fund_id] - compare['pct_chg']) > 0).sum() fund_win_rate = ((compare[fund_id] - compare['pct_chg']) > 0).sum() / compare[fund_id].count()
return_rank = search_rank(fund_rank, fund_id, metric='annual_return_rank') return_rank = search_rank(fund_rank, fund_id, metric='annual_return_rank')
return_level = np.select([return_rank >= 0.8, return_level = np.select([return_rank >= 0.8,
...@@ -955,7 +972,7 @@ class PortfolioDiagnose(object): ...@@ -955,7 +972,7 @@ class PortfolioDiagnose(object):
sharp_rank < 0.6], [0, 1, 2]).item() sharp_rank < 0.6], [0, 1, 2]).item()
data = {1: [total_level, return_level, drawdown_level, sharp_level], data = {1: [total_level, return_level, drawdown_level, sharp_level],
2: [return_triple, str(fund_win_rate), return_bool], 2: [return_triple, format(fund_win_rate, '.2%'), return_bool],
3: [drawdown_triple, drawdown_triple, format(drawdown_value, '.2%'), drawdown_triple], 3: [drawdown_triple, drawdown_triple, format(drawdown_value, '.2%'), drawdown_triple],
4: [return_bool, drawdown_bool, drawdown_bool, return_bool, drawdown_bool]} 4: [return_bool, drawdown_bool, drawdown_bool, return_bool, drawdown_bool]}
...@@ -992,7 +1009,7 @@ class PortfolioDiagnose(object): ...@@ -992,7 +1009,7 @@ class PortfolioDiagnose(object):
sentence = { sentence = {
1: "该基金整体表现%s,收益能力%s,回撤控制能力%s,风险收益比例%s;\n", 1: "该基金整体表现%s,收益能力%s,回撤控制能力%s,风险收益比例%s;\n",
2: "在收益方面,该基金年化收益能力%s同类基金平均水平,有%s区间跑赢指数,绝对收益能力%s;\n", 2: "在收益方面,该基金年化收益能力%s同类基金平均水平,有%s区间跑赢指数,绝对收益能力%s;\n",
3: "在风险方面,该基金抵御风险能力%s,在同类基金中处于%s等水平,最大回撤为%s,%s同类基金平均水平;\n", 3: "在风险方面,该基金抵御风险能力%s,在同类基金中处于%s等水平,最大回撤为%s,%s同类基金平均水平;\n",
4: "该基金收益%s的同时回撤%s,也就是说,该基金在用%s风险换取%s收益,存在%s风险;\n", 4: "该基金收益%s的同时回撤%s,也就是说,该基金在用%s风险换取%s收益,存在%s风险;\n",
5: "基金经理,投资年限%s年,经验丰富;投资能力较强,生涯中共管理过%s只基金,历任的%s只基金平均业绩在同类中处于上游水平,其中%s只排名在前%s;生涯年化回报率%s,同期大盘只有%s;"} 5: "基金经理,投资年限%s年,经验丰富;投资能力较强,生涯中共管理过%s只基金,历任的%s只基金平均业绩在同类中处于上游水平,其中%s只排名在前%s;生涯年化回报率%s,同期大盘只有%s;"}
...@@ -1060,14 +1077,13 @@ class PortfolioDiagnose(object): ...@@ -1060,14 +1077,13 @@ class PortfolioDiagnose(object):
return radar_data return radar_data
portfolio = ['HF00002JJ2', 'HF00005DBQ', 'HF0000681Q', 'HF00006693', 'HF00006AZF', 'HF00006BGS'] # test_portfolio = ['HF00002JJ2', 'HF00005DBQ', 'HF0000681Q', 'HF00006693', 'HF00006AZF', 'HF00006BGS']
portfolio_diagnose = PortfolioDiagnose(client_type=1, portfolio=portfolio, invest_amount=10000000) # portfolio_diagnose = PortfolioDiagnose(client_type=3, portfolio=test_portfolio, invest_amount=10000000)
portfolio_diagnose.optimize() # portfolio_diagnose.optimize()
if __name__ == '__main__': # if __name__ == '__main__':
print(portfolio_diagnose.single_fund_radar()) # print(portfolio_diagnose.single_fund_radar())
print(portfolio_diagnose.propose_fund_radar()) # print(portfolio_diagnose.propose_fund_radar())
print(portfolio_diagnose.old_portfolio_evaluation()) # print('旧组合相关性:', portfolio_diagnose.old_correlation)
print('旧组合相关性:', portfolio_diagnose.old_correlation) # print('新组合相关性:', portfolio_diagnose.new_correlation)
print('新组合相关性:', portfolio_diagnose.new_correlation) # print('旧组合个基评价:', portfolio_diagnose.old_portfolio_evaluation())
print('旧组合个基评价:', portfolio_diagnose.old_portfolio_evaluation()) # print('新组合个基评价:', portfolio_diagnose.propose_fund_evaluation())
print('新组合个基评价:', portfolio_diagnose.propose_fund_evaluation())
\ No newline at end of file
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
import logging import logging
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
from app.api.engine import tamp_fund_engine, TAMP_SQL from app.api.engine import tamp_fund_engine, TAMP_SQL, tamp_product_engine
from app.utils.week_evaluation import * from app.utils.week_evaluation import *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment