Commit d66b12eb authored by jichao's avatar jichao

基金优选完成

parent c023f84d
......@@ -45,6 +45,11 @@ logger:
root:
level: INFO
handlers: [console]
datas:
navs:
exrate:
- from: EUR
ticker: EURUSD BGN Curncy
fund_pool:
fund_optimize:
sortino_weight:
......
......@@ -2,9 +2,13 @@ import datas.datum.robo_base_datum as _rbd
from datas.datum.enums import DatumType
def get_fund_datums(crncy=None, risk=None):
def get_fund_datums(crncy=None, risk=None, fund_ids=None):
return _rbd.get_base_datums(type=DatumType.FUND, crncy=crncy, risk=risk)
def get_without_crncy(crncy):
pass
if __name__ == '__main__':
print(get_fund_datums(risk=1))
......@@ -38,3 +38,33 @@ CREATE TABLE IF NOT EXISTS robo_fund_navs
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT '基金数据表';
CREATE TABLE IF NOT EXISTS `robo_exrate`
(
`re_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`re_ticker` VARCHAR(255) NOT NULL COMMENT '汇率ticker',
`re_date` DATETIME NOT NULL COMMENT '日期',
`re_close` DOUBLE NOT NULL COMMENT '收盘价',
`re_create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`re_update_time` DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`re_id`),
UNIQUE (`re_ticker`, `re_date`),
INDEX (`re_date`)
) ENGINE = InnoDB
AUTO_INCREMENT = 0
DEFAULT CHARSET = utf8mb4 COMMENT '基金数据表';
CREATE TABLE IF NOT EXISTS robo_optimize_pool
(
rop_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
rop_date DATETIME NOT NULL COMMENT '数据日期',
rop_fund_ids JSON DEFAULT NULL COMMENT '基金ID',
rop_create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
rop_update_time DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (rop_id),
INDEX (rop_date)
) ENGINE = InnoDB
AUTO_INCREMENT = 0
DEFAULT CHARSET = utf8mb4 COMMENT '优选基金池';
from .robo_fund_navs import *
from .api import (
get_navs
)
del robo_fund_navs
del api
import pandas as _pd
from datas.navs import robo_exrate as _re, robo_fund_navs as _navs
from datas import datum as _datum, DatumType
from utils import config, to_bool
from datetime import timedelta
navs_config = config['datas']['navs'] if 'datas' in config and 'navs' in config['datas'] else {}
def get_navs(fund_id=None, min_date=None, max_date=None):
navs = _navs.get_navs(fund_id=fund_id, min_date=min_date, max_date=max_date)
if 'exrate' in navs_config:
navs = _pd.DataFrame(navs)
navs = navs.pivot_table(index='nav_date', columns='fund_id', values='nav_cal')
for exrate_config in navs_config['exrate']:
exrate = _pd.DataFrame(_re.get_exrates(ticker=exrate_config['ticker'], min_date=navs.index.min(), max_date=navs.index.max()))
exrate = exrate[['date', 'close']]
exrate.set_index('date', inplace=True)
for fund in _datum.get_fund_datums(crncy=exrate_config['from']):
navs[fund['id']] = round(navs[fund['id']] * exrate['close'], 4)
navs = navs.reset_index().melt(id_vars='nav_date', value_name='nav_cal')
navs.dropna(inplace=True)
navs = navs[['fund_id', 'nav_date', 'nav_cal']]
navs.sort_values('fund_id', inplace=True)
navs = navs.to_dict('records')
return navs
if __name__ == '__main__':
from utils import parse_date
print(get_navs(min_date=parse_date('2022-11-01')))
from utils import read, where, format_date
@read
def _select(*args, **kwargs):
return f'''select re_id as id, re_ticker as ticker, re_date as date, re_close as close from robo_exrate {where(*args, **kwargs)}'''
def get_exrates(ticker=None, min_date=None, max_date=None):
sqls = []
if min_date:
sqls.append(f"re_date >= '{format_date(min_date)}'")
if max_date:
sqls.append(f"re_date <= '{format_date(max_date)}'")
return _select(*sqls, re_ticker=ticker)
@read(one=True)
def get_exrate(ticker, date):
return f'''select re_id as id, re_ticker as ticker, re_date as date, re_close as close from robo_exrate {where(re_ticker=ticker, re_date=date)}'''
if __name__ == '__main__':
from utils import parse_date
print(get_exrate(date=parse_date('2022-11-01'), ticker='EURUSD BGN Curncy'))
......@@ -4,11 +4,14 @@ 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']]
optimize_config = config['fund_pool']['fund_optimize'] if 'fund_pool' in config and 'fund_optimize' in config['fund_pool'] else {}
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']] \
if 'sortino_weight' in optimize_config else []
def find_optimize(fund_ids, day):
if not sortino_config:
raise NameError(f"find optimize, but not found sortino config.")
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)
......@@ -17,31 +20,21 @@ def find_optimize(fund_ids, day):
day_income = round(fund_navs.pct_change(), 4)
sortino = pd.DataFrame()
for item in sortino_config:
delta_kwargs = {**item}
delta_kwargs = item.copy()
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['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():
def get_optimize_fund_pool(day):
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])
get_optimize_fund_pool(date.today())
import os
from functools import reduce
__all__ = ['get_project_path', 'deep_dict_update', 'dict_remove']
__all__ = [
'get_project_path',
'deep_dict_update',
'dict_remove',
'equals_ignore_case',
'to_bool',
]
def get_project_path():
......@@ -36,3 +43,20 @@ def dict_remove(d: dict, k) -> dict:
for key in tuple(k):
del result[key]
return result
def equals_ignore_case(a, b) -> bool:
return str(a).upper() == str(b).upper() if a and b else False
_TRUE_STR = ['true', 't', 'yes', 'y', 'on']
_FALSE_STR = ['false', 'f', 'no', 'n', 'off']
def to_bool(v) -> bool:
if isinstance(v, str):
if reduce(lambda a, b: a or b, [equals_ignore_case(v, x) for x in _TRUE_STR]):
return True
if reduce(lambda a, b: a or b, [equals_ignore_case(v, x) for x in _FALSE_STR]):
return False
return bool(v)
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