Commit c023f84d authored by jichao's avatar jichao

基金优选完成

parent 94fe2e8d
database:
host: ${MYSQL_HOST:127.0.0.1}
port: ${MYSQL_PORT:3306}
user: ${MYSQL_USER:root}
password: ${MYSQL_PWD:123456}
dbname: ${MYSQL_DBNAME:jftech_robo}
database_from:
host: ${MYSQL_HOST:127.0.0.1}
port: ${MYSQL_PORT:3306}
user: ${MYSQL_USER:root}
......@@ -10,7 +16,7 @@ email:
password: 5dbb#30ec6d3
logger:
version: 1
use: ${LOG_NAME:prod}
use: ${LOG_NAME:root}
formatters:
brief:
format: "%(asctime)s - %(levelname)s - %(message)s"
......@@ -39,5 +45,14 @@ logger:
root:
level: INFO
handlers: [console]
fund_pool:
fund_optimize:
sortino_weight:
- months: 3
weight: 0.5
- months: 6
weight: 0.2
- years: 1
weight: 0.2
from . import *
from datas.datum.enums import *
from .api import (
get_fund_datums,
)
del api, robo_base_datum, enums
import datas.datum.robo_base_datum as _rbd
from datas.datum.enums import DatumType
def get_fund_datums(crncy=None, risk=None):
return _rbd.get_base_datums(type=DatumType.FUND, crncy=crncy, risk=risk)
if __name__ == '__main__':
print(get_fund_datums(risk=1))
from enum import Enum, unique
@unique
class DatumType(Enum):
FUND = 'FUND'
from utils import read, write, config, format_date, parse_date, where
import json
from datetime import datetime
from datas.datum.enums import DatumType
@read
def _select(*args, **kwargs):
return f'''select rbd_id as id, rbd_datas as datas from robo_base_datum {where(*args, **kwargs)}'''
def get_base_datums(type: DatumType = None, crncy=None, risk=None):
result = _select(v_rbd_type=type, v_rbd_crncy=crncy, v_rbd_risk=risk)
result = [{**json.loads(x['datas']), **{'id': x['id']}} for x in result]
return [{**x, **{'inceptDate': parse_date(x['inceptDate'])}} for x in result]
if __name__ == '__main__':
from enums import Enum
print(isinstance(DatumType.FUND, Enum))
CREATE TABLE IF NOT EXISTS robo_fund_info
(
rfi_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
rfi_datas JSON DEFAULT NULL COMMENT '所有数据',
v_rfi_gender VARCHAR(100) GENERATED ALWAYS AS (rfi_datas ->> '$.gender') COMMENT '性别',
rfi_create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
rfi_update_time DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (rfi_id)
) ENGINE = InnoDB
AUTO_INCREMENT = 0
DEFAULT CHARSET = utf8mb4 COMMENT '用户信息表';
\ No newline at end of file
CREATE TABLE IF NOT EXISTS robo_base_datum
(
rbd_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
rbd_datas JSON DEFAULT NULL COMMENT '所有数据',
v_rbd_ft_ticker VARCHAR(255) GENERATED ALWAYS AS (rbd_datas ->> '$.ftTicker') COMMENT 'ft_ticker',
v_rbd_bloomberg_ticker VARCHAR(255) GENERATED ALWAYS AS (rbd_datas ->> '$.bloombergTicker') COMMENT 'bloomberg_ticker',
v_rbd_type VARCHAR(255) GENERATED ALWAYS AS (rbd_datas ->> '$.type') COMMENT 'type',
v_rbd_crncy VARCHAR(255) GENERATED ALWAYS AS (rbd_datas ->> '$.currency') COMMENT 'crncy',
v_rbd_risk VARCHAR(255) GENERATED ALWAYS AS (rbd_datas ->> '$.risk') COMMENT 'risk',
rbd_create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
rbd_update_time DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (rbd_id),
UNIQUE KEY (v_rbd_ft_ticker),
UNIQUE KEY (v_rbd_bloomberg_ticker),
INDEX (v_rbd_crncy),
INDEX (v_rbd_risk),
INDEX (v_rbd_type)
) ENGINE = InnoDB
AUTO_INCREMENT = 0
DEFAULT CHARSET = utf8mb4 COMMENT '基础资料信息表';
CREATE TABLE IF NOT EXISTS robo_fund_navs
(
rfn_fund_id BIGINT UNSIGNED NOT NULL COMMENT '基金ID',
rfn_date DATETIME NOT NULL COMMENT '数据日期',
rfn_av DOUBLE NOT NULL COMMENT '原始净值',
rfn_div DOUBLE NOT NULL DEFAULT 0 COMMENT '原始分红',
rfn_split DOUBLE NOT NULL DEFAULT 1 COMMENT '拆股',
rfn_accrue_split DOUBLE NOT NULL DEFAULT 1 COMMENT '累积拆股',
rfn_av_p DOUBLE NOT NULL COMMENT '原始拆股净值',
rfn_div_p DOUBLE NOT NULL DEFAULT 0 COMMENT '复权拆股分红',
rfn_nav_cal DOUBLE NOT NULL COMMENT '复权净值',
rfn_create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
rfn_update_time DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (rfn_fund_id, rfn_date),
INDEX (rfn_date)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT '基金数据表';
from .robo_fund_navs import *
del robo_fund_navs
from utils import read, where, format_date
@read
def _select(*args, **kwargs):
return f'''select rfn_fund_id as fund_id, rfn_date as nav_date, rfn_nav_cal as nav_cal from robo_fund_navs {where(*args, **kwargs)}'''
def get_navs(fund_id=None, min_date=None, max_date=None):
sqls = []
if min_date:
sqls.append(f"rfn_date >= '{format_date(min_date)}'")
if max_date:
sqls.append(f"rfn_date <= '{format_date(max_date)}'")
return _select(*sqls, rfn_fund_id=fund_id)
if __name__ == '__main__':
from utils import parse_date
navs = get_navs(fund_id=1, min_date=parse_date('2022-11-01'))
print(navs)
import pandas as pd
from utils import filter_weekend, config, dict_remove
from datas import navs
from dateutil.relativedelta import relativedelta
from empyrical import sortino_ratio
optimize_config = config['fund_pool']['fund_optimize']
sortino_config = [{**x, 'name': [f"sortino_{y[1]}_{y[0]}" for y in x.items() if y[0] != 'weight'][0]} for x in optimize_config['sortino_weight']]
def find_optimize(fund_ids, day):
start = filter_weekend(sorted([day - relativedelta(days=1, **dict_remove(x, ('weight', 'name'))) for x in sortino_config])[0])
fund_navs = pd.DataFrame(navs.get_navs(fund_id=tuple(fund_ids), min_date=start, max_date=day))
fund_navs.sort_values('nav_date', inplace=True)
fund_navs = fund_navs.pivot_table(index='nav_date', columns='fund_id', values='nav_cal')
fund_navs.fillna(method='ffill', inplace=True)
day_income = round(fund_navs.pct_change(), 4)
sortino = pd.DataFrame()
for item in sortino_config:
delta_kwargs = {**item}
del delta_kwargs['weight'], delta_kwargs['name']
ratio = dict(sortino_ratio(day_income.truncate(before=(day - relativedelta(**delta_kwargs)))))
sortino = pd.concat([sortino, pd.DataFrame([ratio], index=[item['name']])])
sortino = sortino.T
sortino['score'] = sortino.apply(lambda r:sum([x['weight'] * r[x['name']] for x in sortino_config]), axis=1)
sortino.sort_values('score', ascending=False, inplace=True)
return day_income.columns[sortino.index[0]]
def get_base_fund_pool():
pass
if __name__ == '__main__':
from datas import datum
from datetime import date
funds = datum.get_fund_datums()
funds = pd.DataFrame(funds)
for (category, assetType), fund_group in funds.groupby(by=['category', 'assetType']):
if len(fund_group) > 1:
fund_ids = tuple(fund_group['id'])
fund_id = find_optimize(fund_ids, date.today())
print(fund_ids, "->", fund_id)
else:
fund_ids = tuple(fund_group['id'])
print(fund_ids, "->", fund_ids[0])
from utils import *
from utils import logger
from datas import datum
if __name__ == '__main__':
logger.info(dir())
logger.info(datum.get_fund_datums())
certifi==2022.9.24
charset-normalizer==2.1.1
empyrical==0.5.5
idna==3.4
lxml==4.9.1
numpy==1.23.4
pandas==1.5.1
pandas-datareader==0.10.0
PyMySQL==1.0.2
python-dateutil==2.8.2
pytz==2022.6
PyYAML==6.0
requests==2.28.1
scipy==1.9.3
six==1.16.0
urllib3==1.26.12
from .date_utils import *
from .base import *
from .datebase import read, write, transaction
from .datebase import read, write, transaction, where
from .__env_config import config, get_config
from .__logger import build_logger, logger
import os
__all__ = ['get_project_path', 'deep_dict_update']
__all__ = ['get_project_path', 'deep_dict_update', 'dict_remove']
def get_project_path():
......@@ -29,3 +29,10 @@ def deep_dict_update(d1, d2):
for key in d2:
if key not in d1:
d1[key] = d2[key]
def dict_remove(d: dict, k) -> dict:
result = d.copy()
for key in tuple(k):
del result[key]
return result
import calendar
from datetime import timedelta
__all__ = ['filter_weekend', 'next_workday', 'is_workday']
from datetime import timedelta, datetime
def filter_weekend(day):
......@@ -19,3 +17,15 @@ def next_workday(day):
def is_workday(day):
return day.weekday() in range(5)
def format_date(date, has_time=False):
return date.strftime('%Y-%m-%d %H:%M:%S' if has_time else '%Y-%m-%d')
def parse_date(date, has_time=False):
return datetime.strptime(date, '%Y-%m-%d %H:%M:%S' if has_time else '%Y-%m-%d')
if __name__ == '__main__':
print(parse_date('2022-10-01 10:24:24', has_time=True))
......@@ -3,6 +3,8 @@ import pymysql
import threading
from pymysql.cursors import DictCursor
from .__env_config import config as default_config
from .date_utils import format_date, datetime
from enum import Enum
class Database:
......@@ -104,3 +106,24 @@ def transaction(func=None, config=None):
finally:
del __local__.db
return wraps
def where(*args, **kwargs) -> str:
result = []
if kwargs:
for k, v in kwargs.items():
if isinstance(v, str):
result.append(f"{k} = '{v}'")
elif isinstance(v, datetime):
result.append(f"{k} = '{format_date(v)}'")
elif isinstance(v, Enum):
result.append(f"{k} = '{v.value}'")
elif isinstance(v, tuple):
result.append(f"{k} in {v}" if len(v) > 1 else f"{k} = {v[0]}")
elif v is not None:
result.append(f"{k} = {v}")
if args:
result.extend(args)
return f"where {' and '.join(result)}" if result else ''
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