Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Sign in
Toggle navigation
F
fund_report
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
彭熊
fund_report
Commits
73060184
Commit
73060184
authored
Jan 21, 2021
by
赵杰
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
新版本推荐基金,支持ifa导入基金
parent
0fecd195
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
245 additions
and
192 deletions
+245
-192
data_service_v2_1.py
app/service/data_service_v2_1.py
+23
-1
portfolio_diagnose.py
app/service/portfolio_diagnose.py
+190
-180
result_service_v2.py
app/service/result_service_v2.py
+27
-8
jinjia2html_v2.py
app/utils/jinjia2html_v2.py
+5
-3
No files found.
app/service/data_service_v2_1.py
View file @
73060184
...
...
@@ -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
:
...
...
app/service/portfolio_diagnose.py
View file @
73060184
...
...
@@ -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'
:
'
%.2
f'
%
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_i
d
)
sql
=
"SELECT fund_name FROM ifa_imported_fund_info WHERE id='{}'"
.
format
(
fun
d
)
# 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.1
5
self
.
expect_return
=
0.1
0
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.1
8
self
.
expect_return
=
0.1
2
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
.
propos
al_fund
:
for
fund
in
self
.
propos
e_portfolio
.
columns
:
result
.
append
(
self
.
single_evaluation
(
fund
))
return
result
except
Exception
as
e
:
...
...
app/service/result_service_v2.py
View file @
73060184
...
...
@@ -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"
])
...
...
app/utils/jinjia2html_v2.py
View file @
73060184
...
...
@@ -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)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment