Commit add80656 authored by pengxiong's avatar pengxiong

Merge branch 'dev' of http://47.100.44.39:10001/pengxiong/fund_report into dev

# Conflicts:
#	app/service/data_service.py
#	app/utils/jinjia2html.py
parents 3a23edc9 82206f51
......@@ -35,10 +35,10 @@ class UserCustomerDataAdaptor:
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)
self.end_date = pd.to_datetime(str(p_end_date))
self.end_date = pd.to_datetime("2020-12-04")
# self.end_date = pd.to_datetime("2020-12-04")
p_start_date = datetime.date(year=p_end_date.year, month=p_end_date.month, day=1)
self.month_start_date = p_start_date
self.month_start_date = pd.to_datetime("2020-11-1")
# self.month_start_date = pd.to_datetime("2020-11-01")
self.user_customer_order_df = self.get_user_customer_order_data()
self.fund_nav_total, self.fund_cnav_total = self.get_customer_fund_nav_data()
self.index_df = self.get_customer_index_nav_data()
......@@ -68,10 +68,10 @@ class UserCustomerDataAdaptor:
with TAMP_SQL(tamp_user_engine) as tamp_user, TAMP_SQL(tamp_product_engine) as tamp_product:
tamp_user_session = tamp_user.session
tamp_product_session = tamp_product.session
sql_user = """select f2.realname,f3.customer_name,fund_id,f1.order_type,f1.pay_date,f1.subscription_fee,f1.confirm_share_date,f1.confirm_share,f1.confirm_amount,f1.nav,f1.folio_name from customer_order f1, user_info f2,customer_info f3 where f2.id=f1.user_id and f3.id=f1.customer_id and f1.user_id='{}' and f1.customer_id='{}'""".format(self.user_id, self.customer_id)
sql_user = """select f1.id, f2.realname,f3.customer_name,fund_id,f1.order_type,f1.pay_date,f1.subscription_fee,f1.confirm_share_date,f1.confirm_share,f1.confirm_amount,f1.nav,f1.folio_name from customer_order f1, user_info f2,customer_info f3 where f2.id=f1.user_id and f3.id=f1.customer_id and f1.user_id='{}' and f1.customer_id='{}'""".format(self.user_id, self.customer_id)
cur = tamp_user_session.execute(sql_user)
data = cur.fetchall()
order_df = pd.DataFrame(list(data), columns=['username', 'customer_name', 'fund_id', 'order_type', 'pay_date',
order_df = pd.DataFrame(list(data), columns=['order_id', 'username', 'customer_name', 'fund_id', 'order_type', 'pay_date',
'subscription_fee', 'confirm_share_date', 'confirm_share',
'confirm_amount', 'nav', 'folio_name'])
......@@ -122,7 +122,7 @@ class UserCustomerDataAdaptor:
cur_fund_nav_df["price_date"] = pd.to_datetime(cur_fund_nav_df["price_date"])
cur_fund_nav_df.drop_duplicates(subset="price_date", keep='first', inplace=True)
cur_fund_nav_df.set_index("price_date", inplace=True)
cur_fund_nav_df = cur_fund_nav_df[cur_fund_nav_df.index.isin(all_fund_nav.index & cur_fund_nav_df.index)]
cur_fund_nav_df = cur_fund_nav_df[cur_fund_nav_df.index.isin(all_fund_nav.index)]
all_fund_nav[cur_fund_id] = cur_fund_nav_df["nav"]
all_fund_cnav[cur_fund_id] = cur_fund_nav_df["cnav"]
......@@ -186,6 +186,12 @@ class UserCustomerDataAdaptor:
diff_nav = row["nav"] - last_nav_data.values[0]
cur_cnav = last_cnav_data.values[0] + diff_nav
cnav_df.loc[confirm_share_date, cur_fund_id] = cur_cnav
else:
confirm_date_nav_data = p_nav_df[p_nav_df.index == confirm_share_date][cur_fund_id].tail(1)
confirm_date_cnav_data = p_cnav_df[p_cnav_df.index == confirm_share_date][cur_fund_id].tail(1)
diff_nav = row["nav"] - confirm_date_nav_data.values[0]
cur_cnav = confirm_date_cnav_data.values[0] + diff_nav
cnav_df.loc[confirm_share_date, cur_fund_id] = cur_cnav
cnav_df = cnav_df.dropna(axis=0, how="all").fillna(method='ffill')
for index, row in p_order_df.iterrows():
......@@ -237,6 +243,8 @@ class UserCustomerDataAdaptor:
cnav_df[p_fund_id_ + "_cum_earn"] = cnav_df[p_fund_id_ + "_earn"].cumsum().fillna(0)
cnav_df[p_fund_id_ + "_net_amount"] = cnav_df[p_fund_id_ + "_cum_earn"].apply(lambda x: Decimal(x)) + cnav_df[p_fund_id_ + "_amount"]
# cnav_df[p_fund_id_ + "_net_amount"] = cnav_df[p_fund_id_ + "_share"] * cnav_df[p_fund_id_]
# cnav_df[p_fund_id_ + "_profit_ratio"] = cnav_df[p_fund_id_ + "_earn"].apply(lambda x: Decimal(x)) / cnav_df[
# p_fund_id_ + "_net_amount"].shift()
self.group_data[p_folio] = {"result_cnav_data": cnav_df, "order_df": p_order_df}
return cnav_df
......@@ -273,6 +281,12 @@ class UserCustomerDataAdaptor:
diff_nav = row["nav"] - last_nav_data.values[0]
cur_cnav = last_cnav_data.values[0] + diff_nav
cnav_df.loc[confirm_share_date, cur_fund_id] = cur_cnav
else:
confirm_date_nav_data = p_nav_df[p_nav_df.index == confirm_share_date][cur_fund_id].tail(1)
confirm_date_cnav_data = p_cnav_df[p_cnav_df.index == confirm_share_date][cur_fund_id].tail(1)
diff_nav = row["nav"] - confirm_date_nav_data.values[0]
cur_cnav = confirm_date_cnav_data.values[0] + diff_nav
cnav_df.loc[confirm_share_date, cur_fund_id] = cur_cnav
cnav_df = cnav_df.dropna(axis=0, how="all").fillna(method='ffill')
for index, row in p_order_df.iterrows():
......@@ -314,5 +328,8 @@ class UserCustomerDataAdaptor:
# cnav_df[p_fund_id_ + "_earn"] = cnav_df[p_fund_id_ + "_earn"].apply(lambda x: float(x))
cnav_df[p_fund_id_ + "_cum_earn"] = cnav_df[p_fund_id_ + "_earn"].cumsum().fillna(0)
cnav_df[p_fund_id_ + "_net_amount"] = cnav_df[p_fund_id_ + "_cum_earn"].apply(lambda x: Decimal(x)) + cnav_df[p_fund_id_ + "_amount"]
# cnav_df[p_fund_id_ + "_profit_ratio"] = cnav_df[p_fund_id_ + "_earn"].apply(lambda x: Decimal(x)) / cnav_df[
# p_fund_id_ + "_net_amount"].shift()
# cnav_df[p_fund_id_ + "_net_amount"] = cnav_df[p_fund_id_ + "_share"] * cnav_df[p_fund_id_]
return cnav_df
# -*- coding: UTF-8 -*-
"""
@author: Zongxi.Li
@file:portfolio_diagnose.py
@time:2020/12/07
"""
from app.utils.fund_rank import *
from app.utils.risk_parity import *
from app.pypfopt import risk_models
......@@ -52,23 +58,6 @@ def replace_fund(manager, substrategy, fund_rank):
return df['fund_id'].values[0]
def get_tamp_fund():
"""获取探普产品池净值表
Returns:
"""
with TAMP_SQL(tamp_fund_engine) as tamp_fund:
tamp_fund_session = tamp_fund.session
sql = "SELECT id FROM tamp_fund_info WHERE id LIKE 'HF%'"
cur = tamp_fund_session.execute(sql)
data = cur.fetchall()
# df = pd.read_sql(sql, con)
df = pd.DataFrame(list(data), columns=['fund_id'])
# df.rename({'id': 'fund_id'}, axis=1, inplace=True)
return df
def search_rank(fund_rank, fund, metric):
"""查找基金在基金排名表中的指标
......@@ -120,18 +109,18 @@ def choose_good_evaluation(evaluation):
v2 = evaluation[2]
v3 = evaluation[3]
v4 = evaluation[4]
v5 = evaluation[5]
v5 = evaluation.get(5)
if v1[0] > 1:
del evaluation[1]
# if v2[0] > 1:
if v2:
if v2[0] > 1:
del evaluation[2]
if v3[0] > 1:
del evaluation[3]
if v4[0] != 0 or v4[1] != 0:
del evaluation[4]
if v5[0] < 3 or v5[2] > 1: # 基金经理的基金管理年限小于三年或平均业绩处于中下水平
# if v5[0] < 3 or v5[2] > 1: # 基金经理的基金管理年限小于三年或平均业绩处于中下水平
if v5:
del evaluation[5]
return evaluation
......@@ -145,8 +134,7 @@ def choose_bad_evaluation(evaluation):
if v1[0] < 2:
del evaluation[1]
# if v2[0] < 2:
if v2:
if v2[0] < 2:
del evaluation[2]
if v3[0] < 2:
del evaluation[3]
......@@ -157,13 +145,17 @@ def choose_bad_evaluation(evaluation):
def get_fund_rank():
with TAMP_SQL(tamp_fund_engine) as tamp_product:
tamp_product_session = tamp_product.session
sql = "SELECT * FROM fund_rank"
"""获取基金指标排名
:return: 基金指标排名表
"""
with TAMP_SQL(tamp_fund_engine) as tamp_fund:
tamp_fund_session = tamp_fund.session
sql = "SELECT * FROM new_fund_rank"
# df = pd.read_sql(sql, con)
# df = pd.read_csv('fund_rank.csv', encoding='gbk')
cur = tamp_product_session.execute(sql)
cur = tamp_fund_session.execute(sql)
data = cur.fetchall()
df = pd.DataFrame(list(data), columns=['index', 'fund_id', 'range_return', 'annual_return', 'max_drawdown',
'sharp_ratio', 'volatility', 'sortino_ratio', 'downside_risk',
......@@ -173,18 +165,20 @@ def get_fund_rank():
return df
def get_index_daily(index_id):
"""获取指数数据
def get_index_daily(index_id, start_date):
"""获取指数日更数据
Args:
index_id: 指数ID
start_date: 数据开始时间
Returns:与组合净值形式相同的表
"""
with TAMP_SQL(tamp_fund_engine) as tamp_product:
tamp_product_session = tamp_product.session
sql = "SELECT ts_code, trade_date, close FROM index_daily WHERE ts_code='{}'".format(index_id)
sql = "SELECT ts_code, trade_date, close FROM index_daily " \
"WHERE ts_code='{}' AND trade_date>'{}'".format(index_id, start_date)
# df = pd.read_sql(sql, con).dropna(how='any')
cur = tamp_product_session.execute(sql)
data = cur.fetchall()
......@@ -198,6 +192,49 @@ def get_index_daily(index_id):
return df
def get_index_monthly(index_id, start_date):
"""获取指数月度数据
Args:
index_id: 指数ID
start_date: 数据开始时间
Returns:与组合净值形式相同的表
"""
with TAMP_SQL(tamp_fund_engine) as tamp_fund:
tamp_fund_session = tamp_fund.session
sql = "SELECT ts_code, trade_date, pct_chg FROM index_monthly " \
"WHERE ts_code='{}' AND trade_date>'{}'".format(index_id, start_date)
# df = pd.read_sql(sql, con).dropna(how='any')
cur = tamp_fund_session.execute(sql)
data = cur.fetchall()
df = pd.DataFrame(list(data), columns=['fund_id', 'end_date', 'pct_chg'])
df['end_date'] = pd.to_datetime(df['end_date'])
df.set_index('end_date', drop=True, inplace=True)
df.sort_index(inplace=True, ascending=True)
df = rename_col(df, index_id)
return df
def get_tamp_fund():
"""获取探普产品池净值表
Returns:
"""
with TAMP_SQL(tamp_fund_engine) as tamp_fund:
tamp_fund_session = tamp_fund.session
sql = "SELECT id FROM tamp_fund_info WHERE id LIKE 'HF%'"
cur = tamp_fund_session.execute(sql)
data = cur.fetchall()
# df = pd.read_sql(sql, con)
df = pd.DataFrame(list(data), columns=['fund_id'])
# df.rename({'id': 'fund_id'}, axis=1, inplace=True)
return df
def get_tamp_nav(fund, start_date, rollback=False, invest_type='public'):
"""获取基金ID为fund, 起始日期为start_date, 终止日期为当前日期的基金净值表
......@@ -277,11 +314,11 @@ def get_radar_data(fund):
def get_fund_name(fund):
with TAMP_SQL(tamp_fund_engine) as tamp_product:
tamp_product_session = tamp_product.session
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)
# df = pd.read_sql(sql, con)
cur = tamp_product_session.execute(sql)
cur = tamp_fund_session.execute(sql)
data = cur.fetchall()
df = pd.DataFrame(list(data), columns=['fund_short_name'])
return df
......@@ -294,14 +331,17 @@ tamp_fund = get_tamp_fund()
class PortfolioDiagnose(object):
def __init__(self, client_type, portfolio, invest_amount, expect_return=None,
expect_drawdown=None, index_id='000905.SH', invest_type='private', start_date=None, end_date=None):
def __init__(self, client_type, portfolio, invest_amount, expect_return=0.2,
expect_drawdown=0.1, index_id='000905.SH', invest_type='private', start_date=None, end_date=None):
"""基金诊断
Args:
client_type: 客户类型:1:保守型, 2:稳健型, 3:平衡型, 4:成长型, 5:进取型
portfolio: 投资组合:[基金1, 基金2, 基金3...]
invest_amount: 投资金额:10000000元
expect_return: 期望收益
expect_drawdown: 期望回撤
index_id: 指数ID
invest_type: 投资类型:public, private, ...
start_date: 诊断所需净值的开始日期
end_date: 诊断所需净值的结束日期
......@@ -348,51 +388,52 @@ class PortfolioDiagnose(object):
while prod is None:
# 获取的净值表为空时首先考虑基金净值数据不足半年,查找同一基金经理下的相同二级策略的基金ID作替换
result = fund_info[fund_info['fund_id'] == portfolio[0]]
result = fund_info[fund_info['fund_id'] == self.portfolio[0]]
manager = str(result['manager'].values)
strategy = result['substrategy'].values
replaced_fund = replace_fund(manager, strategy, fund_rank)
if replaced_fund is not None:
if replaced_fund:
# 替换基金数据非空则记录替换的基金对
prod = get_nav(replaced_fund, self.start_date, invest_type=self.invest_type)
self.replace_pair[portfolio[0]] = replaced_fund
self.replace_pair[self.portfolio[0]] = replaced_fund
else:
# 替换基金数据为空则记录当前基金为找不到数据的基金, 继续尝试获取下一个基金ID的净值表
self.no_data_fund.append(portfolio[0])
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)
# 记录基金的公布频率
self.freq_list.append(get_frequency(prod))
prod = rename_col(prod, portfolio[0])
prod = rename_col(prod, self.portfolio[0])
# 循环拼接基金净值表构建组合
for idx in range(len(portfolio) - 1):
prod1 = get_tamp_nav(portfolio[idx + 1], self.start_date, invest_type=self.invest_type)
for idx in range(len(self.portfolio) - 1):
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):
result = fund_info[fund_info['fund_id'] == portfolio[idx + 1]]
result = fund_info[fund_info['fund_id'] == self.portfolio[idx + 1]]
if result['fund_manager_id'].count() != 0:
manager = str(result['fund_manager_id'].values)
substrategy = result['substrategy'].values[0]
replaced_fund = replace_fund(manager, substrategy, fund_rank)
else:
self.no_data_fund.append(portfolio[idx + 1])
self.no_data_fund.append(self.portfolio[idx + 1])
continue
if replaced_fund is not None:
if replaced_fund:
prod1 = get_nav(replaced_fund, self.start_date, invest_type=self.invest_type)
self.replace_pair[portfolio[idx + 1]] = replaced_fund
self.replace_pair[self.portfolio[idx + 1]] = replaced_fund
self.freq_list.append(get_frequency(prod1))
prod1 = rename_col(prod1, replaced_fund)
else:
self.no_data_fund.append(portfolio[idx + 1])
self.no_data_fund.append(self.portfolio[idx + 1])
continue
else:
self.freq_list.append(get_frequency(prod1))
prod1 = rename_col(prod1, portfolio[idx + 1])
prod1 = rename_col(prod1, self.portfolio[idx + 1])
# 取prod表和prod1表的并集
prod = pd.merge(prod, prod1, on=['end_date'], how='outer')
......@@ -413,8 +454,8 @@ class PortfolioDiagnose(object):
"""
self.old_correlation = cal_correlation(prod)
for fund in prod.columns:
print(fund)
z_score = search_rank(fund_rank, fund, metric='z_score')
# 建议替换得分为60或与其他基金相关度大于0.8的基金
if z_score < 60:
......@@ -476,6 +517,7 @@ class PortfolioDiagnose(object):
prod = pd.merge(prod, proposal_nav, how='outer', on='end_date').astype(float)
prod.sort_index(inplace=True)
prod.ffill(inplace=True)
prod.bfill(inplace=True)
prod = resample(prod, get_trade_cal(), min(self.freq_list))
self.new_correlation = cal_correlation(prod)
......@@ -529,14 +571,31 @@ class PortfolioDiagnose(object):
for fund in self.propose_portfolio.columns:
propose_risk_mapper[fund] = str(get_risk_level(search_rank(fund_rank, fund, metric='substrategy')))
# risk_upper = {"H": 0.0}
# risk_lower = {"L": 0.6, "M": 0.4}
w_low = 1e6 / self.invest_amount
ef = EfficientFrontier(mu, S, expected_drawdown=dd)
# ef.add_sector_constraints(propose_risk_mapper, risk_lower, risk_upper)
# weights = ef.nonconvex_objective(deviation_risk_parity, ef.cov_matrix)
ef.efficient_return(0.3)
if self.client_type == 1:
risk_upper = {"H": 1.0}
risk_lower = {"L": 0.0}
elif self.client_type == 2:
risk_upper = {"H": 1.0}
risk_lower = {"L": 0.0}
elif self.client_type == 3:
risk_upper = {"H": 1.0}
risk_lower = {"L": 0.0}
elif self.client_type == 4:
risk_upper = {"H": 1.0}
risk_lower = {"L": 0.0}
elif self.client_type == 5:
risk_upper = {"H": 1.0}
risk_lower = {"L": 0.0}
else:
risk_upper = {"H": 1.0}
risk_lower = {"L": 0.0}
raise ValueError
w_low = 1000000 / self.invest_amount
# 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)
clean_weights = ef.clean_weights()
# ef.portfolio_performance(verbose=True)
self.new_weights = np.array(list(clean_weights.values()))
......@@ -564,7 +623,7 @@ class PortfolioDiagnose(object):
# self.proposal_weights = calcu_w(w_origin, S, risk_target)
def return_compare(self):
index_data = get_index_daily(self.index_id)
index_data = get_index_daily(self.index_id, self.start_date)
index_data = pd.merge(index_data, self.propose_portfolio, how='inner', left_index=True, right_index=True)
index_return = index_data.iloc[:, :] / index_data.iloc[0, :] - 1
# origin_fund_return = origin_portfolio.iloc[:, :] / origin_portfolio.iloc[0, :] - 1
......@@ -610,7 +669,7 @@ class PortfolioDiagnose(object):
# 正收益基金数量
group_hold_data = pd.DataFrame(group_result[group_name]["group_hoding_info"])
profit_positive_num = group_hold_data[group_hold_data["profit"]>0]["profit"].count()
profit_positive_num = group_hold_data[group_hold_data["profit"] > 0]["profit"].count()
if profit_positive_num > 0:
profit_positive_evaluate = str(profit_positive_num) + "只基金取的正收益,"
else:
......@@ -690,7 +749,7 @@ class PortfolioDiagnose(object):
data_adaptor.user_customer_order_df["folio_name"] == group_name]
group_order_start_date = pd.to_datetime(group_order_df["confirm_share_date"].min())
# 原组合总市值, 区间收益, 年化收益, 波动率, 最大回撤, 夏普比率
# 原组合总市值, 区间收益, 年化收益, 波动率, 最大回撤, 夏普比率
total_asset = round(hold_info_df["market_values"].sum(), 2)
old_return = group_result_data["cumulative_return"]
old_return_ratio_year = group_result_data["return_ratio_year"]
......@@ -760,14 +819,11 @@ class PortfolioDiagnose(object):
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]
except:
new_return_ratio = 0
new_return_ratio = propose_fund_return_limit_data["new_return"].values[-1]
# 新组合区间年化收益率
freq_max = group_order_df["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(propose_fund_return_limit_data["new_return"].values[-1], propose_fund_return_limit_data, n_freq)
# 新组合波动率
new_volatility = volatility(propose_fund_return_limit_data["new_return"]+1, n_freq)
......@@ -866,6 +922,16 @@ class PortfolioDiagnose(object):
70 <= z_score < 80,
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)
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))
index_return_monthly.index = index_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)
fund_win_rate = ((compare[fund_id] - compare['pct_chg']) > 0).sum()
return_rank = search_rank(fund_rank, fund_id, metric='annual_return_rank')
return_level = np.select([return_rank >= 0.8,
0.7 <= return_rank < 0.8,
......@@ -889,7 +955,7 @@ class PortfolioDiagnose(object):
sharp_rank < 0.6], [0, 1, 2]).item()
data = {1: [total_level, return_level, drawdown_level, sharp_level],
2: [return_triple, "12", return_bool],
2: [return_triple, str(fund_win_rate), return_bool],
3: [drawdown_triple, drawdown_triple, format(drawdown_value, '.2%'), drawdown_triple],
4: [return_bool, drawdown_bool, drawdown_bool, return_bool, drawdown_bool]}
......@@ -926,7 +992,7 @@ class PortfolioDiagnose(object):
sentence = {
1: "该基金整体表现%s,收益能力%s,回撤控制能力%s,风险收益比例%s;\n",
2: "在收益方面,该基金年化收益能力%s同类基金平均水平,有%s区间跑赢指数,绝对收益能力%s;\n",
2: "在收益方面,该基金年化收益能力%s同类基金平均水平,有%s区间跑赢指数,绝对收益能力%s;\n",
3: "在风险方面,该基金抵御风险能力%s,在同类基金中处于%s等水平,最大回撤为%s,%s同类基金平均水平;\n",
4: "该基金收益%s的同时回撤%s,也就是说,该基金在用%s风险换取%s收益,存在%s风险;\n",
5: "基金经理,投资年限%s年,经验丰富;投资能力较强,生涯中共管理过%s只基金,历任的%s只基金平均业绩在同类中处于上游水平,其中%s只排名在前%s;生涯年化回报率%s,同期大盘只有%s;"}
......@@ -948,6 +1014,14 @@ class PortfolioDiagnose(object):
ret.append(single_sentence)
i += 1
fund_name = get_fund_name(fund_id).values[0][0]
if not ret:
try:
default_evaluation = pd.read_csv("evaluation.csv", encoding='utf-8', names=['fund_id', 'eval'])
ret.append('1、' + default_evaluation[default_evaluation['fund_id'] == fund_id]['eval'].values[0])
except Exception:
pass
evaluation_dict = {'name': fund_name, 'data': ret}
if fund_id in self.abandon_fund_score + self.abandon_fund_corr:
evaluation_dict['status'] = "换仓"
......@@ -985,15 +1059,15 @@ class PortfolioDiagnose(object):
radar_data.append(get_radar_data(fund))
return radar_data
portfolio = ['HF00002JJ2', 'HF00005DBQ', 'HF0000681Q', 'HF00006693', 'HF00006AZF', 'HF00006BGS']
portfolio_diagnose = PortfolioDiagnose(client_type=1, portfolio=portfolio, invest_amount=10000000)
portfolio_diagnose.optimize()
if __name__ == '__main__':
print(portfolio_diagnose.single_fund_radar())
print(portfolio_diagnose.propose_fund_radar())
print(portfolio_diagnose.old_portfolio_evaluation())
print('旧组合相关性:', portfolio_diagnose.old_correlation)
print('新组合相关性:', portfolio_diagnose.new_correlation)
print('旧组合个基评价:', portfolio_diagnose.old_portfolio_evaluation())
print('新组合个基评价:', portfolio_diagnose.propose_fund_evaluation())
\ No newline at end of file
#
# portfolio = ['HF00002JJ2', 'HF00005DBQ', 'HF0000681Q', 'HF00006693', 'HF00006AZF', 'HF00006BGS']
# portfolio_diagnose = PortfolioDiagnose(client_type=1, portfolio=portfolio, invest_amount=10000000)
# portfolio_diagnose.optimize()
# if __name__ == '__main__':
# print(portfolio_diagnose.single_fund_radar())
# print(portfolio_diagnose.propose_fund_radar())
# print(portfolio_diagnose.old_portfolio_evaluation())
# print('旧组合相关性:', portfolio_diagnose.old_correlation)
# print('新组合相关性:', portfolio_diagnose.new_correlation)
# print('旧组合个基评价:', portfolio_diagnose.old_portfolio_evaluation())
# print('新组合个基评价:', portfolio_diagnose.propose_fund_evaluation())
\ No newline at end of file
......@@ -402,11 +402,21 @@ class UserCustomerResultAdaptor(UserCustomerDataAdaptor):
# nav_net_amount_df = resample(return_ratio_df, self.trade_cal_date, freq_max)
nav_net_amount_df["sum_net_amount"] = nav_net_amount_df[fund_id_list_amount].sum(axis=1).apply(lambda x: Decimal.from_float(x))
for amount_name in fund_id_list:
price = nav_net_amount_df[amount_name].dropna()
profit = price.diff().fillna(Decimal(0))
profit_ratio_new = profit / price.shift(1)
profit_ratio_old = nav_net_amount_df[amount_name+"_profit_ratio"]
nan_index = profit_ratio_new[pd.isna(profit_ratio_new)].index
profit_ratio_new[nan_index] = profit_ratio_old[nan_index]
nav_net_amount_df[amount_name + "_profit_ratio"] = profit_ratio_new
nav_net_amount_df[amount_name+"_amount_ratio"] = nav_net_amount_df[amount_name+"_net_amount"]/(nav_net_amount_df["sum_net_amount"])
nav_net_amount_df[amount_name+"_profit_ratio_weight"] = nav_net_amount_df[amount_name+"_amount_ratio"].shift(1) * nav_net_amount_df[amount_name+"_profit_ratio"]
fund_profit_ratio = nav_net_amount_df[amount_name + "_profit_ratio"].dropna() + 1
nav_net_amount_df[amount_name + "_profit_cum_ratio_weight"] = (fund_profit_ratio.cumprod()-1)*nav_net_amount_df[amount_name+"_amount_ratio"].shift(1)
# enter_date = nav_net_amount_df[amount_name+"_profit_ratio"].dropna()
amount_ratio_shift = nav_net_amount_df[amount_name + "_amount_ratio"].shift(1)
num_va = len(amount_ratio_shift[amount_ratio_shift.values==0])
amount_ratio_shift.iloc[num_va] = amount_ratio_shift.values[num_va+1]
nav_net_amount_df[amount_name + "_profit_ratio_weight"] = amount_ratio_shift * nav_net_amount_df[amount_name + "_profit_ratio"]
nav_net_amount_df[amount_name + "_profit_cum_ratio_weight"] = (fund_profit_ratio.cumprod()-1)*amount_ratio_shift # enter_date = nav_net_amount_df[amount_name+"_profit_ratio"].dropna()
fund_id_list_profit_ratio_weight = [i + "_profit_ratio_weight" for i in fund_id_list]
nav_profit_ratio_weight = nav_net_amount_df[fund_id_list_profit_ratio_weight].copy().fillna(method='ffill')
......
......@@ -1037,27 +1037,55 @@
<table class="tss1_table">
<tr>
<td>
{%if month_rise>=0%}
本月涨幅:<span class="red">{{month_rise}}%</span>
{%else%}
本月涨幅:<span class="green">{{month_rise}}%</span>
{%endif%}
</td>
<td>
{%if now_month_income>=0%}
本月收益:<span class="red">{{now_month_income}}元</span>
{%else%}
本月收益:<span class="green">{{now_month_income}}元</span>
{%endif%}
</td>
<td>
{%if year_totoal_rate_of_return>=0%}
今年累计收益率:<span class="red">{{year_totoal_rate_of_return}}%</span>
{%else%}
今年累计收益率:<span class="green">{{year_totoal_rate_of_return}}%</span>
{%endif%}
</td>
<td>
{%if now_year_income>=0%}
今年累计收益:<span class="red">{{now_year_income}}元</span>
{%else%}
今年累计收益:<span class="green">{{now_year_income}}元</span>
{%endif%}
</td>
</tr>
<tr>
<td>
{%if totoal_rate_of_return>=0%}
成立以来累计收益率:<span class="red">{{totoal_rate_of_return}}%</span>
{%else%}
成立以来累计收益率:<span class="green">{{totoal_rate_of_return}}%</span>
{%endif%}
</td>
<td>
{%if now_annualised_return>=0%}
年化收益率:<span class="red">{{now_annualised_return}}%</span>
{%else%}
年化收益率:<span class="green">{{now_annualised_return}}%</span>
{%endif%}
</td>
<td>
{%if now_withdrawal>=0%}
最大回撤:<span class="red">{{now_withdrawal}}%</span>
{%else%}
最大回撤:<span class="green">{{now_withdrawal}}%</span>
{%endif%}
</td>
<td>
</td>
......@@ -1076,10 +1104,20 @@
投资成本:<span class="red">{{now_allocation_amount}}元</span>
</td>
<td>
{%if final_balance>=now_allocation_amount%}
期末资产:<span class="red">{{final_balance}}元</span>
{%else%}
期末资产:<span class="green">{{final_balance}}元</span>
{%endif%}
</td>
<td>
{%if total_profit>=0%}
累计盈利:<span class="red">{{total_profit}}元</span>
{%else%}
累计盈利:<span class="green">{{total_profit}}元</span>
{%endif%}
</td>
<td>
</td>
......@@ -1296,7 +1334,7 @@
<th>目标最大回撤(%)</th>
</tr>
<tr>
<td>2000</td>
<td>{{cost_of_investment}}</td>
<td>5%-10%</td>
<td>5%-10%</td>
</tr>
......@@ -1513,7 +1551,7 @@
<table border="1">
<tr>
<th colspan="5">投资本金</th>
<th colspan="4">10月业绩</th>
<th colspan="4">{{month}}月业绩</th>
<th colspan="3">累计业绩</th>
</tr>
<tr>
......
......@@ -211,11 +211,13 @@ class DataIntegrate:
'customer_name': self.customer_name,
'customer_gender': '女',
'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"),
'ifa_company': '飞度工作室',
'title': '10月综述',
'brand_name': '小飞象<br>工作室',
'customer_old': 42, 'customer_level': '平衡型',
# 'new_evaluation': self.new_evaluation,
'position_years': '5年', 'planned_allocation_amount': 2000.00,
'now_allocation_amount': self.total_cost, 'now_yield': self.now_yield, 'index_yield': self.index_yield,
'expected_yield': 20, 'now_annualised_return': self.now_annualised_return,
......@@ -272,5 +274,5 @@ class DataIntegrate:
if __name__ == '__main__':
start = time.time()
DataIntegrate(ifa_id='189138514712358912',customer_id='6716613796058116091')
DataIntegrate(ifa_id='USER_INFO15916072577875', customer_id='6716613804966817792')
print('耗时{}秒'.format(round(time.time()-start, 2)))
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