From a570b2f074e9ea537100387eb89305a647307b81 Mon Sep 17 00:00:00 2001 From: zhaojie01 <zhaojie01@wealthgrow.cn> Date: Tue, 22 Dec 2020 15:43:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/service/portfolio_diagnose.py | 44 +++++++++++++++++++-------- app/templates/v2/monthReportV2.1.html | 16 +++++----- app/utils/fund_rank.py | 7 +++-- app/utils/jinjia2html_v2.py | 14 +++++---- app/utils/week_evaluation.py | 9 +++++- 5 files changed, 61 insertions(+), 29 deletions(-) diff --git a/app/service/portfolio_diagnose.py b/app/service/portfolio_diagnose.py index 183e0e3..3862efc 100644 --- a/app/service/portfolio_diagnose.py +++ b/app/service/portfolio_diagnose.py @@ -381,8 +381,10 @@ class PortfolioDiagnose(object): if self.end_date is None: self.end_date = datetime.datetime(datetime.date.today().year, datetime.date.today().month, 1) - datetime.timedelta(1) + if self.start_date is None: self.start_date = cal_date(self.end_date, 'Y', 1) - + else: + self.start_date = datetime.datetime(start_date.year, start_date.month, start_date.day) self.replace_pair = dict() # 由于数æ®ä¸è¶³åŠå¹´è€Œè¢«æ›¿æ¢ä¸ºç›¸åŒåŸºé‡‘ç»ç†å’Œç–略的原基金和替æ¢åŸºé‡‘çš„æ˜ å°„ self.no_data_fund = [] # 未在数æ®åº“䏿‰¾åˆ°åŸºé‡‘净值或者基金ç»ç†è®°å½•的基金 self.abandon_fund_score = [] # æ‰“åˆ†ä¸æ»¡è¶³è¦æ±‚的基金 @@ -544,15 +546,16 @@ class PortfolioDiagnose(object): 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)) + prod_with_new_fund = pd.merge(prod, proposal_nav, how='outer', on='end_date').astype(float) + prod_with_new_fund.sort_index(inplace=True) + prod_with_new_fund.ffill(inplace=True) - self.new_correlation = cal_correlation(prod) + prod_with_new_fund = resample(prod_with_new_fund, get_trade_cal(), min(self.freq_list)) + self.new_correlation = cal_correlation(prod_with_new_fund) judge_correlation = self.new_correlation.fillna(0) if np.all(judge_correlation < 0.8): + prod = prod_with_new_fund self.proposal_fund.append(proposal) max_len -= 1 # add_strategy -= {proposal_strategy} @@ -561,7 +564,8 @@ class PortfolioDiagnose(object): if max_len == 0: break else: - prod.drop(columns=proposal, inplace=True) + # prod.drop(columns=proposal, inplace=True) + self.freq_list.pop() prod.dropna(how='all', inplace=True) prod.fillna(method='bfill', inplace=True) @@ -704,9 +708,9 @@ class PortfolioDiagnose(object): # æ£æ”¶ç›ŠåŸºé‡‘æ•°é‡ group_hold_data = pd.DataFrame(group_result[group_name]["group_hoding_info"]) - profit_positive_num = group_hold_data[group_hold_data["profit"] > 0]["profit"].count() + profit_positive_num = len(group_hold_data[group_hold_data["profit"] > 0]["fund_name"].unique()) if profit_positive_num > 0: - profit_positive_evaluate = str(profit_positive_num) + "åªåŸºé‡‘å–çš„æ£æ”¶ç›Šï¼Œ" + profit_positive_evaluate = str(profit_positive_num) + "åªåŸºé‡‘å–å¾—æ£æ”¶ç›Šå–å¾—æ£æ”¶ç›Šï¼Œ" else: profit_positive_evaluate = "" @@ -733,7 +737,7 @@ class PortfolioDiagnose(object): strategy_distribution_evaluate = "ç–略上有一定分散" else: strategy_distribution_evaluate = "ç–略分散程度ä¸é«˜" - except ZeroDivisionError: + except: strategy_distribution_evaluate = "ç–略分散程度ä¸é«˜" # 相关性 if len(self.abandon_fund_corr) > 0: @@ -824,7 +828,13 @@ class PortfolioDiagnose(object): return_compare_df = pd.merge(index_return[["new_index_return"]], old_return_df[["cum_return_ratio"]], right_index=True, left_index=True) + start = return_compare_df.index.values[0] + if start > pd.to_datetime(self.start_date): + row = [0, 0] + return_compare_df.loc[pd.to_datetime(self.start_date)] = row + return_compare_df["date"] = return_compare_df.index + return_compare_df.sort_values(by="date", inplace=True) 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 = { @@ -944,7 +954,12 @@ class PortfolioDiagnose(object): # 新组åˆå¤æ™®æ¯”率 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) + try: + new_sharpe = sharpe_ratio(exc, sim, n_freq) + if new_sharpe is None or math.isnan(new_sharpe): + new_sharpe = 0 + except: + new_sharpe = 0 # 指数收益 index_return = index_return[index_return.index >= group_order_start_date] @@ -956,7 +971,12 @@ class PortfolioDiagnose(object): 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) + try: + index_sharpe = sharpe_ratio(index_exc, index_sim, n_freq) + if index_sharpe is None or math.isnan(index_sharpe): + index_sharpe = 0.0 + except: + index_sharpe = 0.0 # æ”¶ç›Šå¯¹æ¯”æ•°æ® return_compare_df = pd.merge(index_return[["new_index_return"]], old_return_df[["cum_return_ratio"]], right_index=True, diff --git a/app/templates/v2/monthReportV2.1.html b/app/templates/v2/monthReportV2.1.html index 0d103f6..779654e 100644 --- a/app/templates/v2/monthReportV2.1.html +++ b/app/templates/v2/monthReportV2.1.html @@ -1041,28 +1041,28 @@ <table class="tss1_table"> <tr> <td> - {%if month_rise>=0%} + {%if (month_rise|float)>=0%} 本月涨幅:<span class="red">{{month_rise}}%</span> {%else%} 本月涨幅:<span class="green">{{month_rise}}%</span> {%endif%} </td> <td> - {%if now_month_income>=0%} + {%if (now_month_income| float) >=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%} + {%if (year_totoal_rate_of_return|float)>=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%} + {%if (now_year_income|float)>=0%} 今年累计收益:<span class="red">{{now_year_income}}å…ƒ</span> {%else%} 今年累计收益:<span class="green">{{now_year_income}}å…ƒ</span> @@ -1071,21 +1071,21 @@ </tr> <tr> <td> - {%if now_yield>=0%} + {%if (now_yield|float)>=0%} æˆç«‹ä»¥æ¥ç´¯è®¡æ”¶ç›ŠçŽ‡ï¼š<span class="red">{{now_yield}}%</span> {%else%} æˆç«‹ä»¥æ¥ç´¯è®¡æ”¶ç›ŠçŽ‡ï¼š<span class="green">{{now_yield}}%</span> {%endif%} </td> <td> - {%if now_annualised_return>=0%} + {%if (now_annualised_return|float)>=0%} 年化收益率:<span class="red">{{now_annualised_return}}%</span> {%else%} 年化收益率:<span class="green">{{now_annualised_return}}%</span> {%endif%} </td> <td> - {%if now_withdrawal>=0%} + {%if (now_withdrawal|float)>=0%} 最大回撤:<span class="red">{{now_withdrawal}}%</span> {%else%} 最大回撤:<span class="green">{{now_withdrawal}}%</span> @@ -1117,7 +1117,7 @@ </td> <td> - {%if total_profit>=0%} + {%if (total_profit|float)>=0%} 累计盈利:<span class="red">{{total_profit}}å…ƒ</span> {%else%} 累计盈利:<span class="green">{{total_profit}}å…ƒ</span> diff --git a/app/utils/fund_rank.py b/app/utils/fund_rank.py index a5f6059..488a563 100644 --- a/app/utils/fund_rank.py +++ b/app/utils/fund_rank.py @@ -80,7 +80,10 @@ def get_frequency(df): # freq_series = index_series - index_series.shift(1) freq_series = index_series.diff(1) logging.log(logging.DEBUG, freq_series.describe()) - f = freq_series.mode()[0].days + try: + f = freq_series.mode()[0].days + except: + return 250 if f in range(0, 3): return 250 elif f in range(6, 9): @@ -92,7 +95,7 @@ def get_frequency(df): elif f in range(110, 133): return 3 else: - raise ValueError + return 250 def get_trade_cal(): diff --git a/app/utils/jinjia2html_v2.py b/app/utils/jinjia2html_v2.py index d953a0b..a3382db 100644 --- a/app/utils/jinjia2html_v2.py +++ b/app/utils/jinjia2html_v2.py @@ -78,7 +78,8 @@ class DataIntegrate: self.all_folio_result[group_name] = cur_group_portfolio_result def get_portfolio_diagnose(self, portfolio, client_type=1, invest_amount=10000000): - portfolio_diagnose = PortfolioDiagnose(client_type=client_type, portfolio=portfolio, invest_amount=invest_amount) + portfolio_diagnose = PortfolioDiagnose(client_type=client_type, portfolio=portfolio, invest_amount=invest_amount, + start_date=self.user_customer.start_date) portfolio_diagnose.optimize() return portfolio_diagnose @@ -246,7 +247,7 @@ class DataIntegrate: 'team': template_folder + '/v2/img/default-user.png', # å…¨å±€æ•°æ® 'customer_name': self.customer_name, - 'year_month': self.user_customer.month_start_date.strftime("%Y-%m"), + 'year_month': self.user_customer.month_start_date.strftime("%Y{}%m{}").format("å¹´", "月"), '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, @@ -254,11 +255,11 @@ class DataIntegrate: 'customer_old': 42, 'customer_level': '平衡型', # ç»¼è¿°æ•°æ® - 'now_allocation_amount': self.total_cost, 'now_yield': self.now_yield, 'index_yield': self.index_yield, + 'now_allocation_amount': '{:,}'.format(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, + 'now_year_income': '{:,}'.format(self.now_year_income), 'now_month_income': '{:,}'.format(self.now_month_income), + 'final_balance': '{:,}'.format(self.final_balance), 'total_profit': '{:,}'.format(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, @@ -324,5 +325,6 @@ class DataIntegrate: if __name__ == '__main__': start = time.time() - DataIntegrate(ifa_id='USER_INFO15914346866762', customer_id='202009281545001') + dt = DataIntegrate(ifa_id='USER_INFO15917853924996', customer_id='6741679287251775488') + dt.render_data() print('耗时{}ç§’'.format(round(time.time()-start, 2))) diff --git a/app/utils/week_evaluation.py b/app/utils/week_evaluation.py index d8a3875..b472c57 100644 --- a/app/utils/week_evaluation.py +++ b/app/utils/week_evaluation.py @@ -49,7 +49,12 @@ def sharpe_ratio(excess_return, simple_return, n): n: æ•°æ®ç±»åž‹ï¼Œ 周(52), 月(12), 日(250) """ import math - d = math.sqrt(n) * excess_return / simple_return.std(ddof=1) + try: + d = math.sqrt(n) * excess_return / simple_return.std(ddof=1) + if d == float("inf") or d == float("-inf"): + return 0.0 + except: + return 0.0 return d @@ -294,6 +299,8 @@ def resample(df, trading_cal, freq): [DataFrame]: [é‡é‡‡æ ·åŽå‰”除ä¸åœ¨äº¤æ˜“日历ä¸çš„净值表和交易日历以净值日期为索引的åˆè¡¨] """ freq_dict = {1: 'B', 2: 'W-FRI', 3: 'M', 4: 'SM', 5: 'Q'} + if math.isnan(freq): + freq = 2 resample_freq = freq_dict[freq] # æŒ‰é‡‡æ ·é¢‘çŽ‡è¿›è¡Œé‡é‡‡æ ·å¹¶è¿›è¡Œå‡€å€¼çš„å‰å‘å¡«å…… df = df.resample(rule=resample_freq).ffill() -- 2.18.1