Commit fdc3475f authored by pengxiong's avatar pengxiong

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

parents aded976b e027a0cb
HF00002JJ2,11月下半月,伴随美国大选形势明朗化,以及美元持续走弱,国内经济持续复苏等综合因素影响,商品和股市都持续走强。商品方面受美元因素影响较大的有色走势更强,股指期货方面由于市场风格转向周期和价值股,IH及IF走势更强。远澜商品组合中趋势,均值回复,量价截面和基本面量化等子策略皆不同程度获利,股指组合中量价和基本面量化择时策略也都小幅盈利。
HF00002G4A,本月市场环境对基金期权套利策略影响不大,整个11月表现为稳定增长。
HF00006693,目前仓位75成,主要配置于食品饮料(7.77%),TMT(17.22%)、医药生物(39.26%)、休闲服务(6.55%)、电力设备(4.68%)。
HF00006BGS,11月份月度涨幅0.59%,成立以来跌幅12.63%。市场轮动加速、 强势股快速回调,导致策略超额不理想。针对近期产品的回撤情况,无量投研团队也在日以继夜的加强研究分析,不断总结经验以求更好适应当下市场。无量逐步排查并解决了一些交易及策略方面存在的问题,并更新了一些低频因子,体现在业绩上还需要一些时间。
HF00006AZF,权益类:仍然持有基本面长期看好的先进制造业类股票,期待迎来戴维斯双击的 上涨,同时用股指期货期权对冲指数风险,整体权益类资产保持净多仓。 商品类:由空翻多,不过会加入跨品种之间的对冲保护。 债券类:大幅减仓,等待企稳后逐步加回仓位。本月收益主要由商品类资产贡献较多。
HF0000681Q,市场从年初以消费、医药、科技板块跑出较长时间的一波行情之后,9月10月经历了市场的调整,11月份之后,A股市场处于震荡上行状态,市场出现各个板块的风格不断快速切换,给阿尔法策略短期带来了较大的压力。另一方面,股指基差收敛,期货价格上升,但对于目前已经持有的股指空头来说,会造成额外的亏损。目前跟踪走势表现正常。
HF00004RHP,本月通过到期兑付、出售获得较多的流动性,并把握住了恐慌波动带来的市场投资良机, 获利了结部分乳业可交换债、全部汽车零部件行业转债;旅游行业公司债完成兑付,文化传媒行业公司债完成部分兑付;加仓建筑环保行业转债、电磁线行业转债,新增铝加工添加剂行业转债;新增环保行业、煤炭行业以及电解铝行业债券。目前仓位94成,重点行业主要分布在地产、电解铝、建筑环保、钢铁、城投。
HF00005DBQ,"本月通过到期兑付、出售获得较多的流动性,并把握住了恐慌波动带来的市场投资良机, 获利了结全部汽车零部件行业转债、服纺行业转债;旅游行业公司债完成兑付,文化传媒行业公司债完成部分
兑付;加仓建筑环保行业转债、电磁线行业转债,新增铝加工添加剂行业转债;新增环保行业、煤炭行业以及电解铝行业债券。目前仓位92成,重点行业主要分布在地产、钢铁、电解铝、煤炭。"
HF00005ZUB,本月通过到期兑付、出售获得较多的流动性,并把握住了恐慌波动带来的市场投资良机, 获利了结全部汽车零部件行业转债、服纺行业转债;旅游行业公司债完成兑付,文化传媒行业公司债完成部分兑付;加仓建筑环保行业转债、电磁线行业转债,新增铝加工添加剂行业转债;新增环保行业、煤炭行业以及电解铝行业债券。目前仓位9成,重点行业主要分布在地产、钢铁、电解铝、建筑环保。
HF00006641,业绩归因方面:本月打新收益0.22%,中高频CTA收益-0.315%,中性策略收益-2.1268%。因11月市场风格转变剧烈,量价因子阶段性失效,导致中性策略发生亏损。近期商品期货中亏损较大的板块是油脂油料和贵金属板块,由于思勰做的是短周期CTA策略,交易级别在小时级,因此更加关注商品期货在日内流畅的波动,然而遇到如开盘跳空或是日内的来回拉锯行情亦或是一些突发事件导致的走势反转,就很难捕捉到交易机会。
HF00006GNQ,目前本策略全部资金进行高频CTA运作,运作正常。
HF00005QOM,本月通过到期兑付、出售获得较多的流动性,并把握住了恐慌波动带来的市场投资良机,获利了结全部汽车零部件行业转债、服纺行业转债;旅游行业公司债完成兑付,文化传媒行业公司债完成部分兑付;加仓建筑环保行业转债、电磁线行业转债,新增铝加工添加剂行业转债;新增环保行业、煤炭行业以及电解铝行业债券。目前仓位9成,重点行业主要分布在地产、煤炭、城投、建筑环保。
HF00006DLK,11月下半月,伴随美国大选形势明朗化,以及美元持续走弱,国内经济持续复苏等综合因素影响,商品和股市都持续走强,很多板块间都有较为明显的趋势,商品中是有色板块,而股市中是周期和价值股。市场环境对于中长周期的象限策略来说偏友好。整个11月的涨幅较可观。
HF00006FEU,11月下半月,伴随美国大选形势明朗化,以及美元持续走弱,国内经济持续复苏等综合因素影响,商品和股市都持续走强,很多板块间都有较为明显的趋势,商品中是有色板块,而股市中则是周期和价值股,大多数量化选股策略由于量价因子失效均出现负超额,黑翼的CTA与指增组合效果表现要优于市场上大多纯指数增强策略。
HF00005AFK,疫情以及美国大选等扰动因素下,跨境价差套利策略较难做,为控制波动风险,适当降低策略配比,加大低风险的股指期现套利。
\ No newline at end of file
......@@ -1091,19 +1091,30 @@ class PortfolioDiagnose(object):
evaluation = choose_bad_evaluation(data)
ret = []
fund_name = get_fund_name(fund_id).values[0][0]
try:
default_evaluation = pd.read_csv("evaluation.csv", encoding='utf-8', names=['fund_id', 'eval'])
if default_evaluation[default_evaluation['fund_id'] == fund_id]['eval'].values[0]:
ret.append('1、' + default_evaluation[default_evaluation['fund_id'] == fund_id]['eval'].values[0])
evaluation_dict = {'name': fund_name, 'data': ret}
if objective:
if fund_id in self.abandon_fund_score + self.abandon_fund_corr:
evaluation_dict['status'] = "换仓"
elif fund_id in self.portfolio:
evaluation_dict['status'] = "保留"
else:
evaluation_dict['status'] = ""
return evaluation_dict
except Exception:
pass
i = 1
for k, v in evaluation.items():
single_sentence = str(i) + "、" + sentence[k] % translate_single(content, k, v)
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}
......@@ -1155,14 +1166,15 @@ class PortfolioDiagnose(object):
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())
# print(portfolio_diagnose.single_evaluation(fund_id='HF0000681Q'))
\ No newline at end of file
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
/*
* Prefixed by https://autoprefixer.github.io
* PostCSS: v7.0.29,
* Autoprefixer: v9.7.6
* Browsers: last 4 version
*/
.all {
width: 1200px;
margin: 0 auto;
page-break-inside:avoid;
}
.clearfix:after {
content: "";
display: block;
clear: both;
overflow: hidden;
}
.fl {
float: left;
}
.fr {
float: right;
}
.dtable {
display: table;
}
.dcell {
display: table-cell;
vertical-align: middle;
}
/* 封面 */
.box0 {
display: {{box0}};
position: relative;
padding: 234px 0 184px 0;
}
.box0 .cover_head {
position: absolute;
top: 45px;
right: 38px;
}
.box0 .cover_head .cover_logo {
width: 172px;
height: 52px;
}
.box0 .cover_head .cover_line {
width: 1px;
height: 34px;
background: #10316E;
margin: 0 10px 0 10px;
}
.box0 .cover_head .cover_text {
font-size: 17px;
line-height: 23px;
color: #B6172B;
font-weight: bold;
}
.box0 .cover_people {
padding: 0 38px 0 858px;
}
.box0 .cover_people .cover_pre {
margin: 0 20px 0 0;
font-size: 28px;
line-height: 40px;
color: #999999;
font-weight: bold;
}
.box0 .cover_people .cover_name {
font-size: 32px;
line-height: 45px;
color: #B6172B;
font-weight: bold;
}
.box0 .cover_people .cover_sir {
font-size: 28px;
line-height: 45px;
color: #333333;
font-weight: bold;
}
.box0 .cover_back {
display: block;
width: 100%;
height: 218px;
}
.box0 .cover_date {
padding: 0 0 0 858px;
}
.box0 .cover_date .cover_time {
display: inline-block;
font-size: 40px;
line-height: 66px;
color: #ffffff;
font-weight: bold;
padding: 0 66px 0 66px;
background: #B6172B;
-webkit-box-shadow: 0px 10px 26px 0px rgba(66, 5, 5, 0.18);
box-shadow: 0px 10px 26px 0px rgba(66, 5, 5, 0.18);
border: 1px solid #979797;
}
.box0 .cover_title {
position: absolute;
top: 100px;
left: 211px;
z-index: 1;
width: 282px;
height: 514px;
background: #B6172B;
-webkit-box-shadow: 0px 10px 26px 0px rgba(66, 5, 5, 0.44);
box-shadow: 0px 10px 26px 0px rgba(66, 5, 5, 0.44);
border: 1px solid #979797;
}
.box0 .cover_title .cover_title_text {
padding: 0 30px 0 50px;
font-size: 46px;
line-height: 61px;
color: #ffffff;
font-weight: bold;
}
.box_line {
height: 15px;
background: #F1F1F1;
}
/* 目录 */
.box1 {
display: {{box1}};
position: relative;
padding: 150px 0 200px 36px;
}
.box1 .catalog_line {
position: absolute;
top: 0;
left: 0;
width: 36px;
height: 100%;
background: #B6172B;
}
.box1 .catalog_box .catalog_item:first-child {
width: 562px;
padding: 0 46px 0 46px;
}
.box1 .catalog_box .catalog_item .catalog_en {
font-size: 72px;
line-height: 77px;
color: #333333;
font-weight: bold;
opacity: 0.45;
}
.box1 .catalog_box .catalog_item .catalog_ch {
margin: 30px 0 0 0;
font-size: 58px;
line-height: 77px;
color: #333333;
font-weight: bold;
}
.box1 .catalog_box .catalog_item .catalog_li {
position: relative;
}
.box1 .catalog_box .catalog_item .catalog_li .catalog_dot {
position: absolute;
top: 32px;
left: 0;
width: 16px;
height: 16px;
background: #D7D7D7;
border-radius: 50%;
}
.box1 .catalog_box .catalog_item .catalog_li .catalog_name {
padding: 0 0 0 40px;
font-size: 32px;
line-height: 77px;
color: #333333;
font-weight: bold;
}
/* 页眉 */
.page {
padding: 54px 34px 10px 34px;
width: 100%;
border-bottom: 1px solid #F1F1F1;
}
.page .page_title {
width: 880px;
font-size: 40px;
line-height: 56px;
color: #333333;
font-weight: bold;
}
.page .page_head .page_logo {
width: 172px;
height: 52px;
}
.page .page_head .page_line {
width: 1px;
height: 34px;
background: #10316E;
margin: 0 10px 0 10px;
}
.page .page_head .page_text {
font-size: 17px;
line-height: 23px;
color: #B6172B;
font-weight: bold;
white-space: nowrap;
}
/* 投资总览 */
.box2{
display: {{box2}};
}
.box2 .box2_content {
padding: 47px 34px 61px 34px;
page-break-after:always;
}
/* 表格样式1 */
.tss1 {
margin: 30px 0 0 0;
}
.tss1 .tss1_title {
font-size: 24px;
line-height: 33px;
color: #333333;
font-weight: bold;
margin: 0 0 15px 0;
}
.tss1 .tss1_table {
width: 100%;
border-collapse: collapse;
}
.tss1 .tss1_table tr {
border-bottom: 2px solid #FFFFFF;
background: #F1F1F1;
width: 100%;
}
.tss1 .tss1_table tr td {
width: 25%;
padding: 12px 26px 12px 26px;
font-size: 16px;
line-height: 22px;
color: #333333;
font-weight: bold;
}
.tss1 .tss1_table tr td .red {
color: #D10000;
}
.tss1 .tss1_table tr td .green {
color: #129298;
}
/* 表格样式2 */
.tss2 {
margin: 40px 0 0 0;
}
.tss2 .tss2_title {
font-size: 24px;
line-height: 33px;
color: #333333;
font-weight: bold;
margin: 0 0 15px 0;
}
.tss2 .tss2_content {
margin: 18px 0 0 0;
border: 1px solid #E4E4E4;
}
.tss2 .tss2_content .tss2_img {
width: 100%;
}
/* 表格样式3 */
.tss3 {
margin: 40px 0 0 0;
}
.tss3 .tss3_head {
width: 100%;
}
.tss3 .tss3_head .tss3_title {
font-size: 24px;
line-height: 33px;
color: #333333;
font-weight: bold;
margin: 0 0 15px 0;
}
.tss3 .tss3_head .tss3_title .tss3_start_time {
font-size: 18px;
line-height: 33px;
color: #666666;
font-weight: normal;
}
.tss3 .tss3_head .tss3_now_time {
font-size: 14px;
line-height: 20px;
color: #333333;
text-align: right;
}
.tss3 .tss3_tag {
position: absolute;
padding: 0 16px 0 26px;
font-size: 16px;
line-height: 30px;
color: #ffffff;
background: #A2A2A2;
}
.tss3 .tss3_tag .tss3_tri {
position: absolute;
top: 0;
right: -10px;
border-top: 15px solid #A2A2A2;
border-bottom: 15px solid transparent;
border-left: 5px solid #A2A2A2;
border-right: 5px solid transparent;
}
.tss3 table {
margin: 24px 0 0 0;
width: 100%;
border: none;
border-color: rgba(151, 151, 151, 0.18);
border-collapse: collapse;
}
.tss3 table tr {
position: relative;
font-size: 14px;
line-height: 20px;
color: #333333;
text-align: center;
vertical-align: middle;
}
.tss3 table tr.yellow {
background: #FFFAF2;
}
.tss3 table tr.gray {
background: #F1F1F1;
}
.tss3 table tr th {
padding: 14px 0 14px 0;
background: #F1F1F1;
}
.tss3 table tr td {
padding: 14px 0 14px 0;
}
.tss3 table tr td.gray {
background: #F1F1F1;
}
.tss3 table tr td.text_red {
color: #D00000;
}
.tss3 table tr td.text_green {
color: #22A236;
}
.tss3 table tr td.summary {
font-size: 18px;
line-height: 25px;
color: #333333;
font-weight: bold;
}
.tss3 .tss3_tip {
margin: 10px 0 0 0;
font-size: 12px;
line-height: 17px;
color: #333333;
}
/* 目标与业绩 */
.box3{
display: {{box3}};
}
.box3 .box3_content {
padding: 47px 34px 61px 34px;
page-break-after:always;
}
/* 收益比较、相关性分析 */
.par {
margin: 47px 0 0 0;
width: 100%;
}
.par .par_item {
width: 540px;
}
.par .par_item .par_title {
font-size: 24px;
line-height: 33px;
color: #333333;
font-weight: bold;
margin: 0 0 15px 0;
}
.par .par_item .par_content {
margin: 18px 0 0 0;
border: 1px solid #E4E4E4;
}
.par .par_item .par_content.relative_chart {
padding: 27px 30px 40px 30px;
}
.par .par_item .par_content.relative_chart table {
width: 100%;
border-collapse: collapse;
border: none;
border-color: #D8D8D8;
}
.par .par_item .par_content.relative_chart table td {
padding: 14px 0 14px 0;
text-align: center;
}
.par .par_item .par_content .par_img {
width: 100%;
}
.rcc_left {
width: 10%;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
}
.rcc_column {
border: 1px solid transparent;
}
.rcc_n {
border: 1px solid transparent;
margin: 9px;
width: 20px;
height: 20px;
border-radius: 50%;
background: #999999;
font-size: 12px;
line-height: 20px;
color: #FFFFFF;
text-align: center;
}
.rcc_right {
width: 90%;
}
.rcc_index {
display: inline-block;
margin: 0 auto;
width: 20px;
height: 20px;
border-radius: 50%;
background: rgba(53, 63, 83, 0.5);
font-size: 12px;
line-height: 20px;
color: #FFFFFF;
text-align: center;
}
.rcc_number {
font-size: 12px;
line-height: 18px;
color: #666666;
}
.rc_tooltip {
margin: 16px 0 0 0;
text-align: center;
}
.rct_color {
display: inline-block;
}
.rct_color_item {
border: 1PX solid #FFFFFF;
width: 38px;
height: 11px;
}
.rtc_number_item {
margin: 0 12px 0 0;
font-size: 12px;
line-height: 18px;
color: #666666;
}
.rct_number {
display: inline-block;
}
.rc_label {
margin: 16px 14px 0 14px;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
.rcl_item {
width: 50%;
position: relative;
margin: 4px 0 4px 0;
}
.rcl_item_number {
position: absolute;
top: 2px;
left: 0;
margin: 0 8px 0 0;
width: 20px;
height: 20px;
border-radius: 50%;
background: #999999;
font-size: 12px;
line-height: 20px;
color: #FFFFFF;
text-align: center;
}
.rcl_item_name {
padding: 0 10px 0 30px;
font-size: 16px;
line-height: 24px;
color: #333333;
}
.level_color1 {
background: #993612;
color: #993612;
}
.level_color2 {
background: #C58E7B;
color: #C58E7B;
}
.level_color3 {
background: #E9D4CD;
color: #E9D4CD;
}
.level_color4 {
background: #C4E4E6;
color: #C4E4E6;
}
.level_color5 {
background: #87C8CB;
color: #87C8CB;
}
.level_color6 {
background: #129298;
color: #129298;
}
/* 业绩的明细 */
.box4{
display: {{box4}};
}
.box4 .box4_content {
padding: 47px 34px 61px 34px;
page-break-after:always;
}
/* 个基点评 */
.box5{
display: {{box5}};
}
.box5 .box5_content {
padding: 47px 34px 61px 34px;
page-break-after:always;
}
.geji_list_wrap .self_item {
background: #FFFFFF;
-webkit-box-shadow: 0px 8px 9px 0px rgba(0, 0, 0, 0.05);
box-shadow: 0px 8px 9px 0px rgba(0, 0, 0, 0.05);
border-radius: 8px;
border: 1px solid #E4E4E4;
margin: 0 0 34px 0;
}
.geji_list_wrap .self_item:last-child {
margin: 0 0 0 0;
}
.geji_list_wrap .self_item .self_item_left {
width: 710px;
padding: 35px 36px 35px 0;
}
.geji_list_wrap .self_item .self_item_left .self_header {
display: inline-block;
font-size: 0;
}
.geji_list_wrap .self_item .self_item_left .self_header .self_title {
position: relative;
padding: 0 10px 0 20px;
font-size: 24px;
line-height: 33px;
color: #333333;
font-weight: bold;
}
.geji_list_wrap .self_item .self_item_left .self_header .self_title .self_title_tri {
width: 0;
height: 0;
border-bottom: 20px solid transparent;
border-top: 20px solid #6C71AA;
border-right: 5px solid transparent;
border-left: 5px solid #6C71AA;
position: absolute;
right: -10px;
top: 0;
}
.geji_list_wrap .self_item .self_item_left .self_header .self_type {
margin: 4px 0 0 0;
padding: 0 10px 0 10px;
font-size: 18px;
line-height: 26px;
color: #4C4C4C;
font-weight: bold;
background: #E5E3E3;
}
.geji_list_wrap .self_item .self_item_left .self_header .self_type.red {
color: #983612;
background: #FFEAE2;
}
.geji_list_wrap .self_item .self_item_left .self_header .self_type.green {
color: #22999F;
background: #E2F7F8;
}
.geji_list_wrap .self_item .self_item_left .self_description {
margin: 20px 0 0 20px;
}
.geji_list_wrap .self_item .self_item_left .self_description .self_description_item {
position: relative;
margin: 0 0 10px 0;
}
.geji_list_wrap .self_item .self_item_left .self_description .self_description_item:last-child {
margin: 0;
}
.geji_list_wrap .self_item .self_item_left .self_description .self_description_item .self_description_dot {
position: absolute;
left: 0;
top: 8px;
width: 6px;
height: 6px;
border-radius: 50%;
background: #CBCBCB;
}
.geji_list_wrap .self_item .self_item_left .self_description .self_description_item .self_description_text {
font-size: 16px;
line-height: 22px;
color: #666666;
text-align: justify;
padding-left: 18px;
}
.geji_list_wrap .self_item .self_item_left .self_description .self_description_item .self_description_text .self_description_red {
color: #983612;
}
.geji_list_wrap .self_item .self_item_left .self_description .self_description_item .self_description_text .self_description_green {
color: #129298;
}
.geji_list_wrap .self_item .self_item_right {
margin: 30px 26px 30px 26px;
}
.geji_list_wrap .self_item .self_item_right .self_img {
width: 100%;
}
/*持仓组合点评*/
.target_comment {
margin: 36px 0 0 0;
}
.target_comment .comment_title {
display: inline-block;
position: relative;
padding: 4px 10px 4px 20px;
border-radius: 8px 0 0px 0px;
background: #B6172B;
font-size: 24px;
line-height: 33px;
color: #FFFFFF;
}
.target_comment .comment_title .comment_tri {
width: 0;
height: 0;
border-top: 20px solid transparent;
border-bottom: 20px solid #B6172B;
border-left: 10px solid #B6172B;
border-right: 10px solid transparent;
position: absolute;
right: -20px;
bottom: 0;
}
.target_comment .comment_content {
padding: 39px 26px 39px 26px;
background: #F7F7F7;
}
.target_comment .comment_content .comment_item {
width: 100%;
position: relative;
}
.target_comment .comment_content .comment_dot {
position: absolute;
top: 16px;
left: 0px;
width: 8px;
height: 8px;
border-radius: 50%;
background: #666666;
}
.target_comment .comment_content .comment_text {
padding-left: 24px;
font-size: 18px;
line-height: 42px;
color: #666666;
}
.target_comment .comment_content .comment_text .comment_tag_red {
color: #A75435;
}
.target_comment .comment_content .comment_text .comment_tag_green {
color: #129298;
}
/* 优化组合建议 */
.box6{
display: {{box6}};
}
.box6 .box6_content {
padding: 47px 34px 61px 34px;
page-break-after:always;
}
/* 新增基金 */
.box7{
display: {{box7}};
}
.box7 .box7_content {
padding: 47px 34px 61px 34px;
page-break-after:always;
}
/* 结尾 */
.box8{
display: {{box8}};
}
.box8 .box8_content {
padding: 60px 0 60px 0;
}
.financial_show .financial_left {
width: 763px;
background: #B6172B;
padding: 58px 0 48px 0;
}
.financial_show .financial_left .financial_scene {
border-top: 1px solid #B6172B;
border-bottom: 1px solid #B6172B;
position: relative;
height: 305px;
}
.financial_show .financial_left .financial_scene .financial_scene_img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
-o-object-fit: cover;
object-fit: cover;
}
.financial_show .financial_left .financial_scene .financial_scene_text {
margin: 31px 77px 0 79px;
font-size: 26px;
line-height: 49px;
color: #FFFFFF;
letter-spacing: 4px;
position: relative;
z-index: 1;
}
.financial_show .financial_left .financial_scene .financial_scene_author {
margin: 0 77px 34px 79px;
font-size: 26px;
line-height: 49px;
color: #FFFFFF;
text-align: right;
position: relative;
z-index: 1;
}
.financial_show .financial_show_right {
background: #F7F7F7;
padding: 58px 0 48px 0;
}
.financial_show .financial_show_right .financial_team {
height: 305px;
}
.financial_show .financial_show_right .financial_team .financial_team_img {
/*width: 100%;*/
/*height: 100%;*/
/*-o-object-fit: cover;*/
/*object-fit: cover;*/
width: auto;
height: 100%;
-o-object-fit: cover;
object-fit: cover;
}
.statement_content {
padding: 27px 31px 0 31px;
}
.statement_content .statement_block {
margin: 0 0 20px 0;
}
.statement_content .statement_block .statement_title {
margin: 0 0 10px 0;
font-size: 10px;
line-height: 27px;
color: #717171;
font-weight: bold;
text-align: justify;
}
.statement_content .statement_block .statement_p {
margin: 0 0 10px 0;
font-size: 10px;
line-height: 27px;
color: #717171;
text-align: justify;
}
.statement_content .statement_block .statement_title2 {
font-size: 10px;
line-height: 27px;
color: #333333;
font-weight: bold;
text-align: justify;
}
</style>
</head>
<body>
<div class="all">
<!-- 封面 -->
<div class="box0">
<div class="cover_head dtable">
<div class="dcell">
<img src={{logo}} alt="" class="cover_logo">
</div>
<div class="dcell">
<div class="cover_line"></div>
</div>
<div class="dcell">
<div class="cover_text">
小飞象<br>
工作室
</div>
</div>
</div>
<div class="cover_people">
<span class="cover_pre">
敬呈
</span>
<span class="cover_name">
{{customer_name}}
</span>
<span class="cover_sir">
客户
</span>
</div>
<img src={{cover_back}} alt="" class="cover_back">
<div class="cover_date">
<div class="cover_time">
{{year_month}}
</div>
</div>
<div class="cover_title dtable">
<div class="cover_title_text dcell">
资产<br>
存续报告
</div>
</div>
</div>
<div class="box_line"></div>
<!-- 目录 -->
<div class="box1">
<div class="catalog_line"></div>
<div class="catalog_box dtable">
<div class="catalog_item dcell">
<div class="catalog_en">
Contents
</div>
<div class="catalog_ch">
目录
</div>
</div>
<div class="catalog_item dcell">
<div class="catalog_li">
<div class="catalog_dot"></div>
<div class="catalog_name">投资总览</div>
</div>
<div class="catalog_li">
<div class="catalog_dot"></div>
<div class="catalog_name">目标与业绩</div>
</div>
<div class="catalog_li">
<div class="catalog_dot"></div>
<div class="catalog_name">业绩的明细</div>
</div>
<!-- <div class="catalog_li">-->
<!-- <div class="catalog_dot"></div>-->
<!-- <div class="catalog_name">优化建议</div>-->
<!-- </div>-->
</div>
</div>
</div>
<div class="box_line"></div>
<!-- 投资总览 -->
<div class="box2" style="page-break-before:always;">
<div class="page dtable">
<div class="page_title dcell">
投资总览
</div>
<div class="page_head dcell">
<div class="dcell">
<img src={{logo}} alt="" class="page_logo">
</div>
<div class="dcell">
<div class="page_line"></div>
</div>
<div class="dcell">
<div class="page_text">
{{brand_name}}
</div>
</div>
</div>
</div>
<div class="box2_content">
<!-- 组合投资表现 -->
<div class="tss1" style="margin: 0;">
<div class="tss1_title">
组合投资表现
</div>
<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 now_yield>=0%}
成立以来累计收益率:<span class="red">{{now_yield}}%</span>
{%else%}
成立以来累计收益率:<span class="green">{{now_yield}}%</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>
</tr>
</table>
</div>
<!-- 资产盈亏情况 -->
<div class="tss1">
<div class="tss1_title">
资产盈亏情况
</div>
<table class="tss1_table">
<tr>
<td>
投资成本:<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>
</tr>
</table>
</div>
<!-- 组合月度及累计回报率曲线 -->
<div class="tss2">
<div class="tss2_title">
组合月度及累计回报率曲线
</div>
<div class="tss2_content">
<img src={{monthly_return_performance_pic}} alt="" class="tss2_img">
</div>
</div>
<!-- 组合月度及累计回报率曲线 -->
<div class="tss3">
<div class="tss3_head dtable">
<div class="tss3_title dcell">
组合月度及累计回报率曲线
</div>
</div>
<table border="1">
<tr>
<th>
年份
</th>
<th>
金额(万)
</th>
<th>
1月
</th>
<th>
2月
</th>
<th>
3月
</th>
<th>
4月
</th>
<th>
5月
</th>
<th>
6月
</th>
<th>
7月
</th>
<th>
8月
</th>
<th>
9月
</th>
<th>
10月
</th>
<th>
11月
</th>
<th>
12月
</th>
<!-- <th>-->
<!-- 累计/月均-->
<!-- </th>-->
</tr>
{%for key, value in monthly_table_return.items() %}
<tr class="yellow">
<td rowspan="2" class="gray">
{{key}}
</td>
<td>
盈亏
</td>
<td class="text_red">
{{value[1]["profit"]}}
</td>
<td class="text_green">
{{value[2]["profit"]}}
</td>
<td>
{{value[3]["profit"]}}
</td>
<td>
{{value[4]["profit"]}}
</td>
<td>
{{value[5]["profit"]}}
</td>
<td>
{{value[6]["profit"]}}
</td>
<td>
{{value[7]["profit"]}}
</td>
<td>
{{value[8]["profit"]}}
</td>
<td>
{{value[9]["profit"]}}
</td>
<td>
{{value[10]["profit"]}}
</td>
<td>
{{value[11]["profit"]}}
</td>
<td>
{{value[12]["profit"]}}
</td>
</tr>
<tr>
<td>
期末资产
</td>
<td class="text_red">
{{value[1]["net_amount"]}}
</td>
<td class="text_green">
{{value[2]["net_amount"]}}
</td>
<td>
{{value[3]["net_amount"]}}
</td>
<td>
{{value[4]["net_amount"]}}
</td>
<td>
{{value[5]["net_amount"]}}
</td>
<td>
{{value[6]["net_amount"]}}
</td>
<td>
{{value[7]["net_amount"]}}
</td>
<td>
{{value[8]["net_amount"]}}
</td>
<td>
{{value[9]["net_amount"]}}
</td>
<td>
{{value[10]["net_amount"]}}
</td>
<td>
{{value[11]["net_amount"]}}
</td>
<td>
{{value[12]["net_amount"]}}
</td>
</tr>
{%endfor%}
</table>
</div>
</div>
</div>
<div class="box_line"></div>
{%for folio, group_result in all_folio_result.items()%}
<!-- 目标与业绩 -->
<div class="box3">
<div class="page dtable">
<div class="page_title dcell">
{%if folio == "default"%}
目标与业绩
{%else%}
目标与业绩({{folio}})
{%endif%}
</div>
<div class="page_head dcell">
<div class="dcell">
<img src={{logo}} alt="" class="page_logo">
</div>
<div class="dcell">
<div class="page_line"></div>
</div>
<div class="dcell">
<div class="page_text">
{{brand_name}}
</div>
</div>
</div>
</div>
<div class="box3_content">
<!-- 投资目标 -->
<div class="tss3" style="margin: 0;">
<div class="tss3_head dtable">
<div class="tss3_title dcell">
投资目标
</div>
</div>
<table border="1">
<tr>
<th>现有配置资金(万元)</th>
<th>现有年化收益情况(%)</th>
<th>现有最大回撤(%)</th>
</tr>
<tr>
<td>{{group_result["cost_of_investment"]}}</td>
<td>{{group_result["annualised_return"]}}</td>
<td>{{group_result["max_withdrawal"]}}</td>
</tr>
</table>
<table border="1" style="margin: 0;">
<tr>
<th>计划配置资金(万元)</th>
<th>目标年化收益情况(%)</th>
<th>目标最大回撤(%)</th>
</tr>
<tr>
<td>{{group_result["cost_of_investment"]}}</td>
<td>5%-10%</td>
<td>5%-10%</td>
</tr>
</table>
</div>
<!-- 指标对比 -->
<div class="tss3">
<div class="tss3_head dtable">
<div class="tss3_title dcell">
指标对比<span class="tss3_start_time">(起始日期{{start_date}})</span>
</div>
<div class="tss3_now_time dcell">
截止日:最新净值日({{latest_worth_day}})
</div>
</div>
<table border="1">
<tr>
<th>类型</th>
<th>区间收益(%)</th>
<th>年化收益(%)</th>
<th>波动率(%)</th>
<th>最大回撤(%)</th>
<th>夏普比率</th>
</tr>
<tr>
<tr class="yellow">
<td>{{group_result["old_indicator_compare"][0]["group_name"]}}</td>
<td>{{group_result["old_indicator_compare"][0]["return_ratio"]}}</td>
<td>{{group_result["old_indicator_compare"][0]["return_ratio_year"]}}</td>
<td>{{group_result["old_indicator_compare"][0]["volatility"]}}</td>
<td>{{group_result["old_indicator_compare"][0]["max_drawdown"]}}</td>
<td>{{group_result["old_indicator_compare"][0]["sharpe"]}}</td>
</tr>
<tr >
<td>{{group_result["old_indicator_compare"][1]["group_name"]}}</td>
<td>{{group_result["old_indicator_compare"][1]["return_ratio"]}}</td>
<td>{{group_result["old_indicator_compare"][1]["return_ratio_year"]}}</td>
<td>{{group_result["old_indicator_compare"][1]["volatility"]}}</td>
<td>{{group_result["old_indicator_compare"][1]["max_drawdown"]}}</td>
<td>{{group_result["old_indicator_compare"][1]["sharpe"]}}</td>
</tr>
</table>
<div class="tss3_tip">
注:以上指标自持仓首日开始计算,结果仅供参考,如果持仓时间过短会造成指标失真的情况。
</div>
</div>
<!-- 收益比较、相关性分析 -->
<div class="par clearfix" cellpadding="38">
<div class="par_item fl">
<div class="par_title">
收益比较
</div>
<div class="par_content">
<img src={{group_result["old_return_compare_pic"]}} alt="" class="par_img">
</div>
</div>
<div class="par_item fr">
<div class="par_title">
相关性分析
</div>
<div class="par_content relative_chart">
<div class="rc_chart clearfix">
<div class="rcc_left fl">
<table border="1" style="border-color: transparent;margin-right: 20px;">
{% for correlation in group_result["old_correlation"] %}
<tr>
<td>
<span class="rcc_index">{{correlation[0]}}</span>
</td>
</tr>
{% endfor %}
</table>
</div>
<div class="rcc_right fr">
<table border="1">
{% for correlation in group_result["old_correlation"] %}
<tr>
{% for i in range(correlation[2]|length) %}
{% if i == correlation[0] - 1 %}
<td>
<span class="rcc_index">{{correlation[0]}}</span>
</td>
{% elif -1 <= correlation[2][i] < -0.5 and i < correlation[0] - 1 %}
<td>{{correlation[2][i]}}</td>
{% elif -1 <= correlation[2][i] < -0.5 %}
<td class="level_color6">{{correlation[2][i]}}</td>
{% elif -0.5 <= correlation[2][i] < -0.25 and i < correlation[0] - 1 %}
<td>{{correlation[2][i]}}</td>
{% elif -0.5 <= correlation[2][i] < -0.25 %}
<td class="level_color5">{{correlation[2][i]}}</td>
{% elif -0.25 <= correlation[2][i] < 0 and i < correlation[0] - 1 %}
<td>{{correlation[2][i]}}</td>
{% elif -0.25 <= correlation[2][i] < 0 %}
<td class="level_color4">{{correlation[2][i]}}</td>
{% elif 0 <= correlation[2][i] < 0.25 and i < correlation[0] - 1 %}
<td>{{correlation[2][i]}}</td>
{% elif 0 <= correlation[2][i] < 0.25 %}
<td class="level_color3">{{correlation[2][i]}}</td>
{% elif 0.25 <= correlation[2][i] < 0.5 and i < correlation[0] - 1 %}
<td>{{correlation[2][i]}}</td>
{% elif 0.25 <= correlation[2][i] < 0.5 %}
<td class="level_color2">{{correlation[2][i]}}</td>
{% elif 0.5 <= correlation[2][i] <= 1 and i < correlation[0] - 1 %}
<td>{{correlation[2][i]}}</td>
{% elif 0.5 <= correlation[2][i] <= 1 %}
<td class="level_color1">{{correlation[2][i]}}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
</div>
<div class="rc_tooltip">
<div class="rct_color clearfix">
<div class="rct_color_item fl level_color1"></div>
<div class="rct_color_item fl level_color2"></div>
<div class="rct_color_item fl level_color3"></div>
<div class="rct_color_item fl level_color4"></div>
<div class="rct_color_item fl level_color5"></div>
<div class="rct_color_item fl level_color6"></div>
</div>
</div>
<div class="rc_tooltip" style="margin: 0;">
<div class="rct_number clearfix">
<div class="rtc_number_item fl">1.00</div>
<div class="rtc_number_item fl">0.50</div>
<div class="rtc_number_item fl">0.25</div>
<div class="rtc_number_item fl">0.00</div>
<div class="rtc_number_item fl">-0.25</div>
<div class="rtc_number_item fl">-0.50</div>
<div class="rtc_number_item fl">-1.00</div>
</div>
</div>
<div class="rc_label clearfix">
{% for correlation in group_result["old_correlation"] %}
<div class="rcl_item fl">
<div class="rcl_item_number">
{{correlation[0]}}
</div>
<div class="rcl_item_name">
{{correlation[1]}}
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="box_line"></div>
<!-- 业绩的明细 -->
<div class="box4">
<div class="page dtable">
<div class="page_title dcell">
业绩的明细
</div>
<div class="page_head dcell">
<div class="dcell">
<img src={{logo}} alt="" class="page_logo">
</div>
<div class="dcell">
<div class="page_line"></div>
</div>
<div class="dcell">
<div class="page_text">
{{brand_name}}
</div>
</div>
</div>
</div>
<div class="box4_content">
<!-- 持仓收益汇总 -->
<div class="tss3" style="margin: 0;">
<div class="tss3_head dtable">
<div class="tss3_title dcell">
持仓收益汇总
</div>
<div class="tss3_now_time dcell">
截止日:最新净值日({{latest_worth_day}})
</div>
</div>
<div style="position: relative;">
<div class="tss3_tag">
私募基金
<div class="tss3_tri"></div>
</div>
<table border="1">
<tr>
<th colspan="5">投资本金</th>
<th colspan="4">{{month}}月业绩</th>
<th colspan="3">累计业绩</th>
</tr>
<tr>
<th>投资策略</th>
<th>基金简称</th>
<th>买入时间</th>
<th>存续年数</th>
<th>投资本金 (万)</th>
<th>当月收益 (万)</th>
<th>当月收益率 (%)</th>
<th>月末市值 (万)</th>
<th>月末占比 (%)</th>
<th>累计收益 (万)</th>
<th>累计收益率 (%)</th>
<th>年化收益率 (%)</th>
</tr>
{% for one in group_result["group_hoding_info"] %}
<tr>
<td rowspan="1">{{one.fund_strategy_name}}</td>
<td>{{one.fund_name}}</td>
<td>{{one.confirm_date}}</td>
<td>{{one.hold_year}}</td>
<td>{{one.cost}}</td>
<td>{{one.profit}}</td>
<td>{{one.month_return_ratio}}</td>
<td>{{one.market_values}}</td>
<td>{{one.weight}}</td>
<td>{{one.cum_profit}}</td>
<td>{{one.cum_profit_ratio}}</td>
<td>{{one.return_ratio_year}}</td>
</tr>
{% endfor %}
<tr class="gray">
<td colspan="4">总计</td>
<td>{{group_result["group_hoding_info_total"]["total_cost"]}}</td>
<td>{{group_result["group_hoding_info_total"]["cur_month_profit"]}}</td>
<td>{{group_result["group_hoding_info_total"]["cur_month_profit_ratio"]}}</td>
<td>{{group_result["group_hoding_info_total"]["ending_assets"]}}</td>
<td>100%</td>
<td>{{group_result["group_hoding_info_total"]["cumulative_profit"]}}</td>
<td>{{group_result["group_hoding_info_total"]["cumulative_return"]}}</td>
<td>{{group_result["group_hoding_info_total"]["return_ratio_year"]}}</td>
</tr>
</table>
</div>
</div>
<!-- 基金净值 -->
<div class="tss3">
<div class="tss3_head dtable">
<div class="tss3_title dcell">
基金净值
</div>
<div class="tss3_now_time dcell">
截止日:最新净值日({{latest_worth_day}})
</div>
</div>
<table border="1">
<tr>
<th rowspan="2">基金简称</th>
<th rowspan="2">申购净值</th>
<th colspan="2">最新净值({{latest_worth_day}})</th>
<th colspan="7">收益率(%)</th>
<th rowspan="2">分红</th>
</tr>
<tr>
<th>单位净值</th>
<th>累计净值</th>
<th>较上周</th>
<th>申购以来</th>
<th>近一月</th>
<th>近半年</th>
<th>近一年</th>
<th>今年以来</th>
<th>成立以来</th>
</tr>
{% for nav_info in group_result["group_nav_info"] %}
<tr>
<td>{{nav_info['fund_name']}}</td>
<td>{{nav_info['confirm_nav']}}</td>
<td>{{nav_info['cur_nav']}}</td>
<td>{{nav_info['cur_cnav']}}</td>
<td>{{nav_info['ret_1w']}}</td>
<td>{{nav_info['ret_after_confirm']}}</td>
<td>{{nav_info['ret_cum_1m']}}</td>
<td>{{nav_info['ret_cum_6m']}}</td>
<td>{{nav_info['ret_cum_1y']}}</td>
<td>{{nav_info['ret_cum_ytd']}}</td>
<td>{{nav_info['ret_cum_incep']}}</td>
<td>{{nav_info['distribution']}}</td>
</tr>
{% endfor %}
</table>
</div>
<!-- 贡献分解 -->
<div class="page dtable" style="page-break-before:always;">
<div class="page_title dcell">
</div>
<div class="page_head dcell">
<div class="dcell">
<img src={{logo}} alt="" class="page_logo">
</div>
<div class="dcell">
<div class="page_line"></div>
</div>
<div class="dcell">
<div class="page_text">
{{brand_name}}
</div>
</div>
</div>
</div>
<div class="tss2">
<div class="tss2_title">
贡献分解
</div>
<div class="tss2_content">
<img src={{group_result["contribution_decomposition"]}} alt="" class="tss2_img">
</div>
</div>
</div>
</div>
<div class="box_line"></div>
<!-- 个基点评 -->
<div class="box5">
<div class="page dtable">
<div class="page_title dcell">
个基点评
</div>
<div class="page_head dcell">
<div class="dcell">
<img src={{logo}} alt="" class="page_logo">
</div>
<div class="dcell">
<div class="page_line"></div>
</div>
<div class="dcell">
<div class="page_text">
{{brand_name}}
</div>
</div>
</div>
</div>
<div class="box5_content">
<div class="geji_list_wrap">
{% for i in range(group_result["single_fund_data_list"]|length) %}
{% if (i+1) % 3 == 1 and i != 0%}
<div class="page dtable">
<div class="page_title dcell">
</div>
<div class="page_head dcell">
<div class="dcell">
<img src={{logo}} alt="" class="page_logo">
</div>
<div class="dcell">
<div class="page_line"></div>
</div>
<div class="dcell">
<div class="page_text">
{{brand_name}}
</div>
</div>
</div>
</div>
{% endif %}
{% if (i+1) % 3 == 0 and i != 0%}
<div class="self_item" style="page-break-after:always;">
{% else %}
<div class="self_item">
{% endif %}
<table>
<tr>
<td style="padding: 0;text-align: left;vertical-align: middle;">
<div class="self_item_left">
<div class="self_header clearfix">
<div class="self_title fl">
{{group_result["single_fund_data_list"][i].fund_name}}
</div>
{{group_result["single_fund_data_list"][i].status}}
</div>
<div class="self_description">
{% for one in group_result["single_fund_data_list"][i].evaluation %}
<div class="self_description_item">
<div class="self_description_dot"></div>
<div class="self_description_text">
{{one}}
</div>
</div>
{% endfor %}
</div>
</div>
</td>
<td style="padding: 0;text-align: left;vertical-align: middle;">
<div class="self_item_right">
<img src={{group_result["single_fund_data_list"][i].radar_chart_path}} alt="" class="self_img">
</div>
</td>
</tr>
</table>
</div>
{% endfor %}
</div>
<!-- 持仓点评 -->
{% if single_fund_data_list|length % 3 == 0 and single_fund_data_list|length != 0%}
<div class="page dtable">
<div class="page_title dcell">
</div>
<div class="page_head dcell">
<div class="dcell">
<img src={{logo}} alt="" class="page_logo">
</div>
<div class="dcell">
<div class="page_line"></div>
</div>
<div class="dcell">
<div class="page_text">
{{brand_name}}
</div>
</div>
</div>
</div>
{% endif %}
<div class="target_comment">
<div class="comment_title">
持仓点评
<div class="comment_tri"></div>
</div>
<div class="comment_content">
{% for eval in group_result["old_evaluation"] %}
<div class="comment_item">
<div class="comment_dot"></div>
<div class="comment_text">
{{eval}}
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<div class="box_line"></div>
<!-- 优化组合建议 -->
<div class="box6">
<div class="page dtable">
<div class="page_title dcell">
优化组合建议
</div>
<div class="page_head dcell">
<div class="dcell">
<img src={{logo}} alt="" class="page_logo">
</div>
<div class="dcell">
<div class="page_line"></div>
</div>
<div class="dcell">
<div class="page_text">
{{brand_name}}
</div>
</div>
</div>
</div>
<div class="box6_content">
<!-- 调仓建议 -->
<div class="tss3" style="margin: 0;">
<div class="tss3_head dtable">
<div class="tss3_title dcell">
调仓建议
</div>
</div>
<table border="1">
<tr>
<th>投资策略</th>
<th>基金简称</th>
<th>优化前(万元)</th>
<th>优化后(万元)</th>
</tr>
{% for key, value in group_result["suggestions_result"].items() %}
{% for i in range(value|length) %}
<tr>
{% if i == 0%}
<td rowspan={{value|length }}>{{key}}</td>
{% endif %}
<td> {{value[i]["fund_name"]}}</td>
<td class="font_red">{{value[i]["before_optimization"]}}</td>
<td class="font_green">{{value[i]["after_optimization"]}}</td>
</tr>
{% endfor %}
{% endfor %}
<tr>
<td colspan="2" class="gray">总市值(万元)</td>
<td class="summary">{{group_result["suggestions_result_asset"]["before"]}}</td>
<td class="summary">{{group_result["suggestions_result_asset"]["after"]}}</td>
</tr>
</table>
</div>
<!-- 指标对比 -->
<div class="tss3">
<div class="tss3_head dtable">
<div class="tss3_title dcell">
指标对比<span class="tss3_start_time">(起始日期2019-11-06)</span>
</div>
<div class="tss3_now_time dcell">
截止日:最新净值日({{latest_worth_day}})
</div>
</div>
<table border="1">
<tr>
<th>类型</th>
<th>区间收益(%)</th>
<th>年化收益(%)</th>
<th>波动率(%)</th>
<th>最大回撤(%)</th>
<th>夏普比率</th>
</tr>
{%for i in range(group_result["indicator_compare"]|length)%}
{%if i<(group_result["indicator_compare"]|length)-1%}
{%if i%2==0 %}
<tr>
{%endif%}
{%if i%2==1 %}
<tr class="yellow">
{%endif%}
{%endif%}
<td>{{group_result["indicator_compare"][i]["group_name"]}}</td>
<td>{{group_result["indicator_compare"][i]["return_ratio"]}}</td>
<td>{{group_result["indicator_compare"][i]["return_ratio_year"]}}</td>
<td>{{group_result["indicator_compare"][i]["volatility"]}}</td>
<td>{{group_result["indicator_compare"][i]["max_drawdown"]}}</td>
<td>{{group_result["indicator_compare"][i]["sharpe"]}}</td>
</tr>
{% endfor %}
</table>
<div class="tss3_tip">
注:以上指标自持仓首日开始计算,结果仅供参考,如果持仓时间过短会造成指标失真的情况。
</div>
</div>
<!-- 收益比较、相关性分析 -->
<div class="page dtable" style="page-break-before:always;">
<div class="page_title dcell">
</div>
<div class="page_head dcell">
<div class="dcell">
<img src={{logo}} alt="" class="page_logo">
</div>
<div class="dcell">
<div class="page_line"></div>
</div>
<div class="dcell">
<div class="page_text">
{{brand_name}}
</div>
</div>
</div>
</div>
<div class="par clearfix" cellpadding="38">
<div class="par_item fl">
<div class="par_title">
收益比较
</div>
<div class="par_content">
<img src={{group_result["return_compare_pic"]}} alt="" class="par_img">
</div>
</div>
<div class="par_item fr">
<div class="par_title">
相关性分析
</div>
<div class="par_content relative_chart">
<div class="rc_chart clearfix">
<div class="rcc_left fl">
<table border="1" style="border-color: transparent;margin-right: 20px;">
{% for correlation in group_result["new_correlation"] %}
<tr>
<td>
<span class="rcc_index">{{correlation[0]}}</span>
</td>
</tr>
{% endfor %}
</table>
</div>
<div class="rcc_right fr">
<table border="1">
{% for correlation in group_result["new_correlation"] %}
<tr>
{% for i in range(correlation[2]|length) %}
{% if i == correlation[0] - 1 %}
<td>
<span class="rcc_index">{{correlation[0]}}</span>
</td>
{% elif -1 <= correlation[2][i] < -0.5 and i < correlation[0] - 1 %}
<td>{{correlation[2][i]}}</td>
{% elif -1 <= correlation[2][i] < -0.5 %}
<td class="level_color6">{{correlation[2][i]}}</td>
{% elif -0.5 <= correlation[2][i] < -0.25 and i < correlation[0] - 1 %}
<td>{{correlation[2][i]}}</td>
{% elif -0.5 <= correlation[2][i] < -0.25 %}
<td class="level_color5">{{correlation[2][i]}}</td>
{% elif -0.25 <= correlation[2][i] < 0 and i < correlation[0] - 1 %}
<td>{{correlation[2][i]}}</td>
{% elif -0.25 <= correlation[2][i] < 0 %}
<td class="level_color4">{{correlation[2][i]}}</td>
{% elif 0 <= correlation[2][i] < 0.25 and i < correlation[0] - 1 %}
<td>{{correlation[2][i]}}</td>
{% elif 0 <= correlation[2][i] < 0.25 %}
<td class="level_color3">{{correlation[2][i]}}</td>
{% elif 0.25 <= correlation[2][i] < 0.5 and i < correlation[0] - 1 %}
<td>{{correlation[2][i]}}</td>
{% elif 0.25 <= correlation[2][i] < 0.5 %}
<td class="level_color2">{{correlation[2][i]}}</td>
{% elif 0.5 <= correlation[2][i] <= 1 and i < correlation[0] - 1 %}
<td>{{correlation[2][i]}}</td>
{% elif 0.5 <= correlation[2][i] <= 1 %}
<td class="level_color1">{{correlation[2][i]}}</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
</div>
<div class="rc_tooltip">
<div class="rct_color clearfix">
<div class="rct_color_item fl level_color1"></div>
<div class="rct_color_item fl level_color2"></div>
<div class="rct_color_item fl level_color3"></div>
<div class="rct_color_item fl level_color4"></div>
<div class="rct_color_item fl level_color5"></div>
<div class="rct_color_item fl level_color6"></div>
</div>
</div>
<div class="rc_tooltip" style="margin: 0;">
<div class="rct_number clearfix">
<div class="rtc_number_item fl">1.00</div>
<div class="rtc_number_item fl">0.50</div>
<div class="rtc_number_item fl">0.25</div>
<div class="rtc_number_item fl">0.00</div>
<div class="rtc_number_item fl">-0.25</div>
<div class="rtc_number_item fl">-0.50</div>
<div class="rtc_number_item fl">-1.00</div>
</div>
</div>
<div class="rc_label clearfix">
{% for correlation in group_result["new_correlation"] %}
<div class="rcl_item fl">
<div class="rcl_item_number">
{{correlation[0]}}
</div>
<div class="rcl_item_name">
{{correlation[1]}}
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="box_line"></div>
<!-- 新增基金 -->
<div class="box7">
<div class="page dtable">
<div class="page_title dcell">
新增基金
</div>
<div class="page_head dcell">
<div class="dcell">
<img src={{logo}} alt="" class="page_logo">
</div>
<div class="dcell">
<div class="page_line"></div>
</div>
<div class="dcell">
<div class="page_text">
{{brand_name}}
</div>
</div>
</div>
</div>
<div class="box7_content">
<div class="geji_list_wrap">
{% for i in range(group_result["propose_fund_data_list"]|length) %}
{% if (i+1) % 3 == 1 and i != 0%}
<div class="page dtable" style="page-break-before:always;">
<div class="page_title dcell">
</div>
<div class="page_head dcell">
<div class="dcell">
<img src={{logo}} alt="" class="page_logo">
</div>
<div class="dcell">
<div class="page_line"></div>
</div>
<div class="dcell">
<div class="page_text">
{{brand_name}}
</div>
</div>
</div>
</div>
<div class="self_item">
{% else %}
<div class="self_item">
{% endif %}
<table>
<tr>
<td style="padding: 0;text-align: left;vertical-align: middle;">
<div class="self_item_left">
<div class="self_header clearfix">
<div class="self_title fl">
{{group_result["propose_fund_data_list"][i].fund_name}}
</div>
</div>
<div class="self_description">
{% for one in group_result["propose_fund_data_list"][i].evaluation %}
<div class="self_description_item">
<div class="self_description_dot"></div>
<div class="self_description_text">
{{one}}
</div>
</div>
{% endfor %}
</div>
</div>
</td>
<td style="padding: 0;text-align: left;vertical-align: middle;">
<div class="self_item_right">
<img src={{group_result["propose_fund_data_list"][i].radar_chart_path}} alt="" class="self_img">
</div>
</td>
</tr>
</table>
</div>
{% endfor %}
</div>
<!-- 持仓点评 -->
<div class="target_comment">
<div class="comment_title">
优化方案点评
<div class="comment_tri"></div>
</div>
<div class="comment_content">
<div class="comment_item">
<div class="comment_dot"></div>
<div class="comment_text">
{{group_result["new_group_evaluation"]}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="box_line"></div>
{%endfor%}
<!-- 结尾 -->
<div class="box8">
<div class="page dtable">
<div class="page_title dcell">
</div>
<div class="page_head dcell">
<div class="dcell">
<img src={{logo}} alt="" class="page_logo">
</div>
<div class="dcell">
<div class="page_line"></div>
</div>
<div class="dcell">
<div class="page_text">
{{brand_name}}
</div>
</div>
</div>
</div>
<div class="box8_content">
<div class="financial_show dtable">
<div class="financial_left dcell">
<div class="financial_scene">
<img src={{scene}} alt="" class="financial_scene_img">
<div class="financial_scene_text">
我们挣的是⻆度和变化的钱⽽不是纠正市场错 误的钱,市场永远是正确的,关键是在其正确 被反复证明后的逆向⽽⾏,⼀定是避开它的正 确被展开的过程 。
</div>
<div class="financial_scene_author">
——飞度
</div>
</div>
</div>
<div class="financial_show_right dcell">
<div class="financial_team">
<img src={{team}} alt="" class="financial_team_img">
</div>
</div>
</div>
<div class="statement_content">
<div class="statement_block">
<div class="statement_title">
探普研究院声明
</div>
<div class="statement_p">
本诊断报告所表述的任何观点均准确地反应了研究人员的看法;该研究人员所得报酬的任何组成部分无论是过去、现在、或者将来均不会直接或间接地与研究报告所表述的建议或观点相联系。
</div>
</div>
<div class="statement_block">
<div class="statement_title">
一般性声明
</div>
<div class="statement_p">
本报告对于收件人而言属于高度机密,只有收件人才能使用。本报告并非意图发送、发布给在
当地法律或监管规则下不允许向其发送、发布该研究报告的人员。本研究报告仅供参考之用,在任何地区均不应被视为买卖任何证券、金融工具、基金、以及其他理财产品的要约或要约邀请。探普研究院并不因收件人收到本报告而视其为客户。本报告所包含的观点及建议并未考虑个别客户的特殊状况、目标或需要,不应视为对特定客户关于特定证券或金融工具、基金、以及其他理财产品的购买建议或策略。对于本报告中提及的任何证券、金融工具、基金、以及其他理财产品,本报告的
收件人须保持自身的独立判断。
</div>
<div class="statement_p">
本报告所载资料的来源被认为是可靠的,但探普研究院不保证其准确性或完整性,并不对使用本报告所包含的材料产生任何直接或间接损失或与此有关的其他损失承担任何责任。本报告提及的任何证券、金融工具、基金或其他理财产品均可能含有巨大的风险,可能不易变卖以及不适合所有的投资者。本报告所提及的证券、金融工具、基金或其他理财产品的价格、价值以及收益可能会受
汇率影响而波动。过往的业绩也不能代表未来的表现。
</div>
<div class="statement_p">
本报告所载的资料、观点以及预测分析均反映了探普研究院在最初报告发布日期当日的判断,可以在不发出通知的情况下做出更改、亦可因使用不同假设和标准、采用不同观点和分析方法而与市场上其他机构、部门、单位、个人在制作类似的其他材料时所给出的意见不同或者相反。探普研究院以及关联公司、单位并不承担提示本报告收件人注意该等材料的责任。负责撰写本报告研究人员薪酬并不基于任何金融产品的销售情况而定,但其薪酬可能会与我司的整体收入有关。
</div>
<div class="statement_p">
若以探普研究院以外的机构或个人发送本报告,则由该机构或个人为此发送行为承担全部责任。该机构或个人应联系相关机构以交易本报告中提及的证券、金融工具、基金、其他理财产品获悉更详细信息。本报告不构成探普研究院向发送本报告的机构或个人的客户提供的投资建议,探普研究院以及关联单位、公司中的各个高级职员、董事、员工亦不为(前述机构或个人)因使用本报告或报告载明的内容产生的直接或间接损失承担任何责任。
</div>
</div>
<div class="statement_block">
<div class="statement_title2">
未经探普研究院事先书面授权,任何人不得以任何目的复制、发送或者销售本报告。探普研究院 2020 版权所有,保留一切权利。
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
\ No newline at end of file
import time
import uuid
from jinja2 import PackageLoader, Environment
from app.api.engine import work_dir, pdf_folder, template_folder
from app.service.portfolio_diagnose import PortfolioDiagnose
from app.service.result_service_v2 import UserCustomerResultAdaptor
import numpy as np
from concurrent import futures
import os
# 准备数据
from app.utils.draw import draw_month_return_chart, draw_contribution_chart, draw_combination_chart, \
draw_old_combination_chart
from app.utils.html_to_pdf import html_to_pdf
from app.utils.radar_chart import gen_radar_chart
class DataIntegrate:
def __init__(self, ifa_id='USER_INFO15914346866762', customer_id='202009281545001', pdf_name=str(uuid.uuid4()) + '.pdf'):
self.user_customer = UserCustomerResultAdaptor(ifa_id, customer_id)
self.customer_name = self.user_customer.customer_real_name
self.pdf_name = pdf_name
# 全部数据
self.df = self.user_customer.calculate_total_data()
# 组合结果数据
self.d = self.user_customer.calculate_group_result_data()
self.all_folio_result = {}
# 分组合拼接结果数据
self.get_group_result()
# # 组合数据
# self.group_result = self.d["default"]
# self.get_portfolio_diagnose(self.group_result["fund_id_list"])
# 投资总览
self.get_summarize()
# 月度回报
self.get_month_return()
# 月度回报表格
self.get_month_table_return()
# 渲染模版
self.render_data()
# 分组和计算个基点评以及新增基金等结果
def get_group_result(self):
for group_name, group_result in self.d.items():
portfolio_diagnose = self.get_portfolio_diagnose(group_result["fund_id_list"], invest_amount=group_result["total_cost"])
cur_group_portfolio_result = {}
# 旧持仓组合点评
self.comments_on_position_portfolio(portfolio_diagnose, group_name, cur_group_portfolio_result)
# 贡献分解
self.contribution_deco(group_result, cur_group_portfolio_result)
# 目标与业绩
self.objectives_performance(group_result, cur_group_portfolio_result)
# 个基点评
self.single_fund_comment(portfolio_diagnose, cur_group_portfolio_result)
# 旧收益比较
self.get_old_compare_pic(cur_group_portfolio_result)
# 旧相关性
self.get_old_correlation(portfolio_diagnose, cur_group_portfolio_result)
# # 新增基金
self.propose_fund(portfolio_diagnose, cur_group_portfolio_result)
# # 新收益比较
self.get_transfer_suggestions(portfolio_diagnose, group_name, cur_group_portfolio_result)
# # 新相关性
self.get_new_correlation(portfolio_diagnose, cur_group_portfolio_result)
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=invest_amount)
portfolio_diagnose.optimize()
return portfolio_diagnose
# 全部数据综述结果
def get_summarize(self):
"""投资总览."""
self.total_cost = round(self.df["total_cost"], 2) # 投资成本
self.now_yield = round((self.df['cumulative_return']-1)*100, 2) # 成立以来累计收益率
self.now_annualised_return = round(self.df["return_ratio_year"] * 100, 2) # 年化收益率
self.index_yield = round((self.df["index_result"]["return_ratio"]-1)*100, 2) # 指数收益率
self.now_withdrawal = round(self.df["max_drawdown"][0]*100, 2) # 最大回撤
self.index_withdrawal = round(self.df["index_result"]["max_drawdown"][0]*100, 2) # 指数最大回撤
self.now_month_income = int(self.df["cur_month_profit"]) # 本月收益
self.month_rise = round(self.df["cur_month_profit_ratio"] * 100, 2) # 本月涨幅
self.year_totoal_rate_of_return = round(self.df["cur_year_profit_ratio"] * 100, 2) # 今年累计收益率
self.now_year_income = int(self.df["cur_year_profit"]) # 今年累计收益
self.final_balance = int(self.df["total_cost"] + self.df["cumulative_profit"]) # 期末资产
self.total_profit = int(self.df["cumulative_profit"]) # 累计盈利
def get_month_return(self):
"""月度回报."""
"""组合月度及累计回报率曲线图"""
xlabels, product_list, cumulative = self.user_customer.get_month_return_chart()
self.monthly_return_performance_pic = draw_month_return_chart(xlabels, product_list, cumulative)
def get_month_table_return(self):
"""月度盈亏和期末资产"""
self.monthly_table_return = self.df["month_return_data_dict"]
# 旧组合持仓点评,贡献分解数据
def comments_on_position_portfolio(self, portfolio_diagnose, folio, cur_group_portfolio_result):
"""旧持仓组合点评. 旧贡献分解数据"""
cur_group_portfolio_result["old_evaluation"], cur_group_portfolio_result["old_return_compare_data"],\
cur_group_portfolio_result["old_indicator_compare"] = portfolio_diagnose.old_evaluation(folio, self.d, self.user_customer)
def contribution_deco(self, group_result, cur_group_portfolio_result):
"""贡献分解."""
g_data = group_result["contribution_decomposition"]
cur_group_portfolio_result["contribution_decomposition"] = draw_contribution_chart(g_data['xlabels'], g_data['product_list'], g_data['cumulative'])
def single_fund_comment(self, portfolio_diagnose, cur_group_portfolio_result):
"""个基点评."""
single_fund_data_list = []
portfolio_evaluation = portfolio_diagnose.old_portfolio_evaluation()
radar_chart_data = portfolio_diagnose.single_fund_radar()
with futures.ProcessPoolExecutor(os.cpu_count()) as executor:
res = executor.map(gen_radar_chart, radar_chart_data)
res = list(res)
for i in range(len(portfolio_evaluation)):
if portfolio_evaluation[i]['status'] == '保留':
portfolio_evaluation[i]['status'] = '<div class="self_type fl">保留</div>'
elif portfolio_evaluation[i]['status'] == '增仓':
portfolio_evaluation[i]['status'] = '<div class="self_type fl red">增仓</div>'
elif portfolio_evaluation[i]['status'] == '换仓':
portfolio_evaluation[i]['status'] = '<div class="self_type fl green">换仓</div>'
elif portfolio_evaluation[i]['status'] == '减仓':
portfolio_evaluation[i]['status'] = '<div class="self_type fl green">减仓</div>'
single_fund_data_list.append({
'fund_name': portfolio_evaluation[i]['name'],
'status': portfolio_evaluation[i]['status'],
'evaluation': portfolio_evaluation[i]['data'],
'radar_chart_path': res[i]
})
cur_group_portfolio_result["single_fund_data_list"] = single_fund_data_list
def get_old_compare_pic(self, cur_group_portfolio_result):
"""旧收益比较"""
cur_group_portfolio_result["old_return_compare_pic"] = draw_old_combination_chart(cur_group_portfolio_result["old_return_compare_data"]["xlabels"],
cur_group_portfolio_result["old_return_compare_data"]["origin_combination"],
cur_group_portfolio_result["old_return_compare_data"]["index"])
def get_transfer_suggestions(self, portfolio_diagnose, folio, cur_group_portfolio_result):
"""新收益比较,调仓建议"""
cur_group_portfolio_result["suggestions_result"], cur_group_portfolio_result["suggestions_result_asset"], \
cur_group_portfolio_result["return_compare_data"], \
cur_group_portfolio_result["indicator_compare"], cur_group_portfolio_result["new_group_evaluation"] = portfolio_diagnose.new_evaluation(folio, self.d,
self.user_customer)
cur_group_portfolio_result["return_compare_pic"] = draw_combination_chart(cur_group_portfolio_result["return_compare_data"]["xlabels"],
cur_group_portfolio_result["return_compare_data"]["new_combination"],
cur_group_portfolio_result["return_compare_data"]["origin_combination"],
cur_group_portfolio_result["return_compare_data"]["index"])
def get_old_correlation(self, portfolio_diagnose, cur_group_portfolio_result):
"""旧相关性分析."""
old_correlation = portfolio_diagnose.old_correlation
old_correlation_columns = old_correlation.columns.tolist()
old_correlation_values = old_correlation.values.tolist()
cur_group_portfolio_result["old_correlation"] = list(zip(range(1, len(old_correlation_columns)+1), old_correlation_columns, old_correlation_values))
def get_new_correlation(self, portfolio_diagnose, cur_group_portfolio_result):
"""新相关性分析."""
new_correlation = portfolio_diagnose.new_correlation
new_correlation_columns = new_correlation.columns.tolist()
new_correlation_values = new_correlation.values.tolist()
cur_group_portfolio_result["new_correlation"] = list(zip(range(1, len(new_correlation_columns)+1), new_correlation_columns, new_correlation_values))
def propose_fund(self, portfolio_diagnose, cur_group_portfolio_result):
"""新增基金"""
# 优化组合建议1 -- 新增基金
propose_fund_data_list = []
propose_fund_evaluation = portfolio_diagnose.propose_fund_evaluation()
propose_radar_chart_data = portfolio_diagnose.propose_fund_radar()
with futures.ProcessPoolExecutor(os.cpu_count()) as executor:
res = executor.map(gen_radar_chart, propose_radar_chart_data)
res = list(res)
for i in range(len(propose_fund_evaluation)):
propose_fund_data_list.append({
'fund_name': propose_fund_evaluation[i]['name'],
'status': '增仓',
'evaluation': propose_fund_evaluation[i]['data'],
'radar_chart_path': res[i]
})
cur_group_portfolio_result["propose_fund_data_list"] = propose_fund_data_list
def objectives_performance(self, group_result, cur_group_portfolio_result):
"""目标与业绩"""
cur_group_portfolio_result["totoal_rate_of_return"] = round((group_result['cumulative_return']-1)*100, 2) # 成立以来累计收益率
cur_group_portfolio_result["annualised_return"] = round(group_result["return_ratio_year"]*100, 2) # 年化收益率
cur_group_portfolio_result["volatility"] = round(group_result["volatility"]*100, 2)
cur_group_portfolio_result["max_withdrawal"] = round(group_result["max_drawdown"][0]*100, 2)
cur_group_portfolio_result["sharpe_ratio"] = round(group_result["sharpe"], 2)
cur_group_portfolio_result["cost_of_investment"] = round(group_result["total_cost"]/10000.0, 2) # 投资成本
cur_group_portfolio_result["index_section_return"] = round((group_result["index_result"]["return_ratio"]-1)*100, 2)
cur_group_portfolio_result["index_annualised_return"] = round(group_result["index_result"]["return_ratio_year"]*100, 2) # 年化收益率
cur_group_portfolio_result["index_volatility"] = round(group_result["index_result"]["volatility"]*100, 2)
cur_group_portfolio_result["index_max_withdrawal"] = round(group_result["index_result"]["max_drawdown"][0]*100, 2)
cur_group_portfolio_result["index_sharpe_ratio"] = round(group_result["index_result"]["sharpe"], 2)
cur_group_portfolio_result["group_nav_info"] = group_result["group_nav_info"]
cur_group_portfolio_result["group_hoding_info"] = group_result["group_hoding_info"]
cur_group_portfolio_result["group_hoding_info_total"] = group_result["group_hoding_info_total"]
def render_data(self):
# 全部数据
data = {
# 封面 值为None不不显示,为block显示
'box0': 'block',
# 目录
'box1': 'block',
# 投资总览
'box2': 'block',
# 目标与业绩
'box3': 'block',
# 业绩的明细
'box4': 'block',
# 个基点评
'box5': 'block',
# 优化组合建议
'box6': None,
# 新增基金
'box7': None,
# 结尾
'box8': 'block',
'cover_back': template_folder + '/v2/img/cover-back.png',
'logo': template_folder + '/v2/img/logo.png',
'scene': template_folder + '/v2/img/scene.png',
'team': template_folder + '/v2/img/team.png',
# 全局数据
'customer_name': self.customer_name,
'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"),
'latest_worth_day': self.user_customer.last_nav_date,
'brand_name': '小飞象<br>工作室',
'customer_old': 42,
'customer_level': '平衡型',
# 综述数据
'now_allocation_amount': self.total_cost, 'now_yield': self.now_yield, 'index_yield': self.index_yield,
'now_annualised_return': self.now_annualised_return,
'now_withdrawal': self.now_withdrawal, 'index_withdrawal': self.index_withdrawal, 'expected_withdrawal': 20,
'now_year_income': self.now_year_income, 'now_month_income': self.now_month_income,
'final_balance': self.final_balance, 'total_profit': self.total_profit,
'monthly_return_performance_pic': self.monthly_return_performance_pic,
'month_rise': self.month_rise, 'year_totoal_rate_of_return': self.year_totoal_rate_of_return,
'monthly_table_return': self.monthly_table_return,
# 组合数据
'all_folio_result': self.all_folio_result,
# 'totoal_rate_of_return': self.totoal_rate_of_return,
# 'annualised_return': self.annualised_return, 'cost_of_investment': self.cost_of_investment,
#
#
# 'index_comparison': {'section_return': self.totoal_rate_of_return, 'annualized_returns': self.annualised_return,
# 'volatility': self.volatility, 'max_withdrawal': self.max_withdrawal,
# 'sharpe_ratio': self.sharpe_ratio},
# 'index_comparison_500': {'section_return': self.index_section_return,
# 'annualized_returns': self.index_annualised_return,
# 'volatility': self.index_volatility, 'max_withdrawal': self.index_max_withdrawal,
# 'sharpe_ratio': self.index_sharpe_ratio},
#
# 'group_nav_info': self.group_nav_info,
# 'group_hoding_info': self.group_hoding_info,
# 'group_hoding_info_total': self.group_hoding_info_total,
# 'old_evaluation': self.old_evaluation,
# 'old_indicator_compare': self.old_indicator_compare,
# 'contribution_decomposition': self.contribution_decomposition,
# 'single_fund_data_list': self.single_fund_data_list,
# 'old_correlation': self.old_correlation,
# 'old_return_compare_pic': self.old_return_compare_pic,
# # 'new_correlation': self.new_correlation,
# # 'propose_fund_data_list': self.propose_fund_data_list,
# # 'suggestions_result': self.suggestions_result,
# # 'suggestions_result_asset': self.suggestions_result_asset,
# # 'return_compare_pic': self.return_compare_pic,
# # 'indicator_compare': self.indicator_compare,
# # 'new_group_evaluation': self.new_group_evaluation
# 'new_correlation': [],
# 'propose_fund_data_list': [],
# 'suggestions_result': {},
# 'suggestions_result_asset': {},
# 'return_compare_pic': [],
# 'indicator_compare': [],
# 'new_group_evaluation': []
}
# 开始渲染html模板
env = Environment(loader=PackageLoader('app', 'templates')) # 创建一个包加载器对象
# template = env.get_template('monthReport.html') # 获取一个模板文件
template = env.get_template('/v2/monthReportV2.1.html') # 获取一个模板文件
monthReport_html = template.render(data) # 渲染
# 保存 monthReport_html
# save_file = "app/html/monthReport.html"
# with open(save_file, 'w', encoding="utf-8") as f:
# f.write(monthReport_html)
# save_file = "app/html/v2/monthReportV2.html"
# with open(save_file, 'w', encoding="utf-8") as f:
# f.write(monthReport_html)
html_to_pdf(monthReport_html, pdf_folder + self.pdf_name)
if __name__ == '__main__':
start = time.time()
DataIntegrate(ifa_id='USER_INFO15916072577875', customer_id='6716613804182482944')
print('耗时{}秒'.format(round(time.time()-start, 2)))
# -*- coding: UTF-8 -*-
"""
@author: Zongxi.Li
@file:portfolio_diagnose.py
@time:2020/12/07
"""
import warnings
warnings.filterwarnings("ignore")
from app.utils.fund_rank import *
from app.utils.risk_parity import *
from app.pypfopt import risk_models
from app.pypfopt import expected_returns
from app.pypfopt import EfficientFrontier
from app.api.engine import tamp_product_engine, tamp_fund_engine, TAMP_SQL
def cal_correlation(prod):
"""计算组合内基金相关性
Args:
prod: 组合净值表:索引为日期,列名为基金ID, 内容为净值
Returns:屏蔽基金与自身相关性的相关矩阵,因为基金与自身相关性为1,妨碍后续高相关性基金筛选的判断
"""
prod_return = prod.iloc[:, :].apply(lambda x: simple_return(x).astype(float))
correlation = prod_return.corr()
correlation = correlation.round(2)
return correlation.mask(np.eye(correlation.shape[0], dtype=np.bool_))
def rename_col(df, fund_id):
"""将列名由adj_nav改为基金ID
Args:
df: 原始净值表:索引为日期,列名分别为 ”fund_id“, "adj_nav", 内容为[基金ID,净值]
fund_id: 基金ID
Returns:删除 ”fund_id” 列, 重命名 “adj_nav” 列为基金ID的净值表
"""
df.rename(columns={'adj_nav': fund_id}, inplace=True)
df.drop('fund_id', axis=1, inplace=True)
return df
def replace_fund(manager, substrategy, fund_rank):
"""查找不足半年数据的基金的替代基金
Args:
manager: 基金经理ID
substrategy: 基金二级策略
fund_rank: 基金打分排名表
Returns: 满足相同基金经理ID下的同种二级策略的基金ID的第一个结果
"""
df = fund_rank[(fund_rank['manager'] == manager) &
(fund_rank['substrategy'] == substrategy)]
return df['fund_id'].values[0]
def search_rank(fund_rank, fund, metric):
"""查找基金在基金排名表中的指标
Args:
fund_rank: 基金排名表
fund: 输入基金ID
metric: 查找的指标名称
Returns: 基金指标的值
"""
return fund_rank[fund_rank['fund_id'] == fund][metric].values[0]
def translate_single(content, content_id, evaluation):
'''
content = [["优秀","良好","一般"],
["优秀","良好","合格","较差"],
["优秀","良好","合格","较差"],
["高","一般","较低"]]
evaluation = [0,1,1,2]
'''
ret = []
for i, v in enumerate(evaluation):
if isinstance(v, str):
ret.append(v)
continue
elif content[content_id][i][v] in ["优秀", "良好", "高", "高于", "较好"]:
ret.append("""<span class="self_description_red">{}</span>""".format(content[content_id][i][v]))
continue
elif content_id == 4 and v == 0:
ret.append("""<span class="self_description_red">{}</span>""".format(content[content_id][i][v]))
continue
else:
ret.append("""<span class="self_description_green">{}</span>""".format(content[content_id][i][v]))
return tuple(ret)
def choose_good_evaluation(evaluation):
"""抽取好的评价
Args:
evaluation: 个基的评价
Returns: 个基好的评价
"""
v1 = evaluation[1]
v2 = evaluation[2]
v3 = evaluation[3]
v4 = evaluation[4]
v5 = evaluation.get(5)
if v1[0] > 1:
del evaluation[1]
if v2[0] > 1 and float(v2[1].strip('%')) <= 60:
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:
del evaluation[5]
return evaluation
def choose_bad_evaluation(evaluation):
v1 = evaluation[1]
v2 = evaluation[2]
v3 = evaluation[3]
v4 = evaluation[4]
if v1[0] < 2:
del evaluation[1]
if v2[0] < 2:
del evaluation[2]
if v3[0] < 2:
del evaluation[3]
if v4[0] != 1 or v4[1] != 1:
del evaluation[4]
return evaluation
def get_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_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',
'substrategy', 'manager', 'annual_return_rank', 'downside_risk_rank',
'max_drawdown_rank', 'sharp_ratio_rank', 'z_score'])
df.drop('index', axis=1, inplace=True)
return df
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='{}' 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()
df = pd.DataFrame(list(data), columns=['ts_code', 'trade_date', ' close'])
df.rename({'ts_code': 'fund_id', 'trade_date': 'end_date', 'close': 'adj_nav'}, axis=1, inplace=True)
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_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, 终止日期为当前日期的基金净值表
Args:
fund[str]:基金ID
start_date[date]:起始日期
rollback[bool]:当起始日期不在净值公布日历中,是否往前取最近的净值公布日
public[bool]:是否为公募
Returns:df[DataFrame]: 索引为净值公布日, 列为复权净值的净值表; 查询失败则返回None
"""
with TAMP_SQL(tamp_product_engine) as tamp_product:
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')
cur = tamp_product_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():
while start_date not in list(df['end_date']):
start_date -= datetime.timedelta(days=1)
df = df[df['end_date'] >= start_date]
df.drop_duplicates(subset='end_date', inplace=True, keep='first')
df.set_index('end_date', inplace=True)
df.sort_index(inplace=True, ascending=True)
return df
def get_risk_level(substrategy):
"""获取风险类型
Args:
substrategy: 二级策略
Returns:
"""
substrategy2risk = {1: "H",
1010: "H", 1020: "H", 1030: "H",
2010: "H",
3010: "H", 3020: "L", 3030: "H", 3040: "L", 3050: "M",
4010: "M", 4020: "M", 4030: "M", 4040: "M",
5010: "M", 5020: "L", 5030: "M",
6010: "L", 6020: "M", 6030: "L",
7010: "H", 7020: "H",
8010: "H", 8020: "M"}
return substrategy2risk[substrategy]
def get_radar_data(fund):
df = fund_rank[fund_rank['fund_id'] == fund]
return_score = df['annual_return_rank'].values[0] * 100
downside_score = df['downside_risk_rank'].values[0] * 100
drawdown_score = df['max_drawdown_rank'].values[0] * 100
sharpe_score = df['sharp_ratio_rank'].values[0] * 100
total_score = df['z_score'].values[0]
fund_name = get_fund_name(fund).values[0][0]
return {'name': fund_name, 'data': [{'name': '绝对收益', 'data': '%.2f' % return_score},
{'name': '抗风险能力', 'data': '%.2f' % downside_score},
{'name': '极端风险', 'data': '%.2f' % drawdown_score},
{'name': '风险调整后收益', 'data': '%.2f' % sharpe_score},
{'name': '业绩持续性', 'data': '%.2f' % np.random.randint(70, 90)},
{'name': '综合评分', 'data': '%.2f' % total_score}]}
def get_fund_name(fund):
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_fund_session.execute(sql)
data = cur.fetchall()
df = pd.DataFrame(list(data), columns=['fund_short_name'])
return df
# 获取排名信息
fund_rank = get_fund_rank()
# 获取探普产品池
tamp_fund = get_tamp_fund()
class PortfolioDiagnose(object):
def __init__(self, client_type, portfolio, invest_amount, expect_return=0.1,
expect_drawdown=0.15, 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: 诊断所需净值的结束日期
"""
self.freq_list = []
self.client_type = client_type
self.portfolio = portfolio
self.expect_return = expect_return
self.expect_drawdown = expect_drawdown
self.index_id = index_id
self.invest_amount = invest_amount
self.invest_type = invest_type
self.start_date = start_date
self.end_date = end_date
if self.end_date is None:
self.end_date = datetime.datetime(datetime.date.today().year,
datetime.date.today().month, 1) - datetime.timedelta(1)
self.start_date = cal_date(self.end_date, 'Y', 1)
self.replace_pair = dict() # 由于数据不足半年而被替换为相同基金经理和策略的原基金和替换基金的映射
self.no_data_fund = [] # 未在数据库中找到基金净值或者基金经理记录的基金
self.abandon_fund_score = [] # 打分不满足要求的基金
self.abandon_fund_corr = [] # 相关性过高
self.proposal_fund = [] # 建议的基金
self.old_correlation = None
self.new_correlation = None
self.old_weights = None
self.new_weights = None
self.origin_portfolio = None
self.abandoned_portfolio = None
self.propose_portfolio = None
def get_portfolio(self, ):
"""获取组合净值表
Returns:
"""
# 获取原始投资组合的第一支基金的净值表
prod = get_tamp_nav(self.portfolio[0], self.start_date, invest_type=self.invest_type)
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:
# 获取的净值表为空时首先考虑基金净值数据不足半年,查找同一基金经理下的相同二级策略的基金ID作替换
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:
# 替换基金数据非空则记录替换的基金对
prod = get_nav(replaced_fund, self.start_date, invest_type=self.invest_type)
self.replace_pair[self.portfolio[0]] = replaced_fund
else:
# 替换基金数据为空则记录当前基金为找不到数据的基金, 继续尝试获取下一个基金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)
# 记录基金的公布频率
self.freq_list.append(get_frequency(prod))
prod = rename_col(prod, self.portfolio[0])
# 循环拼接基金净值表构建组合
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):
if prod1 is None:
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(self.portfolio[idx + 1])
continue
if replaced_fund:
prod1 = get_nav(replaced_fund, self.start_date, invest_type=self.invest_type)
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(self.portfolio[idx + 1])
continue
else:
self.freq_list.append(get_frequency(prod1))
prod1 = rename_col(prod1, self.portfolio[idx + 1])
# 取prod表和prod1表的并集
prod = pd.merge(prod, prod1, on=['end_date'], how='outer')
# 对所有合并后的基金净值表按最大周期进行重采样
prod.sort_index(inplace=True)
prod.ffill(inplace=True)
prod = resample(prod, get_trade_cal(), min(self.freq_list))
prod.dropna(how='any', inplace=True)
return prod
def abandon(self, prod):
"""建议替换的基金
Args:
prod: 原始组合净值表
Returns: 剔除建议替换基金的组合净值表
"""
self.old_correlation = cal_correlation(prod)
for fund in prod.columns:
z_score = search_rank(fund_rank, fund, metric='z_score')
# 建议替换得分为60或与其他基金相关度大于0.8的基金
if z_score < 60:
self.abandon_fund_score.append(fund)
continue
elif np.any(self.old_correlation[fund] > 0.8):
self.abandon_fund_corr.append(fund)
prod = prod.drop(self.abandon_fund_score + self.abandon_fund_corr, 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
def proposal(self, prod):
"""建议申购基金
Args:
prod: 剔除建议替换基金的组合净值表
Returns: 增加建议申购基金的组合净值表
"""
# 组合内已包含的策略
# included_strategy = set()
# 按每种基金最少投资100w确定组合包含的最大基金数量
max_len = len(self.portfolio) - len(prod.columns)
# 排名表内包含的所有策略
# 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
# 遍历产品池,推荐得分>80且与组合内其他基金相关度低于0.8的属于待添加策略的基金
for proposal in tamp_fund['fund_id']:
if proposal in fund_rank['fund_id'].to_list() and proposal not in prod.columns:
proposal_z_score = search_rank(fund_rank, proposal, metric='z_score')
proposal_strategy = fund_rank[fund_rank['fund_id'] == proposal]['substrategy'].values[0]
else:
continue
if proposal_z_score > 60 and (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)
prod.dropna(how='all', 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()
self.origin_portfolio = self.get_portfolio()
end1 = time.time()
print("原始组合数据获取时间:", end1 - start)
self.abandoned_portfolio = self.abandon(self.origin_portfolio)
end2 = time.time()
print("计算换仓基金时间:", end2 - end1)
self.propose_portfolio = self.proposal(self.abandoned_portfolio)
end3 = time.time()
print("遍历产品池获取候选推荐时间:", end3 - end2)
# propose_portfolio.to_csv('test_portfolio.csv', encoding='gbk')
mu = expected_returns.mean_historical_return(self.propose_portfolio, frequency=min(self.freq_list))
S = risk_models.sample_cov(self.propose_portfolio, frequency=min(self.freq_list))
dd = expected_returns.drawdown_from_prices(self.propose_portfolio)
# 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()
propose_risk_mapper = dict()
for fund in self.propose_portfolio.columns:
propose_risk_mapper[fund] = str(get_risk_level(search_rank(fund_rank, fund, metric='substrategy')))
if self.client_type == 1:
risk_upper = {"L": 0.6, "M": 0.4, "H": 0.0}
risk_lower = {"L": 0.6, "M": 0.4, "H": 0.0}
elif self.client_type == 2:
risk_upper = {"L": 0.5, "M": 0.3, "H": 0.2}
risk_lower = {"L": 0.5, "M": 0.3, "H": 0.2}
elif self.client_type == 3:
risk_upper = {"L": 0.3, "M": 0.5, "H": 0.2}
risk_lower = {"L": 0.3, "M": 0.5, "H": 0.2}
elif self.client_type == 4:
risk_upper = {"L": 0.3, "M": 0.4, "H": 0.3}
risk_lower = {"L": 0.3, "M": 0.4, "H": 0.3}
elif self.client_type == 5:
risk_upper = {"L": 0.0, "M": 0.5, "H": 0.5}
risk_lower = {"L": 0.0, "M": 0.5, "H": 0.5}
else:
risk_upper = {"H": 1.0}
risk_lower = {"L": 0.0}
raise ValueError
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)
clean_weights = ef.clean_weights()
ef.portfolio_performance(verbose=True)
self.new_weights = np.array(list(clean_weights.values()))
except:
self.new_weights = np.asarray([1/len(self.propose_portfolio.columns)] * len(self.propose_portfolio.columns))
print(self.new_weights)
end4 = time.time()
print("模型计算一次时间:", end4 - end3)
# S = np.asmatrix(S)
# w_origin = np.asarray([i for i in w_origin.values()])
# risk_target = np.asarray([1 / len(w_origin)] * len(w_origin))
# self.proposal_weights = calcu_w(w_origin, S, risk_target)
# elif self.client_type == 2:
# elif self.client_type == 3:
# elif self.client_type == 4:
# elif self.client_type == 5:
# print(len(propose_portfolio.columns))
# # 单支基金占投资额的下界为 100W/投资总额
# # w_low = 1e6 / self.invest_amount
# w_low = 0
# w_origin, S, mu = optim_drawdown(propose_portfolio, 0.5, [w_low, 1], min(self.freq_list))
# print(w_origin)
# S = np.asmatrix(S)
# w_origin = np.asarray([i for i in w_origin.values()])
# risk_target = np.asarray([1 / len(w_origin)] * len(w_origin))
# self.proposal_weights = calcu_w(w_origin, S, risk_target)
def return_compare(self):
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
propose_fund_return = self.propose_portfolio.iloc[:, :] / self.propose_portfolio.iloc[0, :] - 1
propose_fund_return['return'] = propose_fund_return.T.iloc[:, :].apply(lambda x: np.dot(self.new_weights, x))
return index_return, propose_fund_return
def old_evaluation(self, group_name, group_result, data_adaptor):
start_year = data_adaptor.start_date.year
start_month = data_adaptor.start_date.month
current_year = data_adaptor.end_date.year
current_month = data_adaptor.end_date.month
current_day = data_adaptor.end_date.day
past_month = (current_year - start_year) * 12 + current_month - start_month
# 投入成本(万元)
input_cost = round(group_result[group_name]["total_cost"] / 10000, 2)
# 整体盈利(万元)
total_profit = round(group_result[group_name]["cumulative_profit"] / 10000, 2)
# 整体表现 回撤能力
fund_rank_data = fund_rank[fund_rank["fund_id"].isin(self.portfolio)]
z_score = fund_rank_data["z_score"].mean()
drawdown_rank = fund_rank_data["max_drawdown_rank"].mean()
return_rank_df = fund_rank_data["annual_return_rank"]
z_score_level = np.select([z_score >= 80,
70 <= z_score < 80,
z_score < 70], [0, 1, 2]).item()
drawdown_level = np.select([drawdown_rank >= 0.8,
0.7 <= drawdown_rank < 0.8,
0.6 <= drawdown_rank < 0.7,
drawdown_rank < 0.6], [0, 1, 2, 3]).item()
# 收益稳健
fund_rank_re = fund_rank_data[fund_rank_data["annual_return_rank"] > 0.8]
return_rank_evaluate = ""
if len(fund_rank_re) > 0:
num = len(fund_rank_re)
fund_id_rank_list = list(fund_rank_re["fund_id"])
for f_id in fund_id_rank_list:
name = data_adaptor.user_customer_order_df[data_adaptor.user_customer_order_df["fund_id"] == f_id][
"fund_name"].values[0]
return_rank_evaluate = return_rank_evaluate + name + "、"
return_rank_evaluate = return_rank_evaluate[:-1] + "等" + str(num) + "只产品稳健,对组合的收益率贡献明显,"
# 正收益基金数量
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()
if profit_positive_num > 0:
profit_positive_evaluate = str(profit_positive_num) + "只基金取的正收益,"
else:
profit_positive_evaluate = ""
# 综合得分较低数量
abandon_num = len(self.abandon_fund_score)
abandon_evaluate = str(abandon_num) + "只基金综合得分较低建议更换,"
# 成立时间短
if len(self.no_data_fund) > 0:
no_data_fund_evaluate = str(len(self.no_data_fund)) + "只基金因为成立时间较短,暂不做评价;"
else:
no_data_fund_evaluate = ";"
group_order_df = data_adaptor.user_customer_order_df[
data_adaptor.user_customer_order_df["folio_name"] == group_name]
strategy_list = group_order_df["substrategy"]
uniqe_strategy = list(strategy_list.unique())
uniqe_strategy_name = [dict_substrategy[int(x)] + "、" for x in uniqe_strategy]
# 覆盖的基金名称
strategy_name_evaluate = "".join(uniqe_strategy_name)[:-1]
try:
if len(uniqe_strategy) / float(len(strategy_list)) > 0.6:
strategy_distribution_evaluate = "策略上有一定分散"
else:
strategy_distribution_evaluate = "策略分散程度不高"
except ZeroDivisionError:
strategy_distribution_evaluate = "策略分散程度不高"
# 相关性
if len(self.abandon_fund_corr) > 0:
fund_corr_name = [str(group_order_df[group_order_df["fund_id"] == f_id]["fund_name"].values[0]) + "和" for
f_id in self.abandon_fund_corr]
fund_corr_evaluate = "".join(fund_corr_name)[:-1] + "相关性较高,建议调整组合配比;"
else:
fund_corr_evaluate = ";"
num_fund = len(self.portfolio)
evaluate_enum = [["优秀", "良好", "一般"],
["优秀", "良好", "合格", "较差"]]
z_score_evaluate = evaluate_enum[0][z_score_level]
drawdown_evaluate = evaluate_enum[1][drawdown_level]
if z_score_evaluate in ["优秀", "良好"]:
z_score_evaluate = """<span class="self_description_red">{}</span>""".format(z_score_evaluate)
else:
z_score_evaluate = """<span class="self_description_green">{}</span>""".format(z_score_evaluate)
if drawdown_evaluate in ["优秀", "良好"]:
drawdown_evaluate = """<span class="self_description_red">{}</span>""".format(drawdown_evaluate)
else:
drawdown_evaluate = """<span class="self_description_green">{}</span>""".format(drawdown_evaluate)
sentence = {
1: "1、组合构建于{}年{}月,至今已运行{}个月。投入成本为{}万元,截止{}年{}月{}日,整体盈利{}万元,整体表现{},回撤控制能力{};\n",
2: "2、组合共持有{}只基金,{}{}{}{}\n",
3: "3、策略角度来看,组合涵盖了{}, {}{}\n"
}
data = {1: [start_year, start_month, past_month, input_cost, current_year, current_month, current_day,
total_profit, z_score_evaluate, drawdown_evaluate],
2: [num_fund, return_rank_evaluate, profit_positive_evaluate, abandon_evaluate, no_data_fund_evaluate],
3: [strategy_name_evaluate, strategy_distribution_evaluate, fund_corr_evaluate]
}
ret = []
for k, v in data.items():
ret.append(sentence[k].format(*data[k]).replace(",;", ";"))
# 旧组合累积收益df
group_result_data = group_result[group_name]
hold_info = group_result_data["group_hoding_info"]
hold_info_df = pd.DataFrame(hold_info)
group_order_df = data_adaptor.user_customer_order_df[
data_adaptor.user_customer_order_df["folio_name"] == group_name]
group_order_start_date = pd.to_datetime(group_order_df["confirm_share_date"].min())
freq_max = group_order_df["freq"].max()
n_freq = freq_days(int(freq_max))
old_return_df = group_result_data["return_df"]
old_return_df["cum_return_ratio"] = old_return_df["cum_return_ratio"] - 1
# 原组合总市值, 区间收益, 年化收益, 波动率, 最大回撤, 夏普比率
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"]
old_volatility = group_result_data["volatility"]
old_max_drawdown = group_result_data["max_drawdown"]
old_sharpe = group_result_data["sharpe"]
# 指数收益
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
# 指数收益
index_return = index_return[index_return.index >= group_order_start_date]
start_index_return = index_return[" close"].values[0]
index_return["new_index_return"] = (index_return[" close"] - start_index_return) / (1 + start_index_return)
index_return_ratio = index_return["new_index_return"].values[-1]
index_return_ratio_year = annual_return(index_return["new_index_return"].values[-1],
index_return["new_index_return"], n_freq)
index_volatility = volatility(index_return["new_index_return"] + 1, n_freq)
index_drawdown = max_drawdown(index_return["new_index_return"] + 1)
index_sim = simple_return(index_return["new_index_return"]+1)
index_exc = excess_return(index_sim, BANK_RATE, n_freq)
index_sharpe = sharpe_ratio(index_exc, index_sim, n_freq)
# 收益对比数据
return_compare_df = pd.merge(index_return[["new_index_return"]], old_return_df[["cum_return_ratio"]],
right_index=True,
left_index=True)
return_compare_df["date"] = return_compare_df.index
return_compare_df["date"] = return_compare_df["date"].apply(lambda x: x.strftime("%Y-%m-%d"))
return_compare_df.iloc[1:-1, :]["date"] = ""
old_return_compare_result = {
"index": {"name": "中证500", "data": return_compare_df["new_index_return"].values*100},
"origin_combination": {"name": "原组合", "data": return_compare_df["cum_return_ratio"].values*100},
"xlabels": return_compare_df["date"].values
}
# 指标对比
old_indicator = {"group_name": "现有持仓组合", "return_ratio": round((old_return - 1) * 100, 2),
"return_ratio_year": round(old_return_ratio_year * 100, 2),
"volatility": round(old_volatility * 100, 2),
"max_drawdown": round(old_max_drawdown[0] * 100, 2), "sharpe": round(old_sharpe, 2)}
index_indicator = {"group_name": "中证500", "return_ratio": round(index_return_ratio * 100, 2),
"return_ratio_year": round(index_return_ratio_year * 100, 2),
"volatility": round(index_volatility * 100, 2),
"max_drawdown": round(index_drawdown[0] * 100, 2), "sharpe": round(index_sharpe, 2)}
old_indicator_compare = [old_indicator, index_indicator]
return ret, old_return_compare_result, old_indicator_compare
def new_evaluation(self, group_name, group_result, data_adaptor):
try:
group_result_data = group_result[group_name]
hold_info = group_result_data["group_hoding_info"]
hold_info_df = pd.DataFrame(hold_info)
group_order_df = data_adaptor.user_customer_order_df[
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"]
old_volatility = group_result_data["volatility"]
old_max_drawdown = group_result_data["max_drawdown"]
old_sharpe = group_result_data["sharpe"]
# 建议基金数据
index_return, propose_fund_return = self.return_compare()
propose_fund_id_list = list(propose_fund_return.columns)
propose_fund_id_list.remove("return")
with TAMP_SQL(tamp_product_engine) as tamp_product:
tamp_product_session = tamp_product.session
sql_product = "select distinct `id`, `fund_short_name`, `nav_frequency`, `substrategy` from `fund_info`"
cur = tamp_product_session.execute(sql_product)
data = cur.fetchall()
product_df = pd.DataFrame(list(data), columns=['fund_id', 'fund_name', 'freq', 'substrategy'])
propose_fund_df = product_df[product_df["fund_id"].isin(propose_fund_id_list)]
# 基金名称,策略分级
propose_fund_id_name_list = [propose_fund_df[propose_fund_df["fund_id"] == fund_id]["fund_name"].values[0] for
fund_id in propose_fund_id_list]
propose_fund_id_strategy_name_list = [dict_substrategy[int(propose_fund_df[propose_fund_df["fund_id"] == fund_id]["substrategy"].values[0])] for
fund_id in propose_fund_id_list]
propose_fund_asset = [round(self.new_weights[i] * total_asset, 2) for i in range(len(propose_fund_id_name_list))]
propose_info = {propose_fund_id_strategy_name_list[i]:
{"fund_name": propose_fund_id_name_list[i],
"substrategy": propose_fund_id_strategy_name_list[i],
"asset": propose_fund_asset[i]}
for i in range(len(propose_fund_id_list))}
# 调仓建议
suggestions_result = {}
old_hold_fund_name_list = list(hold_info_df["fund_name"])
for hold in hold_info:
suggestions = {}
if hold["fund_strategy_name"] not in suggestions_result.keys():
suggestions_result[hold["fund_strategy_name"]] = {}
suggestions["fund_strategy_name"] = hold["fund_strategy_name"]
suggestions["fund_name"] = hold["fund_name"]
suggestions["before_optimization"] = hold["market_values"]
suggestions["after_optimization"] = 0
if suggestions["fund_strategy_name"] in propose_fund_id_strategy_name_list:
suggestions["after_optimization"] = 0
suggestions_result[hold["fund_strategy_name"]][suggestions["fund_name"]] = suggestions
for key, value in propose_info.items():
if value["fund_name"] not in old_hold_fund_name_list:
suggestions = {}
if key not in suggestions_result.keys():
suggestions_result[key] = {}
suggestions["fund_strategy_name"] = value["substrategy"]
suggestions["fund_name"] = value["fund_name"]
suggestions["before_optimization"] = 0
suggestions["after_optimization"] = value["asset"]
suggestions_result[key][suggestions["fund_name"]] = suggestions
for key, value in suggestions_result.items():
suggestions_result[key] = list(value.values())
suggestions_result_asset = {"before": total_asset, "after": total_asset}
# 旧组合累积收益df
old_return_df = group_result_data["return_df"]
old_return_df["cum_return_ratio"] = old_return_df["cum_return_ratio"] - 1
# 新组合累积收益df
propose_fund_return_limit_data = propose_fund_return[propose_fund_return.index >= group_order_start_date]
start_return = propose_fund_return_limit_data['return'].values[0]
propose_fund_return_limit_data["new_return"] = (propose_fund_return_limit_data["return"] - start_return)/(1+start_return)
# 新组合累积收益
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(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)
# 新组合最大回撤
new_drawdown = max_drawdown(propose_fund_return_limit_data["new_return"]+1)
# 新组合夏普比率
sim = simple_return(propose_fund_return_limit_data["new_return"]+1)
exc = excess_return(sim, BANK_RATE, n_freq)
new_sharpe = sharpe_ratio(exc, sim, n_freq)
# 指数收益
index_return = index_return[index_return.index >= group_order_start_date]
start_index_return = index_return[" close"].values[0]
index_return["new_index_return"] = (index_return[" close"] - start_index_return) / (1 + start_index_return)
index_return_ratio = index_return["new_index_return"].values[-1]
index_return_ratio_year = annual_return(index_return["new_index_return"].values[-1], index_return["new_index_return"], n_freq)
index_volatility = volatility(index_return["new_index_return"]+1, n_freq)
index_drawdown = max_drawdown(index_return["new_index_return"]+1)
index_sim = simple_return(index_return["new_index_return"]+1)
index_exc = excess_return(index_sim, BANK_RATE, n_freq)
index_sharpe = sharpe_ratio(index_exc, index_sim, n_freq)
# 收益对比数据
return_compare_df = pd.merge(index_return[["new_index_return"]], old_return_df[["cum_return_ratio"]], right_index=True,
left_index=True)
return_compare_df = pd.merge(return_compare_df, propose_fund_return_limit_data["new_return"], right_index=True,
left_index=True)
return_compare_df["date"] = return_compare_df.index
return_compare_df["date"] = return_compare_df["date"].apply(lambda x: x.strftime("%Y-%m-%d"))
return_compare_df.iloc[1:-1,:]["date"] = ""
return_compare_result = {
"new_combination": {"name": "新组合", "data": return_compare_df["new_return"].values*100},
"index": {"name": "中证500", "data": return_compare_df["new_index_return"].values*100},
"origin_combination": {"name": "原组合", "data": return_compare_df["cum_return_ratio"].values*100},
"xlabels": return_compare_df["date"].values
}
# 指标对比
old_indicator = {"group_name": "现有持仓组合", "return_ratio": round((old_return-1)*100, 2), "return_ratio_year": round(old_return_ratio_year*100,2),
"volatility": round(old_volatility*100, 2), "max_drawdown": round(old_max_drawdown[0]*100, 2), "sharpe": round(old_sharpe, 2)}
new_indicator = {"group_name": "建议优化组合", "return_ratio": round(new_return_ratio*100, 2), "return_ratio_year": round(new_return_ratio_year*100, 2),
"volatility": round(new_volatility*100, 2), "max_drawdown": round(new_drawdown[0]*100, 2), "sharpe": round(new_sharpe, 2)}
index_indicator = {"group_name": "中证500", "return_ratio": round(index_return_ratio*100, 2), "return_ratio_year": round(index_return_ratio_year*100, 2),
"volatility": round(index_volatility*100, 2), "max_drawdown": round(index_drawdown[0]*100, 2), "sharpe": round(index_sharpe, 2)}
indicator_compare = [new_indicator, old_indicator, index_indicator]
# 在保留{}的基础上,建议赎回{},并增配{}后,整体组合波动率大幅降低,最大回撤从{}降到不足{},年化收益率提升{}个点
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]
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]
proposal_fund = self.proposal_fund
proposal_fund_name = [get_fund_name(x).values[0][0] for x in proposal_fund]
sentence = []
if hold_fund is not None:
sentence.append("在保留" + "".join([i + "," for i in hold_fund_name]).rstrip(",") + "的基础上")
if abandon_fund is not None:
sentence.append("建议赎回" + "".join([i + "," for i in abandon_fund_name]).rstrip(","))
if proposal_fund is not None:
sentence.append("增配" + "".join([i + "," for i in proposal_fund_name]).rstrip(",") + "后")
if new_volatility < old_volatility * 0.9:
sentence.append("整体组合波动率大幅降低")
if new_drawdown < old_max_drawdown:
sentence.append("最大回撤从{:.2%}降到不足{:.2%}".format(old_max_drawdown[0], new_drawdown[0]))
if new_return_ratio_year > old_return_ratio_year:
sentence.append("年化收益率提升{:.2f}个点".format((new_return_ratio_year - old_return_ratio_year) * 100))
whole_sentence = ",".join(sentence).lstrip(",") + "。"
return suggestions_result, suggestions_result_asset, return_compare_result, indicator_compare, whole_sentence
except Exception as e:
repr(e)
return None, None, None, None, None
def single_evaluation(self, fund_id, objective=False):
"""
1、该基金整体表现优秀/良好/一般,收益能力优秀/良好/合格/较差,回撤控制能力优秀/良好/合格/较差,风险收益比例较高/一般/较低;
2、在收益方面,该基金年化收益能力高于/持平/低于同类基金平均水平,有x%区间跑赢大盘/指数,绝对收益能力优秀/一般;
3、在风险方面,该基金抵御风险能力优秀/良好/一般,在同类基金中处于高/中/低等水平,最大回撤为x%,高于/持平/低于同类基金平均水平;
4、该基金收益较好/较差的同时回撤较大/较小,也就是说,该基金在用较大/较小风险换取较大/较小收益,存在较高/较低风险;
5、基金经理,投资年限5.23年,经验丰富;投资能力较强,生涯中共管理过X只基金,历任的X只基金平均业绩在同类中处于上游水平,其中x只排名在前x%;生涯年化回报率x%,同期大盘只有x%
旧个基显示1-4,新个基显示1-5。
旧个基如果是要保留的,显示好的评价。
如果是要剔除的,显示坏的评价。
新个基只显示好的评价。
Args:
fund_id:
Returns:
"""
z_score = search_rank(fund_rank, fund_id, metric='z_score')
total_level = np.select([z_score >= 80,
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() / compare[fund_id].count()
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,
0.6 <= return_rank < 0.7,
return_rank < 0.6], [0, 1, 2, 3]).item()
return_bool = 1 if return_level > 2 else 0
return_triple = return_level - 1 if return_level >= 2 else return_level
drawdown_rank = search_rank(fund_rank, fund_id, metric='max_drawdown_rank')
drawdown_value = search_rank(fund_rank, fund_id, metric='max_drawdown')
drawdown_level = np.select([drawdown_rank >= 0.8,
0.7 <= drawdown_rank < 0.8,
0.6 <= drawdown_rank < 0.7,
drawdown_rank < 0.6], [0, 1, 2, 3]).item()
drawdown_bool = 1 if drawdown_level > 2 else 0
drawdown_triple = drawdown_level - 1 if drawdown_level >= 2 else drawdown_level
sharp_rank = search_rank(fund_rank, fund_id, metric='sharp_ratio_rank')
sharp_level = np.select([sharp_rank >= 0.8,
0.6 <= sharp_rank < 0.8,
sharp_rank < 0.6], [0, 1, 2]).item()
data = {1: [total_level, return_level, drawdown_level, sharp_level],
2: [return_triple, format(fund_win_rate, '.2%'), return_bool],
3: [drawdown_triple, drawdown_triple, format(drawdown_value, '.2%'), drawdown_triple],
4: [return_bool, drawdown_bool, drawdown_bool, return_bool, drawdown_bool]}
if fund_id in self.abandon_fund_score:
data['remove'] = True
elif fund_id in self.proposal_fund:
data[5] = [1] * 7
data['remove'] = False
else:
data['remove'] = False
x = '30%'
content = {
# 第一个评价
1: [["优秀", "良好", "一般"],
["优秀", "良好", "合格", "较差"],
["优秀", "良好", "合格", "较差"],
["高", "一般", "较低"]],
# 第二个评价
2: [["高于", "持平", "低于"],
x,
["优秀", "一般"]],
# 第三个评价
3: [["优秀", "良好", "一般"],
["高", "中", "低"], x,
["高于", "持平", "低于"]],
# 第四个评价
4: [["较好", "较差"],
["较小", "较大"],
["较小", "较小"],
["较大", "较小"],
["较低", "较高"]],
5: [["TO DO"]] * 7}
sentence = {
1: "该基金整体表现%s,收益能力%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;"}
remove = data["remove"]
del data["remove"]
# 不剔除,选择好的话术
if not remove:
evaluation = choose_good_evaluation(data)
# 剔除,选择坏的话术
else:
evaluation = choose_bad_evaluation(data)
ret = []
i = 1
for k, v in evaluation.items():
single_sentence = str(i) + "、" + sentence[k] % translate_single(content, k, v)
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 objective:
if fund_id in self.abandon_fund_score + self.abandon_fund_corr:
evaluation_dict['status'] = "换仓"
elif fund_id in self.portfolio:
evaluation_dict['status'] = "保留"
else:
evaluation_dict['status'] = ""
return evaluation_dict
def old_portfolio_evaluation(self, objective=False):
try:
result = []
for fund in self.portfolio:
try:
result.append(self.single_evaluation(fund, objective))
except IndexError:
continue
return result
except Exception as e:
repr(e)
return None
def propose_fund_evaluation(self, ):
try:
result = []
for fund in self.proposal_fund:
result.append(self.single_evaluation(fund))
return result
except Exception as e:
repr(e)
return None
def single_fund_radar(self):
radar_data = []
for fund in self.portfolio:
try:
radar_data.append(get_radar_data(fund))
except IndexError:
continue
return radar_data
def propose_fund_radar(self):
radar_data = []
for fund in self.proposal_fund:
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
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