Commit d66b12eb authored by jichao's avatar jichao

基金优选完成

parent c023f84d
...@@ -45,6 +45,11 @@ logger: ...@@ -45,6 +45,11 @@ logger:
root: root:
level: INFO level: INFO
handlers: [console] handlers: [console]
datas:
navs:
exrate:
- from: EUR
ticker: EURUSD BGN Curncy
fund_pool: fund_pool:
fund_optimize: fund_optimize:
sortino_weight: sortino_weight:
......
...@@ -2,9 +2,13 @@ import datas.datum.robo_base_datum as _rbd ...@@ -2,9 +2,13 @@ import datas.datum.robo_base_datum as _rbd
from datas.datum.enums import DatumType 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) return _rbd.get_base_datums(type=DatumType.FUND, crncy=crncy, risk=risk)
def get_without_crncy(crncy):
pass
if __name__ == '__main__': if __name__ == '__main__':
print(get_fund_datums(risk=1)) print(get_fund_datums(risk=1))
...@@ -38,3 +38,33 @@ CREATE TABLE IF NOT EXISTS robo_fund_navs ...@@ -38,3 +38,33 @@ CREATE TABLE IF NOT EXISTS robo_fund_navs
) ENGINE = InnoDB ) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT '基金数据表'; 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 ...@@ -4,11 +4,14 @@ from datas import navs
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from empyrical import sortino_ratio from empyrical import sortino_ratio
optimize_config = config['fund_pool']['fund_optimize'] 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']] 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): 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]) 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 = 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.sort_values('nav_date', inplace=True)
...@@ -17,31 +20,21 @@ def find_optimize(fund_ids, day): ...@@ -17,31 +20,21 @@ def find_optimize(fund_ids, day):
day_income = round(fund_navs.pct_change(), 4) day_income = round(fund_navs.pct_change(), 4)
sortino = pd.DataFrame() sortino = pd.DataFrame()
for item in sortino_config: for item in sortino_config:
delta_kwargs = {**item} delta_kwargs = item.copy()
del delta_kwargs['weight'], delta_kwargs['name'] del delta_kwargs['weight'], delta_kwargs['name']
ratio = dict(sortino_ratio(day_income.truncate(before=(day - relativedelta(**delta_kwargs))))) ratio = dict(sortino_ratio(day_income.truncate(before=(day - relativedelta(**delta_kwargs)))))
sortino = pd.concat([sortino, pd.DataFrame([ratio], index=[item['name']])]) sortino = pd.concat([sortino, pd.DataFrame([ratio], index=[item['name']])])
sortino = sortino.T 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) sortino.sort_values('score', ascending=False, inplace=True)
return day_income.columns[sortino.index[0]] return day_income.columns[sortino.index[0]]
def get_base_fund_pool(): def get_optimize_fund_pool(day):
pass pass
if __name__ == '__main__': if __name__ == '__main__':
from datas import datum
from datetime import date from datetime import date
funds = datum.get_fund_datums() get_optimize_fund_pool(date.today())
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])
import os 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(): def get_project_path():
...@@ -36,3 +43,20 @@ def dict_remove(d: dict, k) -> dict: ...@@ -36,3 +43,20 @@ def dict_remove(d: dict, k) -> dict:
for key in tuple(k): for key in tuple(k):
del result[key] del result[key]
return result 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