Commit 73060184 authored by 赵杰's avatar 赵杰

新版本推荐基金,支持ifa导入基金

parent 0fecd195
......@@ -29,6 +29,8 @@ class UserCustomerDataAdaptor:
end_date = ""
group_data = {}
trade_cal_date = None
all_fund_id_list = None
all_fund_type_dict = None
all_fund_distribution = {}
all_fund_performance = {}
......@@ -38,7 +40,7 @@ class UserCustomerDataAdaptor:
self.compare_index_id = index_id
self.valueSex = ''
p_end_date = pd.to_datetime(end_date).date()
p_end_date = datetime.date(year=p_end_date.year, month=p_end_date.month, day=1) - datetime.timedelta(days=1)
# p_end_date = datetime.date(year=p_end_date.year, month=p_end_date.month, day=1) - datetime.timedelta(days=1)
self.end_date = pd.to_datetime(str(p_end_date))
# self.end_date = pd.to_datetime("2020-12-25")
p_start_date = datetime.date(year=p_end_date.year, month=p_end_date.month, day=1)
......@@ -104,6 +106,20 @@ class UserCustomerDataAdaptor:
if len(fund_df) > 0:
product_df = product_df.append(fund_df)
product_df = product_df.drop_duplicates("fund_id")
fund_type3 = order_df[order_df["type"] == 3]
if len(fund_type3) > 0:
type3_fund_id = list(fund_type3["fund_id"].unique())
fund_list_str_3 = str(type3_fund_id).replace("[", "(").replace("]", ")")
sql_fund_3 = "select distinct `id`, `fund_name`, `substrategy` from `ifa_imported_fund_info` where `id` in {}".format(
fund_list_str_3)
cur = tamp_fund_session.execute(sql_fund_3)
data = cur.fetchall()
fund_df_3 = pd.DataFrame(list(data), columns=['fund_id', 'fund_name', 'substrategy'])
fund_df_3["freq"] = 1
if len(fund_df_3) > 0:
product_df = product_df.append(fund_df_3)
product_df = product_df.drop_duplicates("fund_id")
user_customer_order_df = order_df.set_index('fund_id').join(product_df.set_index('fund_id')).reset_index()
user_customer_order_df["confirm_share_date"] = user_customer_order_df["confirm_share_date"].apply(lambda x: pd.to_datetime(x.date()))
......@@ -168,6 +184,10 @@ class UserCustomerDataAdaptor:
sql = """select distinct `price_date`, `nav`,`cumulative_nav` from `fund_nav` where `fund_id`='{}' order by `price_date` ASC""".format(
cur_fund_id)
cur = tamp_product_session.execute(sql)
elif fund_type == 3:
sql = """select distinct `price_date`, `nav`,`cumulative_nav` from `ifa_imported_fund_nav` where `fund_id`='{}' order by `price_date` ASC""".format(
cur_fund_id)
cur = tamp_fund_session.execute(sql)
data = cur.fetchall()
cur_fund_nav_df = pd.DataFrame(list(data), columns=['price_date', 'nav', 'cnav'])
......@@ -342,6 +362,8 @@ class UserCustomerDataAdaptor:
cnav_df = p_cnav_df[p_cnav_df.index >= start_date].copy()
p_fund_id_list = list(p_order_df["fund_id"].unique())
self.all_fund_id_list = p_fund_id_list
self.all_fund_type_dict = {values["fund_id"]: values["type"] for values in p_order_df[["fund_id", "type"]].drop_duplicates().to_dict(orient='records')}
for p_fund_id in p_fund_id_list:
order_min_date = p_order_df[p_order_df["fund_id"] == p_fund_id]["confirm_share_date"].min()
if pd.to_datetime(order_min_date) > start_date:
......
......@@ -7,7 +7,7 @@
import warnings
warnings.filterwarnings("ignore")
from itertools import combinations
from app.utils.fund_rank import *
from app.utils.risk_parity import *
from app.pypfopt import risk_models
......@@ -246,33 +246,46 @@ def get_tamp_fund():
return df
def get_tamp_nav(fund, start_date, rollback=False, invest_type='public'):
def get_tamp_nav(fund, start_date, rollback=False, invest_type=2):
"""获取基金ID为fund, 起始日期为start_date, 终止日期为当前日期的基金净值表
Args:
fund[str]:基金ID
start_date[date]:起始日期
rollback[bool]:当起始日期不在净值公布日历中,是否往前取最近的净值公布日
public[bool]:是否为公募
invest_type[num]:0:公募 1:私募 2:优选
Returns:df[DataFrame]: 索引为净值公布日, 列为复权净值的净值表; 查询失败则返回None
"""
with TAMP_SQL(tamp_product_engine) as tamp_product:
with TAMP_SQL(tamp_product_engine) as tamp_product, TAMP_SQL(tamp_fund_engine) as tamp_fund:
tamp_product_session = tamp_product.session
if invest_type == "private":
sql = "SELECT fund_id, price_date, cumulative_nav FROM fund_nav " \
"WHERE fund_id='{}'".format(fund)
# df = pd.read_sql(sql, con).dropna(how='any')
tamp_fund_session = tamp_fund.session
# if invest_type == "private":
# sql = "SELECT fund_id, price_date, cumulative_nav FROM fund_nav " \
# "WHERE fund_id='{}'".format(fund)
# # df = pd.read_sql(sql, con).dropna(how='any')
# cur = tamp_product_session.execute(sql)
if invest_type == 0:
sql = """select distinct `id`, `end_date`, `accum_nav` from `public_fund_nav` where `id`='{}' order by `end_date` ASC""".format(
fund)
cur = tamp_fund_session.execute(sql)
elif invest_type == 1:
sql = """select distinct `fund_id`, `price_date`,`cumulative_nav` from `fund_nav` where `fund_id`='{}' order by `price_date` ASC""".format(
fund)
cur = tamp_fund_session.execute(sql)
elif invest_type == 2:
sql = """select distinct `fund_id`,`price_date`,`cumulative_nav` from `fund_nav` where `fund_id`='{}' order by `price_date` ASC""".format(
fund)
cur = tamp_product_session.execute(sql)
elif invest_type == 3:
sql = """select distinct `fund_id`,`price_date`,`cumulative_nav` from `ifa_imported_fund_nav` where `fund_id`='{}' order by `price_date` ASC""".format(
fund)
cur = tamp_fund_session.execute(sql)
data = cur.fetchall()
df = pd.DataFrame(data, columns=['fund_id', 'price_date', 'cumulative_nav']).dropna(how='any')
df.rename({'price_date': 'end_date', 'cumulative_nav': 'adj_nav'}, axis=1, inplace=True)
# if df2['adj_nav'].count() == 0:
# logging.log(logging.ERROR, "CAN NOT FIND {}".format(fund))
# return None
df['end_date'] = pd.to_datetime(df['end_date'])
if rollback and df['end_date'].min() < start_date < df['end_date'].max():
......@@ -324,7 +337,17 @@ def get_radar_data(fund):
{'name': '综合评分', 'data': '%.2f' % total_score}]}
def get_fund_name(fund):
def get_fund_name(fund, fund_type=1):
if fund_type == 0:
with TAMP_SQL(tamp_fund_engine) as tamp_fund:
tamp_fund_session = tamp_fund.session
sql = "SELECT name FROM public_fund_basic WHERE ts_code='{}'".format(fund)
# df = pd.read_sql(sql, con)
cur = tamp_fund_session.execute(sql)
data = cur.fetchall()
df = pd.DataFrame(list(data), columns=['fund_short_name'])
return df
elif fund_type == 1 or fund_type == 2:
with TAMP_SQL(tamp_fund_engine) as tamp_fund:
tamp_fund_session = tamp_fund.session
sql = "SELECT fund_short_name FROM fund_info WHERE id='{}'".format(fund)
......@@ -342,18 +365,17 @@ def get_fund_name(fund):
df = pd.DataFrame(list(data), columns=['fund_short_name'])
return df
return df
def get_index_name(index_id):
else:
with TAMP_SQL(tamp_fund_engine) as tamp_fund:
tamp_fund_session = tamp_fund.session
sql = "SELECT `name` FROM index_basic WHERE `ts_code`='{}'".format(index_id)
sql = "SELECT fund_name FROM ifa_imported_fund_info WHERE id='{}'".format(fund)
# df = pd.read_sql(sql, con)
cur = tamp_fund_session.execute(sql)
data = cur.fetchall()
df = pd.DataFrame(list(data), columns=['index_name'])
df = pd.DataFrame(list(data), columns=['fund_short_name'])
return df
# 获取排名信息
fund_rank = get_fund_rank()
# 获取探普产品池
......@@ -379,7 +401,8 @@ class PortfolioDiagnose(object):
self.freq_list = []
self.client_type = client_type
self.portfolio = portfolio
self.portfolio = list(portfolio.keys())
self.portfolio_dict = portfolio
self.expect_return = expect_return
self.expect_drawdown = expect_drawdown
self.index_id = index_id
......@@ -415,11 +438,11 @@ 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.portfolio_dict[self.portfolio[0]])
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 or prod.index[-1] - prod.index[0] < 0.6 * (self.end_date - self.start_date):
while prod is None:
# 获取的净值表为空时首先考虑基金净值数据不足半年,查找同一基金经理下的相同二级策略的基金ID作替换
result = fund_info[fund_info['fund_id'] == self.portfolio[0]]
if result.empty:
......@@ -438,7 +461,7 @@ class PortfolioDiagnose(object):
# 替换基金数据为空则记录当前基金为找不到数据的基金, 继续尝试获取下一个基金ID的净值表
self.no_data_fund.append(self.portfolio[0])
self.portfolio.pop(0)
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.portfolio_dict[self.portfolio[0]])
# 记录基金的公布频率
self.freq_list.append(get_frequency(prod))
......@@ -446,7 +469,7 @@ class PortfolioDiagnose(object):
# 循环拼接基金净值表构建组合
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.portfolio_dict[self.portfolio[idx+1]])
# if prod1 is None or prod1.index[-1] - prod1.index[0] < 0.6 * (self.end_date - self.start_date):
if prod1 is None:
......@@ -477,12 +500,15 @@ class PortfolioDiagnose(object):
# 对所有合并后的基金净值表按最大周期进行重采样
prod.sort_index(inplace=True)
prod = prod.astype(float).interpolate()
prod.bfill(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))
if 'cal_date' in prod.columns:
prod.drop(labels='cal_date', inplace=True, axis=1)
if 'end_date' in prod.columns:
prod.drop(labels='end_date', inplace=True, axis=1)
prod.fillna(method='bfill', inplace=True)
prod.dropna(how='any', inplace=True)
return prod
......@@ -495,7 +521,7 @@ class PortfolioDiagnose(object):
Returns: 剔除建议替换基金的组合净值表
"""
self.old_correlation = cal_correlation(prod)
self.old_correlation = cal_correlation(prod.fillna(method='bfill'))
for fund in prod.columns:
z_score = search_rank(fund_rank, fund, metric='z_score')
......@@ -508,9 +534,43 @@ class PortfolioDiagnose(object):
self.abandon_fund_corr.append(fund)
prod = prod.drop(self.abandon_fund_score + self.abandon_fund_corr, axis=1)
if prod.empty:
prod = pd.DataFrame()
self.freq_list = []
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.index = self.old_correlation.index.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, self.portfolio_dict[x]).values[0][0])
self.old_correlation.index = self.old_correlation.index.map(lambda x: get_fund_name(x, self.portfolio_dict[x]).values[0][0])
return prod
def product_filter(self, candidate_funds, prod):
for proposal in candidate_funds:
s_date = cal_date(self.end_date, 'Y', 1)
proposal_nav = get_tamp_nav(proposal, s_date, invest_type=2)
# 忽略净值周期大于周更的产品
# if get_frequency(proposal_nav) <= 52:
# continue
self.freq_list.append(get_frequency(proposal_nav))
proposal_nav = rename_col(proposal_nav, proposal)
# 按最大周期进行重采样,计算新建组合的相关性
if prod.empty:
temp = proposal_nav
else:
temp = pd.merge(prod, proposal_nav, how='outer', on='end_date').astype(float)
temp.sort_index(inplace=True)
temp.ffill(inplace=True)
temp = resample(temp, get_trade_cal(), min(self.freq_list))
temp_correlation = cal_correlation(temp)
judge_correlation = temp_correlation.fillna(0)
if np.all(judge_correlation < 0.8):
# self.proposal_fund.append(proposal)
prod = temp
else:
self.freq_list.pop(-1)
return prod
def proposal(self, prod):
......@@ -522,24 +582,6 @@ class PortfolioDiagnose(object):
Returns: 增加建议申购基金的组合净值表
"""
# 组合内已包含的策略
# included_strategy = set()
# 按每种基金最少投资100w确定组合包含的最大基金数量
max_len = min(len(self.portfolio) - len(prod.columns), self.invest_amount/1e6)
# 排名表内包含的所有策略
# all_strategy = set(fund_rank['substrategy'].to_list())
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_risk = set([get_risk_level(search_rank(fund_rank, fund, metric='substrategy'))
for fund in prod.columns])
# 待添加策略为所有策略-组合已包含策略
# add_strategy = all_strategy - included_strategy
add_risk = all_risk - included_risk
candidate_funds = tamp_fund['fund_id'].to_list()
candidate_info = []
for proposal in candidate_funds:
......@@ -556,77 +598,13 @@ class PortfolioDiagnose(object):
# candidate_low_risk = [i[0] for i in list(filter(lambda x: x[2] == 'L', candidate_info))]
candidate_funds = [i[0] for i in candidate_info]
# 遍历产品池,推荐得分>80且与组合内其他基金相关度低于0.8的属于待添加策略的基金
# for proposal in candidate_funds:
# proposal_strategy = fund_rank[fund_rank['fund_id'] == proposal]['substrategy'].values[0]
# if get_risk_level(proposal_strategy) in add_risk or not add_risk:
# # if proposal_z_score > 80:
# proposal_nav = get_tamp_nav(proposal, self.start_date, invest_type=self.invest_type)
# # 忽略净值周期大于周更的产品
# # if get_frequency(proposal_nav) <= 52:
# # continue
#
# self.freq_list.append(get_frequency(proposal_nav))
# proposal_nav = rename_col(proposal_nav, proposal)
#
# # 按最大周期进行重采样,计算新建组合的相关性
# prod = pd.merge(prod, proposal_nav, how='outer', on='end_date').astype(float)
# prod.sort_index(inplace=True)
# prod.ffill(inplace=True)
# prod = resample(prod, get_trade_cal(), min(self.freq_list))
#
# self.new_correlation = cal_correlation(prod)
# judge_correlation = self.new_correlation.fillna(0)
#
# if np.all(judge_correlation < 0.8):
# self.proposal_fund.append(proposal)
# max_len -= 1
# # add_strategy -= {proposal_strategy}
# add_risk -= {get_risk_level(proposal_strategy)}
# # if len(add_strategy) == 0 or max_len == 0:
# if max_len == 0:
# break
# else:
# prod.drop(columns=proposal, inplace=True)
# 遍历产品池,推荐得分>80且与组合内其他基金相关度低于0.8的属于待添加策略的基金
for proposal in candidate_funds:
proposal_nav = get_tamp_nav(proposal, self.start_date, invest_type=self.invest_type)
# 忽略净值周期大于周更的产品
# if get_frequency(proposal_nav) <= 52:
# continue
self.freq_list.append(get_frequency(proposal_nav))
proposal_nav = rename_col(proposal_nav, proposal)
# 按最大周期进行重采样,计算新建组合的相关性
temp = pd.merge(prod, proposal_nav, how='outer', on='end_date').astype(float)
temp.sort_index(inplace=True)
temp.ffill(inplace=True)
temp = resample(temp, get_trade_cal(), min(self.freq_list))
if "cal_date" in temp.columns:
temp.drop(labels=['cal_date', 'end_date'], axis=1, inplace=True)
self.new_correlation = cal_correlation(temp)
judge_correlation = self.new_correlation.fillna(0)
if np.all(judge_correlation < 0.8):
self.proposal_fund.append(proposal)
max_len -= 1
# add_strategy -= {proposal_strategy}
add_risk -= {get_risk_level(proposal_strategy)}
# if len(add_strategy) == 0 or max_len == 0:
prod = temp
if max_len == 0:
break
prod = self.product_filter(candidate_funds, prod)
prod.dropna(how='all', inplace=True)
prod.fillna(method='bfill', inplace=True)
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.index = self.new_correlation.index.map(lambda x: get_fund_name(x).values[0][0])
return prod
def optimize(self, ):
import time
start = time.time()
......@@ -636,72 +614,101 @@ class PortfolioDiagnose(object):
self.abandoned_portfolio = self.abandon(self.origin_portfolio)
end2 = time.time()
print("计算换仓基金时间:", end2 - end1)
self.propose_portfolio = self.proposal(self.abandoned_portfolio)
# self.propose_portfolio = self.proposal(self.abandoned_portfolio)
prod = self.proposal(self.abandoned_portfolio)
end3 = time.time()
print("遍历产品池获取候选推荐时间:", end3 - end2)
# propose_portfolio.to_csv('test_portfolio.csv', encoding='gbk')
mu = [search_rank(fund_rank, x, 'annual_return') for x in self.propose_portfolio.columns]
S = risk_models.sample_cov(self.propose_portfolio, frequency=min(self.freq_list))
dd = [search_rank(fund_rank, x, 'max_drawdown') for x in self.propose_portfolio.columns]
# if self.client_type == 1:
# proposal_risk = [[x, get_risk_level(search_rank(fund_rank, x, metric='substrategy'))] for x in
# propose_portfolio.columns]
# self.proposal_fund = list(filter(lambda x: x[1] != 'H', proposal_risk))
# drop_fund_list = list(filter(lambda x: x[1] = 'H', proposal_risk))
# proposal_portfolio = list((set(self.portfolio) - set(self.no_data_fund) - set(self.replace_pair.keys())) | \
# (set(self.proposal_fund) | set(self.replace_pair.values())))
# propose_portfolio.drop()
prod_risk_zip = []
for fund in prod.columns:
prod_risk_zip.append((fund, str(get_risk_level(search_rank(fund_rank, fund, metric='substrategy')))))
propose_risk_mapper = dict()
for fund in self.propose_portfolio.columns:
for fund in prod.columns:
propose_risk_mapper[fund] = str(get_risk_level(search_rank(fund_rank, fund, metric='substrategy')))
if self.client_type == 1:
risk_upper = {"M": 0.4, "H": 0.0}
risk_lower = {"L": 0.6}
self.expect_return = 0.12
self.expect_return = 0.08
self.expect_drawdown = 0.03
prod_high_risk = [i[0] for i in list(filter(lambda x: x[1] == 'H', prod_risk_zip))]
prod.drop(columns=prod_high_risk, axis=1, inplace=True)
elif self.client_type == 2:
risk_upper = {"H": 0.2}
risk_lower = {"L": 0.5, "M": 0.3}
self.expect_return = 0.15
self.expect_return = 0.10
self.expect_drawdown = 0.05
elif self.client_type == 3:
risk_upper = {"L": 0.3, "H": 0.3}
risk_lower = {"M": 0.4}
self.expect_return = 0.18
self.expect_return = 0.12
self.expect_drawdown = 0.08
elif self.client_type == 4:
risk_upper = {"L": 0.2, "M": 0.4}
risk_lower = {"H": 0.4}
self.expect_return = 0.15
self.expect_drawdown = 0.20
self.expect_drawdown = 0.10
elif self.client_type == 5:
risk_upper = {"L": 0.0, "M": 0.4}
risk_lower = {"H": 0.6}
self.expect_return = 0.25
self.expect_drawdown = 0.15
else:
risk_upper = {"H": 1.0}
risk_lower = {"L": 0.0}
raise ValueError
self.expect_return = 0.20
self.expect_drawdown = 0.20
prod_low_risk = [i[0] for i in list(filter(lambda x: x[1] == 'L', prod_risk_zip))]
prod.drop(columns=prod_low_risk, axis=1, inplace=True)
candidate_funds = list((set(prod.columns) - set(self.no_data_fund) - set(self.replace_pair.keys())) |
set(self.replace_pair.values()))
print(candidate_funds)
max_len = int(self.invest_amount / 1e6)
w_low = 1000000.0 / self.invest_amount
weights_sharp_list = []
for i in range(1, max_len):
proposal_fund_combinations = list(combinations(candidate_funds, r=i))
for proposal_funds in proposal_fund_combinations:
drop_funds = list(set(candidate_funds) - set(proposal_funds))
temp = prod.drop(columns=drop_funds, axis=1)
mu = [search_rank(fund_rank, x, 'annual_return') for x in temp.columns]
S = risk_models.sample_cov(temp, frequency=min(self.freq_list))
dd = [search_rank(fund_rank, x, 'max_drawdown') for x in temp.columns]
w_low = 1000000 / self.invest_amount
try:
ef = EfficientFrontier(mu, S, weight_bounds=[w_low, 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.efficient_return(target_return=self.expect_return, target_drawdown=self.expect_drawdown)
# ef.efficient_return(target_return=self.expect_return, target_drawdown=self.expect_drawdown)
ef.efficient_drawdown(drawdown_limit=self.expect_drawdown)
clean_weights = ef.clean_weights()
ef.portfolio_performance(verbose=True)
self.new_weights = np.array(list(clean_weights.values()))
mu, sigma, sharp = ef.portfolio_performance(verbose=True)
# self.new_weights = np.array(list(clean_weights.values()))
weights_sharp_list.append([clean_weights, sharp])
# 保留的基金是否必须在新组合中
# if len(set(clean_weights.keys) | set(maintain_funds)) == len(set(clean_weights.keys)):
# print(clean_weights)
break
except:
self.new_weights = np.asarray([1/len(self.propose_portfolio.columns)] * len(self.propose_portfolio.columns))
continue
# self.new_weights = np.asarray([1/len(self.propose_portfolio.columns)] * len(self.propose_portfolio.columns))
weights_sharp_list.sort(key=lambda x: x[1], reverse=True)
print(weights_sharp_list)
max_sharp_weights = weights_sharp_list[0][0]
self.proposal_fund = list(max_sharp_weights.keys())
self.propose_portfolio = prod.filter(items=self.proposal_fund)
self.propose_portfolio.fillna(method="bfill", inplace=True)
self.propose_portfolio.fillna(method="ffill", inplace=True)
self.new_weights = np.array(list(max_sharp_weights.values()))
self.new_correlation = cal_correlation(self.propose_portfolio)
# self.new_correlation = self.new_correlation[self.new_correlation > 0.8] = np.random.uniform(0.75, 0.78)
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.index = self.new_correlation.index.map(lambda x: get_fund_name(x).values[0][0])
print(self.new_weights)
end4 = time.time()
print("模型计算一次时间:", end4 - end3)
# S = np.asmatrix(S)
......@@ -1071,9 +1078,9 @@ class PortfolioDiagnose(object):
# 在保留{}的基础上,建议赎回{},并增配{}后,整体组合波动率大幅降低,最大回撤从{}降到不足{},年化收益率提升{}个点
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, self.portfolio_dict[x]).values[0][0] for x in hold_fund]
abandon_fund = (self.abandon_fund_score + self.abandon_fund_corr)
abandon_fund_name = [get_fund_name(x).values[0][0] for x in abandon_fund]
abandon_fund_name = [get_fund_name(x, self.portfolio_dict[x]).values[0][0] for x in abandon_fund]
proposal_fund = self.proposal_fund
proposal_fund_name = [get_fund_name(x).values[0][0] for x in proposal_fund]
......@@ -1098,7 +1105,7 @@ class PortfolioDiagnose(object):
repr(e)
return None, None, None, None, None
def single_evaluation(self, fund_id, objective=False):
def single_evaluation(self, fund_id, fund_id_type=2, objective=False):
"""
1、该基金整体表现优秀/良好/一般,收益能力优秀/良好/合格/较差,回撤控制能力优秀/良好/合格/较差,风险收益比例较高/一般/较低;
2、在收益方面,该基金年化收益能力高于/持平/低于同类基金平均水平,有x%区间跑赢大盘/指数,绝对收益能力优秀/一般;
......@@ -1123,7 +1130,10 @@ class PortfolioDiagnose(object):
z_score < 70], [0, 1, 2]).item()
index_return_monthly = get_index_monthly(self.index_id, self.start_date)
fund_nav = get_tamp_nav(fund_id, self.start_date, invest_type=self.invest_type)
t_type = self.portfolio_dict.get(fund_id, None)
if t_type is not None:
fund_id_type = t_type
fund_nav = get_tamp_nav(fund_id, self.start_date, invest_type=fund_id_type)
fund_nav_monthly = fund_nav.groupby([fund_nav.index.year, fund_nav.index.month]).tail(1)
fund_nav_monthly = rename_col(fund_nav_monthly, fund_id)
fund_return_monthly = simple_return(fund_nav_monthly[fund_id].astype(float))
......@@ -1262,7 +1272,7 @@ class PortfolioDiagnose(object):
def propose_fund_evaluation(self, ):
try:
result = []
for fund in self.proposal_fund:
for fund in self.propose_portfolio.columns:
result.append(self.single_evaluation(fund))
return result
except Exception as e:
......
......@@ -19,8 +19,8 @@ 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 __init__(self, user_id, customer_id):
super().__init__(user_id, customer_id)
# 组合结果数据
def calculate_group_result_data(self):
......@@ -57,6 +57,11 @@ class UserCustomerResultAdaptor(UserCustomerDataAdaptor):
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)
if resample_df.index.values[-1] > self.end_date:
last = resample_df.index.values[-1]
resample_df["index_date"] = resample_df.index
resample_df.loc[last, "index_date"] = self.end_date
resample_df.set_index("index_date", inplace=True)
resample_df = resample_df[resample_df.index <= self.end_date]
......@@ -87,7 +92,7 @@ class UserCustomerResultAdaptor(UserCustomerDataAdaptor):
# 波动率
volatility_ = volatility(resample_df["cum_return_ratio"], n_freq)
folio_report_data["volatility"] = float(volatility_)
folio_report_data["volatility"] = float(volatility_) if not math.isnan(volatility_) else 0.0
# 最大回撤
drawdown = max_drawdown(resample_df["cum_return_ratio"])
......@@ -100,7 +105,7 @@ class UserCustomerResultAdaptor(UserCustomerDataAdaptor):
sharpe = sharpe_ratio(exc, sim, n_freq)
except ZeroDivisionError:
sharpe = 0.0
folio_report_data["sharpe"] = float(sharpe)
folio_report_data["sharpe"] = float(sharpe) if not math.isnan(sharpe) else 0.0
# 期末资产
ending_assets = cumulative_profit + total_cost
......@@ -134,6 +139,9 @@ class UserCustomerResultAdaptor(UserCustomerDataAdaptor):
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:
if len(cur_profit_ratio) < 1:
cur_year_profit_ratio = 0
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)
......@@ -226,6 +234,11 @@ class UserCustomerResultAdaptor(UserCustomerDataAdaptor):
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)
if resample_df.index.values[-1] > self.end_date:
last = resample_df.index.values[-1]
resample_df["index_date"] = resample_df.index
resample_df.loc[last, "index_date"] = self.end_date
resample_df.set_index("index_date", inplace=True)
resample_df = resample_df[resample_df.index <= self.end_date]
# 总成本
......@@ -288,6 +301,12 @@ class UserCustomerResultAdaptor(UserCustomerDataAdaptor):
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:
if len(cur_profit_ratio) < 1:
cur_year_profit_ratio = 0.0
else:
if len(cur_profit_ratio) < 1:
cur_year_profit_ratio = 0
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)
......@@ -422,7 +441,7 @@ class UserCustomerResultAdaptor(UserCustomerDataAdaptor):
# 基金持仓数据
total_market_values = p_sum_profit + p_total_amount # 月末总市值
fund_strategy_name = dict_substrategy[int(row["substrategy"])]
if "长富" in row["fund_name"] or "盈沛" in row["fund_name"] :
if "长富" in row["fund_name"] or "盈沛" in row["fund_name"]:
fund_strategy_name = "FOF"
fund_hoding_info = {"fund_strategy_name": fund_strategy_name, "fund_name": row["fund_name"]}
fund_hoding_info["confirm_date"] = row["confirm_share_date"].strftime("%Y-%m-%d")
......@@ -571,7 +590,7 @@ class UserCustomerResultAdaptor(UserCustomerDataAdaptor):
# 波动率
volatility_ = volatility(fund_nav_df["cum_return_ratio"], n_freq)
result["volatility"] = float(volatility_)
result["volatility"] = float(volatility_) if not math.isnan(volatility_) else 0.0
# 最大回撤
drawdown = max_drawdown(fund_nav_df["cum_return_ratio"])
......
......@@ -45,7 +45,6 @@ class DataIntegrate:
# 月度回报表格
self.get_month_table_return()
# 分组和计算个基点评以及新增基金等结果
def get_group_result(self):
for group_name, group_result in self.d.items():
......@@ -83,7 +82,10 @@ class DataIntegrate:
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),
if invest_amount < 10000000:
invest_amount = 10000000
portfolio_diagnose = PortfolioDiagnose(client_type=client_type, portfolio=self.user_customer.all_fund_type_dict,
invest_amount=float(invest_amount),
start_date=self.user_customer.start_date)
portfolio_diagnose.optimize()
return portfolio_diagnose
......@@ -299,7 +301,7 @@ class DataIntegrate:
template = env.get_template('/v2/monthReportV2.1.html') # 获取一个模板文件
monthReport_html = template.render(self.data).replace('None', 'none') # 渲染
# 保存 monthReport_html
# save_file = "app/html/monthReport.html"
# save_file = "app/pdf/monthReport.html"
# with open(save_file, 'w', encoding="utf-8") as f:
# f.write(monthReport_html)
......
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