From 72450f19dd18a3d55a7c1eb354280862fff3f204 Mon Sep 17 00:00:00 2001 From: jackluson <18219112108@163.com> Date: Sat, 24 Sep 2022 15:29:07 +0800 Subject: [PATCH] feat: support multiple manager when outputing high-score-fund --- src/crud/query.py | 43 ++++++++++++++++++++++++ src/fund_strategy.py | 67 ++++++++++++++++++++++++++++--------- src/models/fund.py | 19 ++++++++--- src/sql_model/fund_query.py | 40 ---------------------- src/utils/index.py | 5 ++- 5 files changed, 114 insertions(+), 60 deletions(-) create mode 100644 src/crud/query.py diff --git a/src/crud/query.py b/src/crud/query.py new file mode 100644 index 0000000..ba87a45 --- /dev/null +++ b/src/crud/query.py @@ -0,0 +1,43 @@ +''' +Desc: +File: /query.py +Project: crud +File Created: Wednesday, 21st September 2022 10:54:27 pm +Author: luxuemin2108@gmail.com +----- +Copyright (c) 2022 Camel Lu +''' + +import sys +sys.path.append('./src') +from sqlalchemy.orm import Session +from sqlalchemy import and_ +from models.fund import FundBase, FundQuarter +from models.manager import ManagerAssoc +from models.var import engine + +session = Session(engine) + +def query_high_score_funds(quarter_index): + words = ['%指数%', '%C', '%E', '%H%'] + rule = and_(*[FundBase.fund_name.notlike(w) for w in words]) + res = session.query(FundQuarter,ManagerAssoc,FundBase).where(FundQuarter.fund_code == ManagerAssoc.fund_code).where(FundQuarter.fund_code == FundBase.fund_code).filter(FundQuarter.quarter_index == quarter_index, \ + FundQuarter.morning_star_rating_5 >= 3, # 5年评级大于等于3 + FundQuarter.morning_star_rating_3 == 5, # 3年评级等于5 + FundQuarter.stock_position_total >= 50, # 股票仓位大于50 + FundQuarter.stock_position_ten <= 60, # 十大股票仓位小于60 + FundQuarter.risk_assessment_sharpby > 1, # 夏普比例大于1 + FundQuarter.risk_rating_2 > 1, # 2年风险评级大于1 + FundQuarter.risk_rating_3 > 1, # 3年风险评级大于1 + FundQuarter.risk_rating_5 > 1, # 5年风险评级大于1 + # ManagerAssoc.manager_start_date < last_year_date, # 至少任职该基金一年 + FundQuarter.total_asset < 100, # 总规模资金小于100亿 + ).filter(rule).all() + return res + + +if __name__ == '__main__': + quarter_index = '2022-Q2' + fund_list = query_high_score_funds(quarter_index) + # print("fund_list",fund_list) + diff --git a/src/fund_strategy.py b/src/fund_strategy.py index db4f2ea..f6d53bc 100644 --- a/src/fund_strategy.py +++ b/src/fund_strategy.py @@ -7,36 +7,73 @@ Author: luxuemin2108@gmail.com ----- Copyright (c) 2021 Camel Lu ''' - +from datetime import timedelta, date from utils.index import get_last_quarter_str, update_xlsx_file_with_insert -from sql_model.fund_query import FundQuery -from pprint import pprint +from crud.query import query_high_score_funds import pandas as pd - -def output_high_score_funds(each_query=None, quarter_index=None): +def output_high_score_funds(quarter_index=None): """ 输出高分基金 """ - if each_query == None: - each_query = FundQuery() if quarter_index == None: quarter_index = get_last_quarter_str() print("quarter_index", quarter_index) - high_score_funds = each_query.select_high_score_funds( - quarter_index=quarter_index) - columns_bk = ['代码', '名称', '季度', '总资产', '现任基金经理管理起始时间', '投资风格', '三月最大回撤', '六月最大回撤', '夏普比率', '阿尔法系数', '贝塔系数', - 'R平方', '标准差', '风险系数', '两年风险评级', '三年风险评级', '五年风险评级', '五年晨星评级', '三年晨星评级', '股票仓位', '十大持股仓位'] + fund_list = query_high_score_funds(quarter_index) + funds_map = {} + for fund in fund_list: + fund_code = fund[0].fund_code + exist_fund = funds_map.get(fund_code) + if exist_fund: + exist_fund.append(fund) + funds_map[fund_code] = exist_fund + else: + funds_map[fund_code] = [fund] + high_score_funds = [] + delta = timedelta(days=1 * 365) + date_now = date.today() + for fund_code, funds in funds_map.items(): + fund_info = [] + is_meet_manager_start_date = False + is_super_one = False + for fund_item in funds: + if is_meet_manager_start_date == False and date_now - delta > fund_item[1].manager_start_date: + is_meet_manager_start_date = True + if is_super_one == True: + fund_info[3] = fund_info[3] + ",\n" + fund_item[1].manager.name + manager_start_date = fund_item[1].manager_start_date.strftime('%Y-%m-%d') + fund_info[4] = fund_info[4] + ",\n" + manager_start_date + else: + is_super_one = True + fund_info.append(fund_item[0].fund_code) + fund_info.append(fund_item[0].fund.fund_name) + fund_info.append(fund_item[0].investname_style) + fund_info.append(fund_item[1].manager.name) + manager_start_date = fund_item[1].manager_start_date.strftime('%Y-%m-%d') + fund_info.append(manager_start_date) + fund_info.append(fund_item[0].fund.found_date) + fund_info.append(fund_item[0].morning_star_rating_3) + fund_info.append(fund_item[0].morning_star_rating_5) + fund_info.append(fund_item[0].risk_assessment_sharpby) + fund_info.append(fund_item[0].stock_position_total) + fund_info.append(fund_item[0].stock_position_ten) + fund_info.append(fund_item[0].risk_rating_2) + fund_info.append(fund_item[0].risk_rating_3) + fund_info.append(fund_item[0].risk_rating_5) + fund_info.append(fund_item[0].risk_statistics_alpha) + fund_info.append(fund_item[0].risk_statistics_beta) + fund_info.append(fund_item[0].risk_assessment_standard_deviation) + fund_info.append(fund_item[0].total_asset) + fund_info.append(fund_item[0].quarter_index) + if is_meet_manager_start_date: + high_score_funds.append(fund_info) columns = ['代码', '名称', '投资风格', '基金经理', '现任经理管理起始时间', '成立时间', '三年晨星评级', '五年晨星评级', '夏普比率', '股票仓位', '十大持股仓位', '两年风险评级', '三年风险评级', '五年风险评级', '阿尔法系数', '贝塔系数', '标准差', '总资产', '数据更新时间'] df_high_score_funds = pd.DataFrame(high_score_funds, columns=columns) - # pprint(df_high_score_funds) - path = './outcome/数据整理/funds/high-score-funds.xlsx' update_xlsx_file_with_insert(path, df_high_score_funds, quarter_index) if __name__ == '__main__': - each_query = FundQuery() - output_high_score_funds(each_query) + output_high_score_funds() diff --git a/src/models/fund.py b/src/models/fund.py index 7464d69..e097f5a 100644 --- a/src/models/fund.py +++ b/src/models/fund.py @@ -9,16 +9,27 @@ Copyright (c) 2022 Camel Lu ''' import sys sys.path.append('./src') +from sqlalchemy.orm import relationship from sqlalchemy import Table from models.var import prefix, ORM_Base, engine -fund_table_base = prefix + 'base' +fund_base_tablename = prefix + 'base' +fund_quarter_tablename = prefix + 'quarter' -fund_table = Table(fund_table_base, ORM_Base.metadata, autoload=True, autoload_with=engine) +fund_base_table = Table(fund_base_tablename, ORM_Base.metadata, autoload=True, autoload_with=engine) +fund_quarter_table = Table(fund_quarter_tablename, ORM_Base.metadata, autoload=True, autoload_with=engine) -class Fund(ORM_Base): - __table__ = fund_table +class FundBase(ORM_Base): + __table__ = fund_base_table def __repr__(self): return f"Fund Base(id={self.id!r}, name={self.fund_code!r}, manager_id={self.fund_name!r})" + + +class FundQuarter(ORM_Base): + __table__ = fund_quarter_table + fund = relationship('FundBase', backref='fund_quarter') + + def __repr__(self): + return f"Fund {fund_quarter_tablename}(id={self.id!r}, name={self.fund_code!r})" diff --git a/src/sql_model/fund_query.py b/src/sql_model/fund_query.py index cd22baa..bf5a7a9 100644 --- a/src/sql_model/fund_query.py +++ b/src/sql_model/fund_query.py @@ -105,46 +105,6 @@ class FundQuery(BaseQuery): sql, [self.quarter_date, self.quarter_index, page_start, page_limit]) # 执行sql语句 return self.cursor.fetchall() # 获取查询的所有记录 - def select_high_score_funds(self, *, quarter_index=None): - """获取高分基金池 - - Args: - quarter_index (string, optional): 查询季度. Defaults to None. - - Returns: - []tuple: 高分基金池 - """ - last_year_time = time.localtime(time.time() - 365 * 24 * 3600) - last_year_date = time.strftime('%Y-%m-%d', last_year_time) - - if quarter_index == None: - quarter_index = self.quarter_index - sql = "SELECT a.fund_code, b.fund_name, a.investname_style, c.name, a.manager_start_date, b.found_date, a.morning_star_rating_3, \ - a.morning_star_rating_5, a.risk_assessment_sharpby, a.stock_position_total, a.stock_position_ten,\ - a.risk_rating_2, a.risk_rating_3, a.risk_rating_5,\ - a.risk_statistics_alpha, a.risk_statistics_beta, a.risk_assessment_standard_deviation,\ - a.total_asset, a.quarter_index FROM fund_morning_quarter as a \ - LEFT JOIN fund_morning_base AS b ON a.fund_code = b.fund_code \ - LEFT JOIN fund_morning_manager AS c ON c.manager_id = a.manager_id \ - WHERE b.fund_name NOT LIKE '%%C' AND b.fund_name NOT LIKE '%%E' AND b.fund_name NOT LIKE '%%H%%' AND b.fund_name NOT LIKE '%%指数%%' \ - AND a.quarter_index = %s AND a.total_asset < 100 AND \ - a.morning_star_rating_5 >= 3 AND a.morning_star_rating_3 = 5 AND a.stock_position_total >= 50 AND a.stock_position_ten <= 60 \ - AND a.risk_assessment_sharpby > 1 AND a.risk_rating_2 > 1 AND a.risk_rating_3 > 1 AND a.risk_rating_5 > 1 AND a.manager_start_date < %s \ - ORDER BY a.risk_assessment_sharpby DESC, a.risk_statistics_alpha DESC;" - sql_bk = "SELECT a.fund_code, b.fund_name, a.quarter_index, a.total_asset , a.manager_start_date, \ - a.investname_style, a.three_month_retracement, a.june_month_retracement, a.risk_assessment_sharpby,\ - a.risk_statistics_alpha, a.risk_statistics_beta, a.risk_statistics_r_square, a.risk_assessment_standard_deviation,\ - a.risk_assessment_risk_coefficient, a.risk_rating_2, a.risk_rating_3, a.risk_rating_5, a.morning_star_rating_5,\ - a.morning_star_rating_3, a.stock_position_total, a.stock_position_ten FROM fund_morning_quarter as a \ - LEFT JOIN fund_morning_base AS b ON a.fund_code = b.fund_code \ - WHERE b.fund_name NOT LIKE '%%C' AND b.fund_name NOT LIKE '%%H' AND b.fund_name NOT LIKE '%%E' AND a.quarter_index = %s AND \ - a.morning_star_rating_5 >= 3 AND a.morning_star_rating_3 = 5 AND a.stock_position_total >= 50 AND a.stock_position_ten <= 60 \ - AND a.risk_assessment_sharpby >1 AND a.risk_rating_2 > 1 AND a.risk_rating_3 > 1 AND a.risk_rating_5 > 1 AND a.manager_start_date < %s \ - ORDER BY a.risk_assessment_sharpby DESC, a.risk_rating_5 DESC;" - self.cursor.execute(sql, [quarter_index, last_year_date]) # 执行sql语句 - results = self.cursor.fetchall() # 获取查询的所有记录 - return results - def select_certain_condition_funds(self, *, quarter_index=None, morning_star_rating_5=None, morning_star_rating_3=None, manager_start_date=None, stock_position_total=None, stock_position_ten=None, **rest_dicts): print("rest_dicts", rest_dicts) if quarter_index == None: diff --git a/src/utils/index.py b/src/utils/index.py index 2010f1a..b902af9 100644 --- a/src/utils/index.py +++ b/src/utils/index.py @@ -254,13 +254,16 @@ def update_xlsx_file_with_insert(path, df_data, sheet_name, index = 0): if os.path.exists(path): writer = pd.ExcelWriter(path, engine='openpyxl') workbook = load_workbook(path) + if sheet_name in workbook.sheetnames: + del workbook[sheet_name] writer.book = workbook df_data.to_excel( writer, sheet_name=sheet_name) workbook = writer.book writer.sheets = {ws.title:ws for ws in workbook.worksheets} - del workbook[sheet_name] # workbook.remove(sheet_name) + del workbook[sheet_name] + workbook._add_sheet(writer.sheets.get(sheet_name), index) writer.book = workbook