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

添加个基评价

parent 33df3fe6
...@@ -305,8 +305,6 @@ class BaseConvexOptimizer(BaseOptimizer): ...@@ -305,8 +305,6 @@ class BaseConvexOptimizer(BaseOptimizer):
is_sector = [sector_mapper[t] == sector for t in self.tickers] is_sector = [sector_mapper[t] == sector for t in self.tickers]
self._constraints.append(cp.sum(self._w[is_sector]) >= sector_lower[sector]) self._constraints.append(cp.sum(self._w[is_sector]) >= sector_lower[sector])
print(self._constraints)
def convex_objective(self, custom_objective, weights_sum_to_one=True, **kwargs): def convex_objective(self, custom_objective, weights_sum_to_one=True, **kwargs):
""" """
Optimise a custom convex objective function. Constraints should be added with Optimise a custom convex objective function. Constraints should be added with
......
...@@ -16,6 +16,7 @@ def cal_correlation(prod): ...@@ -16,6 +16,7 @@ def cal_correlation(prod):
""" """
prod_return = prod.iloc[:, :].apply(lambda x: simple_return(x)) prod_return = prod.iloc[:, :].apply(lambda x: simple_return(x))
correlation = prod_return.corr() correlation = prod_return.corr()
correlation = correlation.round(2)
return correlation.mask(np.eye(correlation.shape[0], dtype=np.bool)) return correlation.mask(np.eye(correlation.shape[0], dtype=np.bool))
...@@ -92,7 +93,8 @@ def choose_good_evaluation(evaluation): ...@@ -92,7 +93,8 @@ 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:
if v2:
del evaluation[2] del evaluation[2]
if v3[0] > 1: if v3[0] > 1:
del evaluation[3] del evaluation[3]
...@@ -112,7 +114,8 @@ def choose_bad_evaluation(evaluation): ...@@ -112,7 +114,8 @@ 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]
...@@ -207,7 +210,7 @@ def get_fund_name(fund): ...@@ -207,7 +210,7 @@ def get_fund_name(fund):
# 获取排名信息 # 获取排名信息
fund_rank = get_fund_rank() fund_rank = get_fund_rank()
# 获取探普产品池 # 获取探普产品池
tamp_fund = get_fund_rank() tamp_fund = get_tamp_fund()
class PortfolioDiagnose(object): class PortfolioDiagnose(object):
...@@ -337,6 +340,10 @@ class PortfolioDiagnose(object): ...@@ -337,6 +340,10 @@ class PortfolioDiagnose(object):
if np.any(self.old_correlation[fund] > 0.8): if np.any(self.old_correlation[fund] > 0.8):
self.abandon_fund_corr.append(fund) self.abandon_fund_corr.append(fund)
prod = prod.drop(fund, axis=1) prod = prod.drop(fund, axis=1)
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])
return prod return prod
def proposal(self, prod): def proposal(self, prod):
...@@ -349,17 +356,17 @@ class PortfolioDiagnose(object): ...@@ -349,17 +356,17 @@ class PortfolioDiagnose(object):
""" """
# 组合内已包含的策略 # 组合内已包含的策略
included_strategy = set() # included_strategy = set()
# 按每种基金最少投资100w确定组合包含的最大基金数量 # 按每种基金最少投资100w确定组合包含的最大基金数量
max_len = self.invest_amount // 1e6 - len(prod.columns) max_len = self.invest_amount // 1e6 - len(prod.columns)
# 排名表内包含的所有策略 # 排名表内包含的所有策略
all_strategy = set(fund_rank['substrategy'].to_list()) # all_strategy = set(fund_rank['substrategy'].to_list())
if prod is not None: # 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])
# 待添加策略为所有策略-组合已包含策略 # 待添加策略为所有策略-组合已包含策略
add_strategy = all_strategy - included_strategy # add_strategy = all_strategy - included_strategy
# 遍历产品池,推荐得分>80且与组合内其他基金相关度低于0.8的属于待添加策略的基金 # 遍历产品池,推荐得分>80且与组合内其他基金相关度低于0.8的属于待添加策略的基金
for proposal in tamp_fund['fund_id']: for proposal in tamp_fund['fund_id']:
...@@ -370,8 +377,8 @@ class PortfolioDiagnose(object): ...@@ -370,8 +377,8 @@ class PortfolioDiagnose(object):
else: else:
continue continue
if proposal_z_score > 80 and proposal_strategy in add_strategy: # if proposal_z_score > 80 and proposal_strategy in add_strategy:
# if proposal_z_score > 80: if proposal_z_score > 60:
proposal_nav = get_nav(proposal, self.start_date, invest_type=self.invest_type) proposal_nav = get_nav(proposal, self.start_date, invest_type=self.invest_type)
# 忽略净值周期大于周更的产品 # 忽略净值周期大于周更的产品
...@@ -387,29 +394,42 @@ class PortfolioDiagnose(object): ...@@ -387,29 +394,42 @@ class PortfolioDiagnose(object):
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))
_correlation = cal_correlation(prod) self.new_correlation = cal_correlation(prod)
_correlation = _correlation.fillna(0) judge_correlation = self.new_correlation.fillna(0)
if np.all(_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}
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)
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 return prod
def optimize(self, ): def optimize(self, ):
import time
start = time.time()
origin_portfolio = self.get_portfolio() origin_portfolio = self.get_portfolio()
end1 = time.time()
print("原始组合数据获取时间:", end1 - start)
abandoned_portfolio = self.abandon(origin_portfolio) abandoned_portfolio = self.abandon(origin_portfolio)
end2 = time.time()
print("计算换仓基金时间:", end2 - end1)
propose_portfolio = self.proposal(abandoned_portfolio) propose_portfolio = self.proposal(abandoned_portfolio)
end3 = time.time()
print("遍历产品池获取候选推荐时间:", end3 - end2)
# propose_portfolio.to_csv('test_portfolio.csv', encoding='gbk') # propose_portfolio.to_csv('test_portfolio.csv', encoding='gbk')
returns = propose_portfolio.pct_change().dropna()
mu = expected_returns.mean_historical_return(propose_portfolio) mu = expected_returns.mean_historical_return(propose_portfolio, frequency=min(self.freq_list))
S = risk_models.sample_cov(propose_portfolio) 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: # if self.client_type == 1:
# proposal_risk = [[x, get_risk_level(search_rank(fund_rank, x, metric='substrategy'))] for x in # proposal_risk = [[x, get_risk_level(search_rank(fund_rank, x, metric='substrategy'))] for x in
# self.proposal_fund] # self.proposal_fund]
...@@ -422,13 +442,16 @@ class PortfolioDiagnose(object): ...@@ -422,13 +442,16 @@ class PortfolioDiagnose(object):
risk_upper = {"H": 0.0} risk_upper = {"H": 0.0}
risk_lower = {"L": 0.6, "M": 0.4} risk_lower = {"L": 0.6, "M": 0.4}
ef = EfficientFrontier(mu, S) 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) # weights = ef.nonconvex_objective(deviation_risk_parity, ef.cov_matrix)
ef.efficient_return(0.2) ef.efficient_drawdown(drawdown_limit=0.5)
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)
end4 = time.time()
print("模型计算一次时间:", end4 - end3)
# S = np.asmatrix(S) # S = np.asmatrix(S)
# w_origin = np.asarray([i for i in w_origin.values()]) # w_origin = np.asarray([i for i in w_origin.values()])
# risk_target = np.asarray([1 / len(w_origin)] * len(w_origin)) # risk_target = np.asarray([1 / len(w_origin)] * len(w_origin))
...@@ -471,7 +494,7 @@ class PortfolioDiagnose(object): ...@@ -471,7 +494,7 @@ class PortfolioDiagnose(object):
current_day = datetime.datetime.now().day current_day = datetime.datetime.now().day
past_month = (current_year - start_year) * 12 + current_month - start_month past_month = (current_year - start_year) * 12 + current_month - start_month
num_fund = len(self.portfolio) num_fund = len(self.portfolio)
abandon_fund = [[x, self.invest_type] for x in self.abandon_fund] abandon_fund = [[x, self.invest_type] for x in self.abandon_fund_score + self.abandon_fund_corr]
old_strategy = set([search_rank(fund_rank, x, metric='substrategy') for x in self.portfolio]) old_strategy = set([search_rank(fund_rank, x, metric='substrategy') for x in self.portfolio])
data = [start_year, start_month, past_month, self.invest_amount, data = [start_year, start_month, past_month, self.invest_amount,
...@@ -532,11 +555,11 @@ class PortfolioDiagnose(object): ...@@ -532,11 +555,11 @@ 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, "TO DO", return_triple], 2: [return_triple, "12", return_bool],
3: [drawdown_triple, drawdown_triple, drawdown_value, 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]}
if fund_id in self.abandon_fund: if fund_id in self.abandon_fund_score:
data['remove'] = True data['remove'] = True
elif fund_id in self.proposal_fund: elif fund_id in self.proposal_fund:
data[5] = [1] * 7 data[5] = [1] * 7
...@@ -545,33 +568,34 @@ class PortfolioDiagnose(object): ...@@ -545,33 +568,34 @@ class PortfolioDiagnose(object):
data['remove'] = False data['remove'] = False
x = '30%' x = '30%'
# 第一个评价 content = {
content = {1: [["优秀", "良好", "一般"], # 第一个评价
["优秀", "良好", "合格", "较差"], 1: [["优秀", "良好", "一般"],
["优秀", "良好", "合格", "较差"], ["优秀", "良好", "合格", "较差"],
["高", "一般", "较低"]], ["优秀", "良好", "合格", "较差"],
# 第二个评价 ["高", "一般", "较低"]],
2: [["高于", "持平", "低于"], # 第二个评价
x, 2: [["高于", "持平", "低于"],
["优秀", "一般"]], x,
# 第三个评价 ["优秀", "一般"]],
3: [["优秀", "良好", "一般"], # 第三个评价
["高", "中", "低"], x, 3: [["优秀", "良好", "一般"],
["高于", "持平", "低于"]], ["高", "中", "低"], x,
# 第四个评价 ["高于", "持平", "低于"]],
4: [["较好", "较差"], # 第四个评价
["较小", "较大"], 4: [["较好", "较差"],
["较小", "较小"], ["较小", "较大"],
["较大", "较小"], ["较小", "较小"],
["较低", "较高"]], ["较大", "较小"],
5: [["TO DO"] * 7]} ["较低", "较高"]],
5: [["TO DO"]] * 7}
sentence = { sentence = {
1: "1、该基金整体表现%s,收益能力%s,回撤控制能力%s,风险收益比例%s;\n", 1: "该基金整体表现%s,收益能力%s,回撤控制能力%s,风险收益比例%s;\n",
2: "2、在收益方面,该基金年化收益能力%s同类基金平均水平,有%s区间跑赢指数,绝对收益能力%s;\n", 2: "在收益方面,该基金年化收益能力%s同类基金平均水平,有%s区间跑赢指数,绝对收益能力%s;\n",
3: "3、在风险方面,该基金抵御风险能力%s,在同类基金中处于%s等水平,最大回撤为%s,%s同类基金平均水平;\n", 3: "在风险方面,该基金抵御风险能力%s,在同类基金中处于%s等水平,最大回撤为%s,%s同类基金平均水平;\n",
4: "4、该基金收益%s的同时回撤%s,也就是说,该基金在用%s风险换取%s收益,存在%s风险;\n", 4: "该基金收益%s的同时回撤%s,也就是说,该基金在用%s风险换取%s收益,存在%s风险;\n",
5: "5、基金经理,投资年限%s年,经验丰富;投资能力较强,生涯中共管理过%s只基金,历任的%s只基金平均业绩在同类中处于上游水平,其中%s只排名在前%s;生涯年化回报率%s,同期大盘只有%s;"} 5: "基金经理,投资年限%s年,经验丰富;投资能力较强,生涯中共管理过%s只基金,历任的%s只基金平均业绩在同类中处于上游水平,其中%s只排名在前%s;生涯年化回报率%s,同期大盘只有%s;"}
remove = data["remove"] remove = data["remove"]
del data["remove"] del data["remove"]
...@@ -582,18 +606,21 @@ class PortfolioDiagnose(object): ...@@ -582,18 +606,21 @@ class PortfolioDiagnose(object):
# 剔除,选择坏的话术 # 剔除,选择坏的话术
else: else:
evaluation = choose_bad_evaluation(data) evaluation = choose_bad_evaluation(data)
print(evaluation)
ret = "" ret = ""
for k, v in evaluation.items(): for k, v in evaluation.items():
# print(translate_single(content[k], v)) # print(translate_single(content[k], v))
ret = ret + sentence[k] % translate_single(content[k], v) ret = ret + sentence[k] % translate_single(content[k], v)
return {fund_id: ret} fund_name = get_fund_name(fund_id).values[0][0]
return {'name': fund_name, 'data': ret}
def old_portfolio_evaluation(self, ): def old_portfolio_evaluation(self, ):
result = [] result = []
for fund in self.portfolio: for fund in self.portfolio:
result.append(self.single_evaluation(fund)) try:
result.append(self.single_evaluation(fund))
except IndexError:
continue
return result return result
def propose_fund_evaluation(self, ): def propose_fund_evaluation(self, ):
...@@ -622,6 +649,10 @@ portfolio = ['HF00002JJ2', 'HF00005DBQ', 'HF0000681Q', 'HF00006693', 'HF00006AZF ...@@ -622,6 +649,10 @@ portfolio = ['HF00002JJ2', 'HF00005DBQ', 'HF0000681Q', 'HF00006693', 'HF00006AZF
portfolio_diagnose = PortfolioDiagnose(client_type=1, portfolio=portfolio, invest_amount=10000000) portfolio_diagnose = PortfolioDiagnose(client_type=1, portfolio=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.propose_fund_evaluation()) # 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())
# import pymysql # import pymysql
from sqlalchemy import create_engine from sqlalchemy import create_engine
db = create_engine( db = create_engine(
'mysql+pymysql://tamp_fund:@imeng408@tamper.mysql.polardb.rds.aliyuncs.com:3306/tamp_fund?charset=utf8mb4') 'mysql+pymysql://tamp_fund:@imeng408@tamper.mysql.polardb.rds.aliyuncs.com:3306/tamp_fund?charset=utf8mb4',
pool_size=50,
pool_recycle=3600,
pool_pre_ping=True)
con = db.connect() con = db.connect()
import logging import logging
logging.basicConfig(level=logging.INFO)
logging.basicConfig(level=logging.INFO)
from app.utils.week_evaluation import * from app.utils.week_evaluation import *
...@@ -297,4 +301,4 @@ if __name__ == '__main__': ...@@ -297,4 +301,4 @@ if __name__ == '__main__':
# fund_rank.to_csv("fund_rank.csv", encoding='gbk') # fund_rank.to_csv("fund_rank.csv", encoding='gbk')
# df = pd.read_csv('fund_rank.csv') # df = pd.read_csv('fund_rank.csv')
# df.to_sql("fund_rank", con, if_exists='replace') # df.to_sql("fund_rank", con, if_exists='replace')
con.close() con.close()
\ 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