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

'暂存'

parent e85fed5b
<changelist name="在进行Update之前于_2020_12_2_20_07_取消提交了变更_[Default_Changelist]" date="1606910825708" recycled="false" toDelete="true">
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/在进行Update之前于_2020_12_2_20_07_取消提交了变更_[Default_Changelist]/shelved.patch" />
<option name="DESCRIPTION" value="在进行Update之前于 2020/12/2 20:07 取消提交了变更 [Default Changelist]" />
</changelist>
\ No newline at end of file
<changelist name="在进行Update之前于_2020_12_2_20_16_取消提交了变更_[Default_Changelist]" date="1606911402085" recycled="false" toDelete="true">
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/在进行Update之前于_2020_12_2_20_16_取消提交了变更_[Default_Changelist]/shelved.patch" />
<option name="DESCRIPTION" value="在进行Update之前于 2020/12/2 20:16 取消提交了变更 [Default Changelist]" />
</changelist>
\ No newline at end of file
<changelist name="在进行Update之前于_2020_12_2_20_17_取消提交了变更_[Default_Changelist]" date="1606911458436" recycled="false" toDelete="true">
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/在进行Update之前于_2020_12_2_20_17_取消提交了变更_[Default_Changelist]/shelved.patch" />
<option name="DESCRIPTION" value="在进行Update之前于 2020/12/2 20:17 取消提交了变更 [Default Changelist]" />
</changelist>
\ No newline at end of file
Index: app/service/portfolio_diagnose.py
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
<+>from app.utils.fund_rank import *\r\nfrom app.utils.risk_parity import *\r\nfrom app.pypfopt import risk_models\r\nfrom app.pypfopt import expected_returns\r\nfrom app.pypfopt import EfficientFrontier\r\n\r\n\r\ndef cal_correlation(prod):\r\n \"\"\"计算组合内基金相关性\r\n\r\n Args:\r\n prod: 组合净值表:索引为日期,列名为基金ID, 内容为净值\r\n\r\n Returns:屏蔽基金与自身相关性的相关矩阵,因为基金与自身相关性为1,妨碍后续高相关性基金筛选的判断\r\n\r\n \"\"\"\r\n prod_return = prod.iloc[:, :].apply(lambda x: simple_return(x))\r\n correlation = prod_return.corr()\r\n correlation = correlation.round(2)\r\n return correlation.mask(np.eye(correlation.shape[0], dtype=np.bool))\r\n\r\n\r\ndef rename_col(df, fund_id):\r\n \"\"\"将列名由adj_nav改为基金ID\r\n\r\n Args:\r\n df: 原始净值表:索引为日期,列名分别为 ”fund_id“, \"adj_nav\", 内容为[基金ID,净值]\r\n fund_id: 基金ID\r\n\r\n Returns:删除 ”fund_id” 列, 重命名 “adj_nav” 列为基金ID的净值表\r\n\r\n \"\"\"\r\n df.rename(columns={'adj_nav': fund_id}, inplace=True)\r\n df.drop('fund_id', axis=1, inplace=True)\r\n return df\r\n\r\n\r\ndef replace_fund(manager, substrategy, fund_rank):\r\n \"\"\"查找不足半年数据的基金的替代基金\r\n\r\n Args:\r\n manager: 基金经理ID\r\n substrategy: 基金二级策略\r\n fund_rank: 基金打分排名表\r\n\r\n Returns: 满足相同基金经理ID下的同种二级策略的基金ID的第一个结果\r\n\r\n \"\"\"\r\n df = fund_rank[(fund_rank['manager'] == manager) &\r\n (fund_rank['substrategy'] == substrategy)]\r\n return df['fund_id'].values[0]\r\n\r\n\r\ndef search_rank(fund_rank, fund, metric):\r\n \"\"\"查找基金在基金排名表中的指标\r\n\r\n Args:\r\n fund_rank: 基金排名表\r\n fund: 输入基金ID\r\n metric: 查找的指标名称\r\n\r\n Returns: 基金指标的值\r\n\r\n \"\"\"\r\n return fund_rank[fund_rank['fund_id'] == fund][metric].values[0]\r\n\r\n\r\ndef translate_single(content, evaluation):\r\n '''\r\n content = [[\"优秀\",\"良好\",\"一般\"],\r\n [\"优秀\",\"良好\",\"合格\",\"较差\"],\r\n [\"优秀\",\"良好\",\"合格\",\"较差\"],\r\n [\"高\",\"一般\",\"较低\"]]\r\n evaluation = [0,1,1,2]\r\n '''\r\n return tuple([content[i][v] if type(v) == int else v for i, v in enumerate(evaluation)])\r\n\r\n\r\ndef choose_good_evaluation(evaluation):\r\n \"\"\"抽取好的评价\r\n\r\n Args:\r\n evaluation: 个基的评价\r\n\r\n Returns: 个基好的评价\r\n\r\n \"\"\"\r\n v1 = evaluation[1]\r\n v2 = evaluation[2]\r\n v3 = evaluation[3]\r\n v4 = evaluation[4]\r\n v5 = evaluation[5]\r\n\r\n if v1[0] > 1:\r\n del evaluation[1]\r\n # if v2[0] > 1:\r\n if v2:\r\n del evaluation[2]\r\n if v3[0] > 1:\r\n del evaluation[3]\r\n if v4[0] != 0 or v4[1] != 0:\r\n del evaluation[4]\r\n if v5[0] < 3 or v5[2] > 1: # 基金经理的基金管理年限小于三年或平均业绩处于中下水平\r\n del evaluation[5]\r\n\r\n return evaluation\r\n\r\n\r\ndef choose_bad_evaluation(evaluation):\r\n v1 = evaluation[1]\r\n v2 = evaluation[2]\r\n v3 = evaluation[3]\r\n v4 = evaluation[4]\r\n\r\n if v1[0] < 2:\r\n del evaluation[1]\r\n # if v2[0] < 2:\r\n if v2:\r\n del evaluation[2]\r\n if v3[0] < 2:\r\n del evaluation[3]\r\n if v4[0] != 1 or v4[1] != 1:\r\n del evaluation[4]\r\n\r\n return evaluation\r\n\r\n\r\ndef get_fund_rank():\r\n sql = \"SELECT * FROM fund_rank\"\r\n df = pd.read_sql(sql, con)\r\n # df = pd.read_csv('fund_rank.csv', encoding='gbk')\r\n return df\r\n\r\n\r\ndef get_index_daily(index_id):\r\n \"\"\"获取指数数据\r\n\r\n Args:\r\n index_id: 指数ID\r\n\r\n Returns:与组合净值形式相同的表\r\n\r\n \"\"\"\r\n sql = \"SELECT ts_code, trade_date, close FROM index_daily WHERE ts_code='{}'\".format(index_id)\r\n df = pd.read_sql(sql, con).dropna(how='any')\r\n df.rename({'ts_code': 'fund_id', 'trade_date': 'end_date', 'close': 'adj_nav'}, axis=1, inplace=True)\r\n df['end_date'] = pd.to_datetime(df['end_date'])\r\n df.set_index('end_date', drop=True, inplace=True)\r\n df.sort_index(inplace=True, ascending=True)\r\n df = rename_col(df, index_id)\r\n return df\r\n\r\n\r\ndef get_tamp_fund():\r\n \"\"\"获取探普产品池净值表\r\n\r\n Returns:\r\n\r\n \"\"\"\r\n sql = \"SELECT id FROM tamp_fund_info WHERE id LIKE 'HF%'\"\r\n df = pd.read_sql(sql, con)\r\n df.rename({'id': 'fund_id'}, axis=1, inplace=True)\r\n return df\r\n\r\n\r\ndef get_risk_level(substrategy):\r\n \"\"\"获取风险类型\r\n\r\n Args:\r\n substrategy: 二级策略\r\n\r\n Returns:\r\n\r\n \"\"\"\r\n substrategy2risk = {1: \"H\",\r\n 1010: \"H\", 1020: \"H\", 1030: \"H\",\r\n 2010: \"H\",\r\n 3010: \"H\", 3020: \"L\", 3030: \"H\", 3040: \"L\", 3050: \"M\",\r\n 4010: \"M\", 4020: \"M\", 4030: \"M\", 4040: \"M\",\r\n 5010: \"M\", 5020: \"L\", 5030: \"M\",\r\n 6010: \"L\", 6020: \"M\", 6030: \"L\",\r\n 7010: \"H\", 7020: \"H\",\r\n 8010: \"H\", 8020: \"M\"}\r\n return substrategy2risk[substrategy]\r\n\r\n\r\ndef get_radar_data(fund):\r\n df = fund_rank[fund_rank['fund_id'] == fund]\r\n return_score = df['annual_return_rank'].values[0] * 100\r\n downside_score = df['downside_risk_rank'].values[0] * 100\r\n drawdown_score = df['max_drawdown_rank'].values[0] * 100\r\n sharpe_score = df['sharp_ratio_rank'].values[0] * 100\r\n total_score = df['z_score'].values[0]\r\n fund_name = get_fund_name(fund).values[0][0]\r\n # print(fund_name)\r\n return {'name': fund_name, 'data': [{'name': '绝对收益', 'data': '%.2f' % return_score},\r\n {'name': '抗风险能力', 'data': '%.2f' % downside_score},\r\n {'name': '极端风险', 'data': '%.2f' % drawdown_score},\r\n {'name': '风险调整后收益', 'data': '%.2f' % sharpe_score},\r\n {'name': '业绩持续性', 'data': '%.2f' % np.random.randint(70, 90)},\r\n {'name': '综合评分', 'data': '%.2f' % total_score}]}\r\n\r\n\r\ndef get_fund_name(fund):\r\n sql = \"SELECT fund_short_name FROM fund_info WHERE id='{}'\".format(fund)\r\n df = pd.read_sql(sql, con)\r\n return df\r\n\r\n\r\n# 获取排名信息\r\nfund_rank = get_fund_rank()\r\n# 获取探普产品池\r\ntamp_fund = get_tamp_fund()\r\n\r\n\r\nclass PortfolioDiagnose(object):\r\n def __init__(self, client_type, portfolio, invest_amount, expect_return=None,\r\n expect_drawdown=None, index_id='000905.SH', invest_type='private', start_date=None, end_date=None):\r\n \"\"\"基金诊断\r\n\r\n Args:\r\n client_type: 客户类型:1:保守型, 2:稳健型, 3:平衡型, 4:成长型, 5:进取型\r\n portfolio: 投资组合:[基金1, 基金2, 基金3...]\r\n invest_amount: 投资金额:10000000元\r\n invest_type: 投资类型:public, private, ...\r\n start_date: 诊断所需净值的开始日期\r\n end_date: 诊断所需净值的结束日期\r\n \"\"\"\r\n\r\n self.freq_list = []\r\n self.client_type = client_type\r\n self.portfolio = portfolio\r\n self.expect_return = expect_return\r\n self.expect_drawdown = expect_drawdown\r\n self.index_id = index_id\r\n self.invest_amount = invest_amount\r\n self.invest_type = invest_type\r\n self.start_date = start_date\r\n self.end_date = end_date\r\n\r\n if self.end_date is None:\r\n self.end_date = datetime.datetime.now() - datetime.timedelta(days=1)\r\n self.start_date = cal_date(self.end_date, 'Y', 1)\r\n\r\n self.replace_pair = dict() # 由于数据不足半年而被替换为相同基金经理和策略的原基金和替换基金的映射\r\n self.no_data_fund = [] # 未在数据库中找到基金净值或者基金经理记录的基金\r\n self.abandon_fund_score = [] # 打分不满足要求的基金\r\n self.abandon_fund_corr = [] # 相关性过高\r\n self.proposal_fund = [] # 建议的基金\r\n self.old_correlation = None\r\n self.new_correlation = None\r\n self.old_weights = None\r\n self.new_weights = None\r\n\r\n def get_portfolio(self, ):\r\n \"\"\"获取组合净值表\r\n\r\n Returns:\r\n\r\n \"\"\"\r\n # 获取原始投资组合的第一支基金的净值表\r\n prod = get_nav(self.portfolio[0], self.start_date, invest_type=self.invest_type)\r\n fund_info = get_fund_info(self.end_date, invest_type=self.invest_type)\r\n\r\n while prod is None:\r\n # 获取的净值表为空时首先考虑基金净值数据不足半年,查找同一基金经理下的相同二级策略的基金ID作替换\r\n result = fund_info[fund_info['fund_id'] == portfolio[0]]\r\n manager = str(result['manager'].values)\r\n strategy = result['substrategy'].values\r\n replaced_fund = replace_fund(manager, strategy, fund_rank)\r\n\r\n if replaced_fund is not None:\r\n # 替换基金数据非空则记录替换的基金对\r\n prod = get_nav(replaced_fund, self.start_date, invest_type=self.invest_type)\r\n self.replace_pair[portfolio[0]] = replaced_fund\r\n else:\r\n # 替换基金数据为空则记录当前基金为找不到数据的基金, 继续尝试获取下一个基金ID的净值表\r\n self.no_data_fund.append(portfolio[0])\r\n self.portfolio.pop(0)\r\n prod = get_nav(self.portfolio[0], self.start_date, invest_type=self.invest_type)\r\n\r\n # 记录基金的公布频率\r\n self.freq_list.append(get_frequency(prod))\r\n prod = rename_col(prod, portfolio[0])\r\n\r\n # 循环拼接基金净值表构建组合\r\n for idx in range(len(portfolio) - 1):\r\n prod1 = get_nav(portfolio[idx + 1], self.start_date, invest_type=self.invest_type)\r\n\r\n if prod1 is None or prod1.index[-1] - prod1.index[0] < 0.6 * (self.end_date - self.start_date):\r\n result = fund_info[fund_info['fund_id'] == portfolio[idx + 1]]\r\n\r\n if result['fund_manager_id'].count() != 0:\r\n manager = str(result['fund_manager_id'].values)\r\n substrategy = result['substrategy'].values[0]\r\n replaced_fund = replace_fund(manager, substrategy, fund_rank)\r\n else:\r\n self.no_data_fund.append(portfolio[idx + 1])\r\n continue\r\n\r\n if replaced_fund is not None:\r\n prod1 = get_nav(replaced_fund, self.start_date, invest_type=self.invest_type)\r\n self.replace_pair[portfolio[0]] = replaced_fund\r\n self.freq_list.append(get_frequency(prod1))\r\n prod1 = rename_col(prod1, replaced_fund)\r\n else:\r\n self.no_data_fund.append(portfolio[idx + 1])\r\n continue\r\n else:\r\n self.freq_list.append(get_frequency(prod1))\r\n prod1 = rename_col(prod1, portfolio[idx + 1])\r\n\r\n # 取prod表和prod1表的并集\r\n prod = pd.merge(prod, prod1, on=['end_date'], how='outer')\r\n\r\n # 对所有合并后的基金净值表按最大周期进行重采样\r\n prod.sort_index(inplace=True)\r\n prod.ffill(inplace=True)\r\n prod = resample(prod, get_trade_cal(), min(self.freq_list))\r\n return prod\r\n\r\n def abandon(self, prod):\r\n \"\"\"建议替换的基金\r\n\r\n Args:\r\n prod: 原始组合净值表\r\n\r\n Returns: 剔除建议替换基金的组合净值表\r\n\r\n \"\"\"\r\n self.old_correlation = cal_correlation(prod)\r\n\r\n for fund in prod.columns:\r\n z_score = search_rank(fund_rank, fund, metric='z_score')\r\n # 建议替换得分为60或与其他基金相关度大于0.8的基金\r\n if z_score < 60:\r\n self.abandon_fund_score.append(fund)\r\n prod = prod.drop(fund, axis=1)\r\n\r\n if np.any(self.old_correlation[fund] > 0.8):\r\n self.abandon_fund_corr.append(fund)\r\n prod = prod.drop(fund, axis=1)\r\n\r\n self.old_correlation = self.old_correlation.fillna(1).round(2)\r\n self.old_correlation.columns = self.old_correlation.columns.map(lambda x: get_fund_name(x).values[0][0])\r\n self.old_correlation.index = self.old_correlation.index.map(lambda x: get_fund_name(x).values[0][0])\r\n return prod\r\n\r\n def proposal(self, prod):\r\n \"\"\"建议申购基金\r\n\r\n Args:\r\n prod: 剔除建议替换基金的组合净值表\r\n\r\n Returns: 增加建议申购基金的组合净值表\r\n\r\n \"\"\"\r\n # 组合内已包含的策略\r\n # included_strategy = set()\r\n # 按每种基金最少投资100w确定组合包含的最大基金数量\r\n max_len = self.invest_amount // 1e6 - len(prod.columns)\r\n\r\n # 排名表内包含的所有策略\r\n # all_strategy = set(fund_rank['substrategy'].to_list())\r\n # if prod is not None:\r\n # included_strategy = set([search_rank(fund_rank, fund, metric='substrategy') for fund in prod.columns])\r\n\r\n # 待添加策略为所有策略-组合已包含策略\r\n # add_strategy = all_strategy - included_strategy\r\n\r\n # 遍历产品池,推荐得分>80且与组合内其他基金相关度低于0.8的属于待添加策略的基金\r\n for proposal in tamp_fund['fund_id']:\r\n\r\n if proposal in fund_rank['fund_id'].to_list():\r\n proposal_z_score = search_rank(fund_rank, proposal, metric='z_score')\r\n proposal_strategy = fund_rank[fund_rank['fund_id'] == proposal]['substrategy'].values[0]\r\n else:\r\n continue\r\n\r\n # if proposal_z_score > 80 and proposal_strategy in add_strategy:\r\n if proposal_z_score > 60:\r\n\r\n proposal_nav = get_nav(proposal, self.start_date, invest_type=self.invest_type)\r\n # 忽略净值周期大于周更的产品\r\n if get_frequency(proposal_nav) <= 52:\r\n continue\r\n\r\n self.freq_list.append(get_frequency(proposal_nav))\r\n proposal_nav = rename_col(proposal_nav, proposal)\r\n\r\n # 按最大周期进行重采样,计算新建组合的相关性\r\n prod = pd.merge(prod, proposal_nav, how='outer', on='end_date')\r\n prod.sort_index(inplace=True)\r\n prod.ffill(inplace=True)\r\n prod = resample(prod, get_trade_cal(), min(self.freq_list))\r\n\r\n self.new_correlation = cal_correlation(prod)\r\n judge_correlation = self.new_correlation.fillna(0)\r\n\r\n if np.all(judge_correlation < 0.8):\r\n self.proposal_fund.append(proposal)\r\n max_len -= 1\r\n\r\n # add_strategy -= {proposal_strategy}\r\n # if len(add_strategy) == 0 or max_len == 0:\r\n if max_len == 0:\r\n break\r\n else:\r\n prod.drop(columns=proposal, inplace=True)\r\n\r\n self.new_correlation = self.new_correlation.fillna(1).round(2)\r\n self.new_correlation.columns = self.new_correlation.columns.map(lambda x: get_fund_name(x).values[0][0])\r\n self.new_correlation.index = self.new_correlation.index.map(lambda x: get_fund_name(x).values[0][0])\r\n return prod\r\n\r\n def optimize(self, ):\r\n import time\r\n start = time.time()\r\n origin_portfolio = self.get_portfolio()\r\n end1 = time.time()\r\n print(\"原始组合数据获取时间:\", end1 - start)\r\n abandoned_portfolio = self.abandon(origin_portfolio)\r\n end2 = time.time()\r\n print(\"计算换仓基金时间:\", end2 - end1)\r\n propose_portfolio = self.proposal(abandoned_portfolio)\r\n end3 = time.time()\r\n print(\"遍历产品池获取候选推荐时间:\", end3 - end2)\r\n # propose_portfolio.to_csv('test_portfolio.csv', encoding='gbk')\r\n\r\n mu = expected_returns.mean_historical_return(propose_portfolio, frequency=min(self.freq_list))\r\n S = risk_models.sample_cov(propose_portfolio, frequency=min(self.freq_list))\r\n dd = expected_returns.drawdown_from_prices(propose_portfolio)\r\n # if self.client_type == 1:\r\n # proposal_risk = [[x, get_risk_level(search_rank(fund_rank, x, metric='substrategy'))] for x in\r\n # self.proposal_fund]\r\n # self.proposal_fund = list(filter(lambda x: x[1] != 3, proposal_risk))\r\n # proposal_portfolio = list((set(self.portfolio) - set(self.no_data_fund) - set(self.replace_pair.keys())) | \\\r\n # (set(self.proposal_fund) | set(self.replace_pair.values())))\r\n propose_risk_mapper = dict()\r\n for fund in propose_portfolio.columns:\r\n propose_risk_mapper[fund] = str(get_risk_level(search_rank(fund_rank, fund, metric='substrategy')))\r\n risk_upper = {\"H\": 0.0}\r\n risk_lower = {\"L\": 0.6, \"M\": 0.4}\r\n\r\n ef = EfficientFrontier(mu, S, expected_drawdown=dd)\r\n ef.add_sector_constraints(propose_risk_mapper, risk_lower, risk_upper)\r\n # weights = ef.nonconvex_objective(deviation_risk_parity, ef.cov_matrix)\r\n ef.efficient_drawdown(drawdown_limit=0.5)\r\n clean_weights = ef.clean_weights()\r\n # ef.portfolio_performance(verbose=True)\r\n self.new_weights = np.array(list(clean_weights.values()))\r\n print(clean_weights)\r\n end4 = time.time()\r\n print(\"模型计算一次时间:\", end4 - end3)\r\n # S = np.asmatrix(S)\r\n # w_origin = np.asarray([i for i in w_origin.values()])\r\n # risk_target = np.asarray([1 / len(w_origin)] * len(w_origin))\r\n # self.proposal_weights = calcu_w(w_origin, S, risk_target)\r\n\r\n # elif self.client_type == 2:\r\n # elif self.client_type == 3:\r\n # elif self.client_type == 4:\r\n # elif self.client_type == 5:\r\n # print(len(propose_portfolio.columns))\r\n # # 单支基金占投资额的下界为 100W/投资总额\r\n # # w_low = 1e6 / self.invest_amount\r\n # w_low = 0\r\n # w_origin, S, mu = optim_drawdown(propose_portfolio, 0.5, [w_low, 1], min(self.freq_list))\r\n # print(w_origin)\r\n # S = np.asmatrix(S)\r\n # w_origin = np.asarray([i for i in w_origin.values()])\r\n # risk_target = np.asarray([1 / len(w_origin)] * len(w_origin))\r\n # self.proposal_weights = calcu_w(w_origin, S, risk_target)\r\n\r\n def return_compare(self):\r\n index_data = get_index_daily(self.index_id)\r\n origin_portfolio = self.get_portfolio()\r\n abandoned_portfolio = self.abandon(origin_portfolio)\r\n propose_portfolio = self.proposal(abandoned_portfolio)\r\n index_data = pd.merge(index_data, propose_portfolio, how='inner', left_index=True, right_index=True)\r\n index_return = index_data.iloc[:, :] / index_data.iloc[0, :] - 1\r\n # origin_fund_return = origin_portfolio.iloc[:, :] / origin_portfolio.iloc[0, :] - 1\r\n propose_fund_return = propose_portfolio.iloc[:, :] / propose_portfolio.iloc[0, :] - 1\r\n print(self.new_weights)\r\n propose_fund_return['return'] = propose_fund_return.T.iloc[:, :].apply(lambda x: np.dot(self.new_weights, x))\r\n propose_fund_return.to_csv('new_port.csv', encoding='gbk')\r\n return index_return, propose_fund_return\r\n\r\n def old_evaluation(self):\r\n start_year = self.start_date.year\r\n start_month = self.start_date.month\r\n current_year = datetime.datetime.now().year\r\n current_month = datetime.datetime.now().month\r\n current_day = datetime.datetime.now().day\r\n past_month = (current_year - start_year) * 12 + current_month - start_month\r\n num_fund = len(self.portfolio)\r\n abandon_fund = [[x, self.invest_type] for x in self.abandon_fund_score + self.abandon_fund_corr]\r\n old_strategy = set([search_rank(fund_rank, x, metric='substrategy') for x in self.portfolio])\r\n\r\n data = [start_year, start_month, past_month, self.invest_amount,\r\n current_year, current_month, current_day]\r\n return data\r\n\r\n def new_evaluation(self):\r\n hold_fund = set(self.portfolio) - set(self.abandon_fund)\r\n abandon_fund = self.abandon_fund\r\n proposal_fund = self.proposal_fund\r\n data = [hold_fund, abandon_fund, proposal_fund]\r\n return data\r\n\r\n def single_evaluation(self, fund_id):\r\n \"\"\"\r\n 1、该基金整体表现优秀/良好/一般,收益能力优秀/良好/合格/较差,回撤控制能力优秀/良好/合格/较差,风险收益比例较高/一般/较低;\r\n 2、在收益方面,该基金年化收益能力高于/持平/低于同类基金平均水平,有x%区间跑赢大盘/指数,绝对收益能力优秀/一般;\r\n 3、在风险方面,该基金抵御风险能力优秀/良好/一般,在同类基金中处于高/中/低等水平,最大回撤为x%,高于/持平/低于同类基金平均水平;\r\n 4、该基金收益较好/较差的同时回撤较大/较小,也就是说,该基金在用较大/较小风险换取较大/较小收益,存在较高/较低风险;\r\n 5、基金经理,投资年限5.23年,经验丰富;投资能力较强,生涯中共管理过X只基金,历任的X只基金平均业绩在同类中处于上游水平,其中x只排名在前x%;生涯年化回报率x%,同期大盘只有x%;\r\n\r\n 旧个基显示1-4,新个基显示1-5。\r\n\r\n 旧个基如果是要保留的,显示好的评价。\r\n 如果是要剔除的,显示坏的评价。\r\n\r\n 新个基只显示好的评价。\r\n Args:\r\n fund_id:\r\n\r\n Returns:\r\n \"\"\"\r\n z_score = search_rank(fund_rank, fund_id, metric='z_score')\r\n total_level = np.select([z_score >= 80,\r\n 70 <= z_score < 80,\r\n z_score < 70], [0, 1, 2]).item()\r\n\r\n return_rank = search_rank(fund_rank, fund_id, metric='annual_return_rank')\r\n return_level = np.select([return_rank >= 0.8,\r\n 0.7 <= return_rank < 0.8,\r\n 0.6 <= return_rank < 0.7,\r\n return_rank < 0.6], [0, 1, 2, 3]).item()\r\n return_bool = 1 if return_level > 2 else 0\r\n return_triple = return_level - 1 if return_level >= 2 else return_level\r\n\r\n drawdown_rank = search_rank(fund_rank, fund_id, metric='max_drawdown_rank')\r\n drawdown_value = search_rank(fund_rank, fund_id, metric='max_drawdown')\r\n drawdown_level = np.select([drawdown_rank >= 0.8,\r\n 0.7 <= drawdown_rank < 0.8,\r\n 0.6 <= drawdown_rank < 0.7,\r\n drawdown_rank < 0.6], [0, 1, 2, 3]).item()\r\n drawdown_bool = 1 if drawdown_level > 2 else 0\r\n drawdown_triple = drawdown_level - 1 if drawdown_level >= 2 else drawdown_level\r\n\r\n sharp_rank = search_rank(fund_rank, fund_id, metric='sharp_ratio_rank')\r\n sharp_level = np.select([sharp_rank >= 0.8,\r\n 0.6 <= sharp_rank < 0.8,\r\n sharp_rank < 0.6], [0, 1, 2]).item()\r\n\r\n data = {1: [total_level, return_level, drawdown_level, sharp_level],\r\n 2: [return_triple, \"12\", return_bool],\r\n 3: [drawdown_triple, drawdown_triple, format(drawdown_value, '.2%'), drawdown_triple],\r\n 4: [return_bool, drawdown_bool, drawdown_bool, return_bool, drawdown_bool]}\r\n\r\n if fund_id in self.abandon_fund_score:\r\n data['remove'] = True\r\n elif fund_id in self.proposal_fund:\r\n data[5] = [1] * 7\r\n data['remove'] = False\r\n else:\r\n data['remove'] = False\r\n\r\n x = '30%'\r\n content = {\r\n # 第一个评价\r\n 1: [[\"优秀\", \"良好\", \"一般\"],\r\n [\"优秀\", \"良好\", \"合格\", \"较差\"],\r\n [\"优秀\", \"良好\", \"合格\", \"较差\"],\r\n [\"高\", \"一般\", \"较低\"]],\r\n # 第二个评价\r\n 2: [[\"高于\", \"持平\", \"低于\"],\r\n x,\r\n [\"优秀\", \"一般\"]],\r\n # 第三个评价\r\n 3: [[\"优秀\", \"良好\", \"一般\"],\r\n [\"高\", \"中\", \"低\"], x,\r\n [\"高于\", \"持平\", \"低于\"]],\r\n # 第四个评价\r\n 4: [[\"较好\", \"较差\"],\r\n [\"较小\", \"较大\"],\r\n [\"较小\", \"较小\"],\r\n [\"较大\", \"较小\"],\r\n [\"较低\", \"较高\"]],\r\n 5: [[\"TO DO\"]] * 7}\r\n\r\n sentence = {\r\n 1: \"该基金整体表现%s,收益能力%s,回撤控制能力%s,风险收益比例%s;\\n\",\r\n 2: \"在收益方面,该基金年化收益能力%s同类基金平均水平,有%s区间跑赢指数,绝对收益能力%s;\\n\",\r\n 3: \"在风险方面,该基金抵御风险能力%s,在同类基金中处于%s等水平,最大回撤为%s,%s同类基金平均水平;\\n\",\r\n 4: \"该基金收益%s的同时回撤%s,也就是说,该基金在用%s风险换取%s收益,存在%s风险;\\n\",\r\n 5: \"基金经理,投资年限%s年,经验丰富;投资能力较强,生涯中共管理过%s只基金,历任的%s只基金平均业绩在同类中处于上游水平,其中%s只排名在前%s;生涯年化回报率%s,同期大盘只有%s;\"}\r\n\r\n remove = data[\"remove\"]\r\n del data[\"remove\"]\r\n\r\n # 不剔除,选择好的话术\r\n if not remove:\r\n evaluation = choose_good_evaluation(data)\r\n # 剔除,选择坏的话术\r\n else:\r\n evaluation = choose_bad_evaluation(data)\r\n\r\n ret = \"\"\r\n for k, v in evaluation.items():\r\n # print(translate_single(content[k], v))\r\n ret = ret + sentence[k] % translate_single(content[k], v)\r\n fund_name = get_fund_name(fund_id).values[0][0]\r\n return {'name': fund_name, 'data': ret}\r\n\r\n def old_portfolio_evaluation(self, ):\r\n result = []\r\n for fund in self.portfolio:\r\n try:\r\n result.append(self.single_evaluation(fund))\r\n except IndexError:\r\n continue\r\n return result\r\n\r\n def propose_fund_evaluation(self, ):\r\n result = []\r\n for fund in self.proposal_fund:\r\n result.append(self.single_evaluation(fund))\r\n return result\r\n\r\n def single_fund_radar(self):\r\n radar_data = []\r\n for fund in self.portfolio:\r\n try:\r\n radar_data.append(get_radar_data(fund))\r\n except IndexError:\r\n continue\r\n return radar_data\r\n\r\n def propose_fund_radar(self):\r\n radar_data = []\r\n for fund in self.proposal_fund:\r\n radar_data.append(get_radar_data(fund))\r\n return radar_data\r\n\r\n\r\nportfolio = ['HF00002JJ2', 'HF00005DBQ', 'HF0000681Q', 'HF00006693', 'HF00006AZF', 'HF00006BGS']\r\nportfolio_diagnose = PortfolioDiagnose(client_type=1, portfolio=portfolio, invest_amount=10000000)\r\nportfolio_diagnose.optimize()\r\nif __name__ == '__main__':\r\n # print(portfolio_diagnose.single_fund_radar())\r\n # print(portfolio_diagnose.propose_fund_radar())\r\n # print(portfolio_diagnose.old_portfolio_evaluation())\r\n print('旧组合相关性:', portfolio_diagnose.old_correlation)\r\n print('新组合相关性:', portfolio_diagnose.new_correlation)\r\n print('旧组合个基评价:', portfolio_diagnose.old_portfolio_evaluation())\r\n print('新组合个基评价:', portfolio_diagnose.propose_fund_evaluation())\r\n
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- app/service/portfolio_diagnose.py (revision a12f0fb313aea047d7d89c3af73ed9832e157fb9)
+++ app/service/portfolio_diagnose.py (date 1606909169433)
@@ -373,7 +373,7 @@
if proposal in fund_rank['fund_id'].to_list():
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:
continue
@@ -430,24 +430,32 @@
mu = expected_returns.mean_historical_return(propose_portfolio, frequency=min(self.freq_list))
S = risk_models.sample_cov(propose_portfolio, frequency=min(self.freq_list))
dd = expected_returns.drawdown_from_prices(propose_portfolio)
+
# if self.client_type == 1:
- # proposal_risk = [[x, get_risk_level(search_rank(fund_rank, x, metric='substrategy'))] for x in
- # self.proposal_fund]
- # self.proposal_fund = list(filter(lambda x: x[1] != 3, 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())))
+ # 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()
+
+
propose_risk_mapper = dict()
for fund in 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}
+ # 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)
+ # ef.add_sector_constraints(propose_risk_mapper, risk_lower, risk_upper)
# weights = ef.nonconvex_objective(deviation_risk_parity, ef.cov_matrix)
- ef.efficient_drawdown(drawdown_limit=0.5)
+ ef.efficient_return(0.3)
clean_weights = ef.clean_weights()
- # ef.portfolio_performance(verbose=True)
+ ef.portfolio_performance(verbose=True)
self.new_weights = np.array(list(clean_weights.values()))
print(clean_weights)
end4 = time.time()
@@ -607,10 +615,10 @@
else:
evaluation = choose_bad_evaluation(data)
- ret = ""
+ ret = []
for k, v in evaluation.items():
# print(translate_single(content[k], v))
- ret = ret + sentence[k] % translate_single(content[k], v)
+ ret.append(sentence[k] % translate_single(content[k], v))
fund_name = get_fund_name(fund_id).values[0][0]
return {'name': fund_name, 'data': ret}
Index: .idea/misc.xml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
<+><?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<project version=\"4\">\r\n <component name=\"JavaScriptSettings\">\r\n <option name=\"languageLevel\" value=\"ES6\" />\r\n </component>\r\n <component name=\"ProjectRootManager\" version=\"2\" project-jdk-name=\"Python 3.6\" project-jdk-type=\"Python SDK\" />\r\n</project>
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- .idea/misc.xml (revision a12f0fb313aea047d7d89c3af73ed9832e157fb9)
+++ .idea/misc.xml (date 1606811694134)
@@ -3,5 +3,5 @@
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
- <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
+ <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8" project-jdk-type="Python SDK" />
</project>
\ No newline at end of file
Index: .idea/fund_report.iml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
<+><?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<module type=\"PYTHON_MODULE\" version=\"4\">\r\n <component name=\"NewModuleRootManager\">\r\n <content url=\"file://$MODULE_DIR$\" />\r\n <orderEntry type=\"jdk\" jdkName=\"Python 3.6\" jdkType=\"Python SDK\" />\r\n <orderEntry type=\"sourceFolder\" forTests=\"false\" />\r\n </component>\r\n <component name=\"TestRunnerService\">\r\n <option name=\"PROJECT_TEST_RUNNER\" value=\"Unittests\" />\r\n </component>\r\n</module>
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- .idea/fund_report.iml (revision a12f0fb313aea047d7d89c3af73ed9832e157fb9)
+++ .idea/fund_report.iml (date 1606811694098)
@@ -2,7 +2,7 @@
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
- <orderEntry type="jdk" jdkName="Python 3.6" jdkType="Python SDK" />
+ <orderEntry type="jdk" jdkName="Python 3.8" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TestRunnerService">
Index: app/service/order_service.py
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
<+># -*- encoding: utf-8 -*-\r\n# -----------------------------------------------------------------------------\r\n# @File Name : order_service.py\r\n# @Time : 2020/11/18 下午3:35\r\n# @Author : X. Peng\r\n# @Email : acepengxiong@163.com\r\n# @Software : PyCharm\r\n# -----------------------------------------------------------------------------\r\nfrom app.model.order import OrderInfo\r\n\r\n\r\nclass OrderService:\r\n \"\"\"\"\"\"\r\n def __init__(self):\r\n pass\r\n\r\n def create_order(self):\r\n pass\r\n\r\n def get_order(self):\r\n pass\r\n
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- app/service/order_service.py (revision a12f0fb313aea047d7d89c3af73ed9832e157fb9)
+++ app/service/order_service.py (date 1605779670767)
@@ -7,7 +7,8 @@
# @Software : PyCharm
# -----------------------------------------------------------------------------
from app.model.order import OrderInfo
-
+from app.api.engine import tamp_product_session
+from app.model.tamp_product_models import *
class OrderService:
""""""
@@ -18,4 +19,7 @@
pass
def get_order(self):
- pass
+ # pass
+ res = tamp_product_session.query(CompanyInfo).filter().all()
+ r = [x.to_dict for x in res]
+ print(r)
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
File added
# -*- encoding: utf-8 -*-
# -----------------------------------------------------------------------------
# @File Name : radar_chart.py
# @Time : 2020/12/1 下午4:50
# @Author : X. Peng
# @Email : acepengxiong@163.com
# @Software : PyCharm
# -----------------------------------------------------------------------------
import imgkit
import pyecharts.options as opts
from pyecharts.charts import Radar, Bar
import uuid
import cv2
from app.api.engine import work_dir
def gen_radar_chart(radar_chart_data):
"""
Gallery 使用 pyecharts 1.1.0
参考地址: https://echarts.apache.org/examples/editor.html?c=radar
目前无法实现的功能:
1、雷达图周围的图例的 textStyle 暂时无法设置背景颜色
"""
# v1 = [[4300, 10000, 28000, 35000, 50000]]
radar_chart = radar_chart_data['data'][0:5]
v1 = [[data['data'] for data in radar_chart]]
print(radar_chart)
radar = Radar(
init_opts=opts.InitOpts(bg_color="#fff", animation_opts=opts.AnimationOpts(animation=False))).add_schema(
schema=[
opts.RadarIndicatorItem(name="绝对收益", max_=100),
opts.RadarIndicatorItem(name="抗风险能力", max_=100),
opts.RadarIndicatorItem(name="极端风险", max_=100),
opts.RadarIndicatorItem(name="风险调整后收益", max_=100),
opts.RadarIndicatorItem(name="业绩持续性", max_=100),
],
# schema=[
# opts.RadarIndicatorItem(name=data['name'], max_=float(data['data'])) for data in radar_chart
# ],
splitarea_opt=opts.SplitAreaOpts(
is_show=True, areastyle_opts=opts.AreaStyleOpts(opacity=1, color=[
'rgba(198, 167, 116, 0.5)',
'rgba(198, 167, 116, 0.4)',
'rgba(198, 167, 116, 0.3)',
'rgba(198, 167, 116, 0.2)',
'rgba(198, 167, 116, 0.1)',
])
),
textstyle_opts=opts.TextStyleOpts(color="#000", font_size=16),
).add(
series_name="预算分配(Allocated Budget)",
data=v1,
linestyle_opts=opts.LineStyleOpts(width=2, color="#C6A774"),
tooltip_opts=None
).set_series_opts(label_opts=opts.LabelOpts(is_show=False)).set_global_opts(
title_opts=opts.TitleOpts(
title="综合评分", subtitle=radar_chart_data['data'][5]['data'], pos_top="center", pos_left="46%",
title_textstyle_opts={'color': '#333',
'fontSize': 18,
'lineHeight': 20,
'fontWeight': 'normal'
},
subtitle_textstyle_opts={'color': '#222A77',
'fontSize': 20,
'lineHeight': 23,
'fontWeight': 'bolder',
}),
legend_opts=opts.LegendOpts(selected_mode=False, is_show=False)
)
html_name = work_dir + '/app/html/' + str(uuid.uuid4()) + '.html'
img_name = work_dir + '/app/html/img/radar_chart_' + str(uuid.uuid4()) + '.png'
radar.render(html_name)
imgkit.from_file(html_name, img_name)
# 读取图片
img = cv2.imread(img_name)
# 获取宽度和高度
height = len(img)
width = len(img[0])
img = img[0:height, 160:750]
cv2.imwrite(img_name, img)
return img_name
if __name__ == '__main__':
gen_radar_chart()
\ No newline at end of file
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