Commit 3d3e4fde authored by jichao's avatar jichao

实盘完成

parent c42d36f3
...@@ -99,7 +99,7 @@ class Datum(ABC): ...@@ -99,7 +99,7 @@ class Datum(ABC):
''' '''
@abstractmethod @abstractmethod
def get_datums(self, type: DatumType = None, crncy=None, risk=None, datum_ids=None, ticker=None): def get_datums(self, type: DatumType = None, crncy=None, risk=None, datum_ids=None, ticker=None, exclude=True):
''' '''
获取资料信息,当id和ticker都有时,取二者并集 获取资料信息,当id和ticker都有时,取二者并集
:param type: 资料类型 :param type: 资料类型
...@@ -107,6 +107,7 @@ class Datum(ABC): ...@@ -107,6 +107,7 @@ class Datum(ABC):
:param risk: 风险等级,仅对基金资料有效 :param risk: 风险等级,仅对基金资料有效
:param datum_ids: 资料ID列表 :param datum_ids: 资料ID列表
:param ticker: 资料ticker列表 :param ticker: 资料ticker列表
:param exclude: 是否排除过滤的资料,默认为True
:return: 资料信息数据 :return: 资料信息数据
''' '''
pass pass
...@@ -120,6 +121,15 @@ class Datum(ABC): ...@@ -120,6 +121,15 @@ class Datum(ABC):
''' '''
pass pass
@abstractmethod
def update_change(self, date):
'''
在指定对日期,执行资料变更,如果有变更则返回True, 否则返回False,注意,改方法非幂等,变更后无法复原
:param date: 执行变更的日期
:return: 如果有变更则返回True,否则返回False
'''
pass
class Navs(ABC): class Navs(ABC):
''' '''
......
...@@ -43,9 +43,12 @@ class SortinoAssetOptimize(AssetOptimize, ABC): ...@@ -43,9 +43,12 @@ class SortinoAssetOptimize(AssetOptimize, ABC):
return pct_change.columns[sortino.index[0]] return pct_change.columns[sortino.index[0]]
def get_optimize_pool(self, day): def get_optimize_pool(self, day):
opt_pool = rop.get_one(day=day, type=AssetPoolType.OPTIMIZE)
if opt_pool is not None:
return json.loads(opt_pool['asset_ids'])
last_one = rop.get_last_one(day=day, type=AssetPoolType.OPTIMIZE) last_one = rop.get_last_one(day=day, type=AssetPoolType.OPTIMIZE)
start = get_quarter_start(day or dt.today()) start = get_quarter_start(day or dt.today())
if not last_one or start > last_one['date'] or self.has_incept_asset(last_one['date'] + timedelta(1), day): if not last_one or start > last_one['date'] or self.has_incept_asset(last_one['date'] + timedelta(1), day) or self.has_change(day):
pool = [] pool = []
min_dates = self.nav_min_dates min_dates = self.nav_min_dates
max_incept_date = sorted([(day - relativedelta(**x)) for x in self.delta_kwargs])[0] max_incept_date = sorted([(day - relativedelta(**x)) for x in self.delta_kwargs])[0]
...@@ -86,6 +89,10 @@ class SortinoAssetOptimize(AssetOptimize, ABC): ...@@ -86,6 +89,10 @@ class SortinoAssetOptimize(AssetOptimize, ABC):
''' '''
pass pass
@abstractmethod
def has_change(self, day):
return False
@component @component
class FundSortinoAssetOptimize(SortinoAssetOptimize): class FundSortinoAssetOptimize(SortinoAssetOptimize):
...@@ -108,6 +115,9 @@ class FundSortinoAssetOptimize(SortinoAssetOptimize): ...@@ -108,6 +115,9 @@ class FundSortinoAssetOptimize(SortinoAssetOptimize):
end_date = sorted([(end_date - relativedelta(**x)) for x in self.delta_kwargs])[0] end_date = sorted([(end_date - relativedelta(**x)) for x in self.delta_kwargs])[0]
return len([x for x in self.nav_min_dates.items() if start_date <= x[1] <= end_date]) > 0 return len([x for x in self.nav_min_dates.items() if start_date <= x[1] <= end_date]) > 0
def has_change(self, day):
return self._datum.update_change(day)
def get_groups(self): def get_groups(self):
funds = pd.DataFrame(self._datum.get_datums(type=DatumType.FUND)) funds = pd.DataFrame(self._datum.get_datums(type=DatumType.FUND))
min_dates = self._navs.get_nav_start_date() min_dates = self._navs.get_nav_start_date()
......
from py_jftech import read, where, to_tuple from py_jftech import read, where, to_tuple, write
from api import DatumType from api import DatumType
...@@ -13,3 +13,8 @@ def get_base_datums(type: DatumType = None, crncy=None, risk=None, datum_ids=Non ...@@ -13,3 +13,8 @@ def get_base_datums(type: DatumType = None, crncy=None, risk=None, datum_ids=Non
'v_rbd_bloomberg_ticker': to_tuple(ticker) 'v_rbd_bloomberg_ticker': to_tuple(ticker)
} }
return f'''select rbd_id as id, rbd_datas as datas from robo_base_datum {where(**kwargs)}''' return f'''select rbd_id as id, rbd_datas as datas from robo_base_datum {where(**kwargs)}'''
@write
def update_datum(id, datas: str):
return f'''update robo_base_datum set rbd_datas = '{datas}' where rbd_id = {id}'''
import json import json
import os
from datetime import datetime as dt from datetime import datetime as dt
from typing import List from typing import List
import pandas as pd import pandas as pd
from py_jftech import component, parse_date, get_config, to_tuple, autowired from py_jftech import component, parse_date, get_config, to_tuple, autowired, get_project_path, transaction
from api import DatumType, Datum, PortfoliosRisk, RoboReportor from api import DatumType, Datum, PortfoliosRisk, RoboReportor, RoboExecutor
from basic.dao import robo_base_datum as rbd from basic.dao import robo_base_datum as rbd
...@@ -17,7 +18,27 @@ class DefaultDatum(Datum): ...@@ -17,7 +18,27 @@ class DefaultDatum(Datum):
@property @property
def excludes(self): def excludes(self):
return self._config['excludes'] if 'excludes' in self._config else [] excludes = self._config['excludes'] if 'excludes' in self._config else []
if isinstance(excludes, dict):
excludes = excludes[RoboExecutor.use_name()] if RoboExecutor.use_name() in excludes else []
return excludes
@property
def change_date(self):
change = self._config['change'] if 'change' in self._config else {}
return pd.to_datetime(change['date']) if 'date' in change else None
@property
def change_file(self):
change = self._config['change'] if 'change' in self._config else {}
if 'file' not in change or change['file'] is None:
return None
file_path: str = change['file']
if file_path.startswith('.'):
return os.path.abspath(os.path.join(os.path.dirname(__file__), file_path))
elif file_path.startswith('/'):
return os.path.abspath(file_path)
return os.path.abspath(os.path.join(get_project_path(), file_path))
def format_datum(self, datum): def format_datum(self, datum):
if DatumType(datum['type']) is DatumType.FUND: if DatumType(datum['type']) is DatumType.FUND:
...@@ -27,14 +48,14 @@ class DefaultDatum(Datum): ...@@ -27,14 +48,14 @@ class DefaultDatum(Datum):
} }
return datum return datum
def get_datums(self, type: DatumType = None, crncy=None, risk=None, datum_ids=None, ticker=None): def get_datums(self, type: DatumType = None, crncy=None, risk=None, datum_ids=None, ticker=None, exclude=True):
datum_ids = to_tuple(datum_ids) datum_ids = to_tuple(datum_ids)
if ticker: if ticker:
datums = rbd.get_base_datums(type=type, ticker=ticker) datums = rbd.get_base_datums(type=type, ticker=ticker)
datum_ids = tuple(set(datum_ids or []) | {x['id'] for x in datums}) datum_ids = tuple(set(datum_ids or []) | {x['id'] for x in datums})
result = rbd.get_base_datums(type=type, crncy=crncy, risk=risk, datum_ids=datum_ids) result = rbd.get_base_datums(type=type, crncy=crncy, risk=risk, datum_ids=datum_ids)
result = [{**json.loads(x['datas']), 'id': x['id']} for x in result] result = [{**json.loads(x['datas']), 'id': x['id']} for x in result]
return [self.format_datum(x) for x in result if DatumType(x['type']) is not DatumType.FUND or x['bloombergTicker'] not in self.excludes] return [self.format_datum(x) for x in result if not exclude or x['bloombergTicker'] not in self.excludes]
def get_high_risk_datums(self, risk: PortfoliosRisk): def get_high_risk_datums(self, risk: PortfoliosRisk):
risk3 = self.get_datums(type=DatumType.FUND, risk=3) risk3 = self.get_datums(type=DatumType.FUND, risk=3)
...@@ -47,6 +68,19 @@ class DefaultDatum(Datum): ...@@ -47,6 +68,19 @@ class DefaultDatum(Datum):
return risk3 + self.get_datums(type=DatumType.FUND, risk=(4, 5)) return risk3 + self.get_datums(type=DatumType.FUND, risk=(4, 5))
return None return None
@transaction
def update_change(self, date):
if self.change_date is not None and self.change_date == pd.to_datetime(date):
if self.change_file is not None:
for fund in pd.read_excel(self.change_file).to_dict('records'):
db_data = rbd.get_base_datums(ticker=fund['bloombergTicker'])
rbd.update_datum(db_data['id'], json.dumps({
**json.loads(db_data['datas']),
**fund
}))
return True
return False
@component(bean_name='funds-report') @component(bean_name='funds-report')
class FundReportor(RoboReportor): class FundReportor(RoboReportor):
...@@ -62,5 +96,6 @@ class FundReportor(RoboReportor): ...@@ -62,5 +96,6 @@ class FundReportor(RoboReportor):
def load_report(self, max_date=dt.today(), min_date=None) -> List[dict]: def load_report(self, max_date=dt.today(), min_date=None) -> List[dict]:
datums = self._datum.get_datums(type=DatumType.FUND) datums = self._datum.get_datums(type=DatumType.FUND)
datums = pd.DataFrame(datums) datums = pd.DataFrame(datums)
datums = datums[['id', 'ftTicker', 'bloombergTicker', 'chineseName', 'englishName', 'lipperKey', 'isin', 'currency', 'risk', 'inceptDate', 'category', 'assetType']] datums = datums[
['id', 'ftTicker', 'bloombergTicker', 'chineseName', 'englishName', 'lipperKey', 'isin', 'currency', 'risk', 'inceptDate', 'category', 'assetType']]
return datums.to_dict('records') return datums.to_dict('records')
...@@ -2,7 +2,6 @@ import logging ...@@ -2,7 +2,6 @@ import logging
import unittest import unittest
from typing import List from typing import List
import pandas as pd
from py_jftech import autowired, parse_date, to_str from py_jftech import autowired, parse_date, to_str
from api import Navs, Datum, PortfoliosRisk, DataSync, RoboReportor from api import Navs, Datum, PortfoliosRisk, DataSync, RoboReportor
......
...@@ -35,6 +35,12 @@ py-jftech: ...@@ -35,6 +35,12 @@ py-jftech:
user: ${MYSQL_USER:root} user: ${MYSQL_USER:root}
password: ${MYSQL_PWD:123456} password: ${MYSQL_PWD:123456}
dbname: ${MYSQL_DBNAME:jftech_robo} dbname: ${MYSQL_DBNAME:jftech_robo}
database-2:
host: 106.14.56.221
port: 3306
user: robo_user
password: robo2.1@20220521
dbname: robo_ft_fund_real
injectable: injectable:
types: types:
api.PortfoliosBuilder: portfolios.builder.PoemPortfoliosBuilder api.PortfoliosBuilder: portfolios.builder.PoemPortfoliosBuilder
...@@ -48,7 +54,11 @@ basic: # 基础信息模块 ...@@ -48,7 +54,11 @@ basic: # 基础信息模块
sync: sync:
start-date: 2007-01-01 # 同步数据开始日期 start-date: 2007-01-01 # 同步数据开始日期
datum: # 资料模块 datum: # 资料模块
change:
date: ${DATUM_CHANGE_DATE}
file: ${DATUM_CHANGE_FILE}
excludes: # 排除的资料彭博ticker excludes: # 排除的资料彭博ticker
backtest:
- 'FKUQX US Equity' - 'FKUQX US Equity'
- 'FTAAUSH LX Equity' - 'FTAAUSH LX Equity'
- 'FTJAPAU LX Equity' - 'FTJAPAU LX Equity'
...@@ -57,6 +67,21 @@ basic: # 基础信息模块 ...@@ -57,6 +67,21 @@ basic: # 基础信息模块
- 'TEUSAAU LX Equity' - 'TEUSAAU LX Equity'
- 'FTEAUH1 LX Equity' - 'FTEAUH1 LX Equity'
- 'TFIAAUS LX Equity' - 'TFIAAUS LX Equity'
real:
- 'FGFSACU LX Equity'
- 'TMEEAAU LX Equity'
- 'FTEAMUH LX Equity'
- 'FKUTX US Equity'
- 'TEMUSGI LX Equity'
- 'TEMFIAI LX Equity'
- 'TEMGROA LX Equity'
- 'TEMFMEA LX Equity'
- 'TEMDGAA LX Equity'
- 'TEMJAAU LX Equity'
- 'TEMFHAC LX Equity'
- 'TEMLATA LX Equity'
- 'LEPEUAA ID Equity'
- 'LGBOAAU ID Equity'
navs: # 净值模块 navs: # 净值模块
exrate: # 汇率,如果不开启,整个这块注释掉 exrate: # 汇率,如果不开启,整个这块注释掉
- from: EUR # 需要转换的货币类型 - from: EUR # 需要转换的货币类型
...@@ -139,7 +164,7 @@ rebalance: # 再平衡模块 ...@@ -139,7 +164,7 @@ rebalance: # 再平衡模块
signals: # 信号相关 signals: # 信号相关
crisis-signal: # 危机信号相关 crisis-signal: # 危机信号相关
exp-years: 3 # 预警期时长,单位自然年,点到点计算 exp-years: 3 # 预警期时长,单位自然年,点到点计算
exp-init: 2008-01-01 # 设置起始危机预警开始时间,如果关闭初始预警起,注释到这一条即可 exp-init: 2022-03-04 # 设置起始危机预警开始时间,如果关闭初始预警起,注释到这一条即可
inversion-years: 1 # 利率倒挂计算时长,单位自然年,点到点取值 inversion-years: 1 # 利率倒挂计算时长,单位自然年,点到点取值
inversion-threshold: 0.3 # 利率倒挂触发阀值 inversion-threshold: 0.3 # 利率倒挂触发阀值
crisis-1: # 危机1相关 crisis-1: # 危机1相关
...@@ -211,7 +236,7 @@ reports: # 报告模块相关 ...@@ -211,7 +236,7 @@ reports: # 报告模块相关
backtest: # 回测导出曹策略 backtest: # 回测导出曹策略
exist-build: on # 如果报告文件存在,是否重新构建文件 exist-build: on # 如果报告文件存在,是否重新构建文件
save-path: ${EXPORT_PATH:excels} # 导出报告文件存放路径,如果以./或者../开头,则会以执行python文件为根目录,如果以/开头,则为系统绝对路径,否则,以项目目录为根目录 save-path: ${EXPORT_PATH:excels} # 导出报告文件存放路径,如果以./或者../开头,则会以执行python文件为根目录,如果以/开头,则为系统绝对路径,否则,以项目目录为根目录
file-name: ${EXPORT_FILENAME:1323} file-name: ${EXPORT_FILENAME:real}
include-report: # 需要导出的报告类型列表,下面的顺序,也代表了excel中sheet的顺序 include-report: # 需要导出的报告类型列表,下面的顺序,也代表了excel中sheet的顺序
# - funds-report # 基金资料 # - funds-report # 基金资料
# - navs-report # 净值报告 # - navs-report # 净值报告
...@@ -224,16 +249,16 @@ reports: # 报告模块相关 ...@@ -224,16 +249,16 @@ reports: # 报告模块相关
- fixed-range-report # 固定区间收益报告 - fixed-range-report # 固定区间收益报告
- relative-range-report # 相对区间收益报告 - relative-range-report # 相对区间收益报告
robo-executor: # 执行器相关 robo-executor: # 执行器相关
use: ${ROBO_EXECUTOR:backtest} # 执行哪个执行器,优先取系统环境变量ROBO_EXECUTOR的值,默认backtest use: ${ROBO_EXECUTOR:real} # 执行哪个执行器,优先取系统环境变量ROBO_EXECUTOR的值,默认backtest
sync-data: ${SYNC_DATA:off} # 是否开启同步资料数据 sync-data: ${SYNC_DATA:off} # 是否开启同步资料数据
backtest: # 回测执行器相关 backtest: # 回测执行器相关
start-date: 2008-01-02 # 回测起始日期 start-date: 2022-09-01 # 回测起始日期
end-date: 2022-11-01 # 回测截止日期 end-date: 2023-02-20 # 回测截止日期
start-step: ${BACKTEST_START_STEP:4} # 回测从哪一步开始执行 1:计算资产ewma;2:计算资产池;3:计算最优投组:4:计算再平衡信号以及持仓投组 start-step: ${BACKTEST_START_STEP:4} # 回测从哪一步开始执行 1:计算资产ewma;2:计算资产池;3:计算最优投组:4:计算再平衡信号以及持仓投组
end-step: ${BACKTEST_END_STEP:4} # 回测从哪一步执行完成后结束执行 1:计算资产ewma;2:计算资产池;3:计算最优投组:4:计算再平衡信号以及持仓投组 end-step: ${BACKTEST_END_STEP:4} # 回测从哪一步执行完成后结束执行 1:计算资产ewma;2:计算资产池;3:计算最优投组:4:计算再平衡信号以及持仓投组
clean-up: on clean-up: off
real: # 实盘执行器 real: # 实盘执行器
start-date: 2022-11-01 # 实盘开始时间 start-date: 2022-09-01 # 实盘开始时间
include-date: include-date:
- 2023-02-18 - 2023-02-18
- 2023-03-25 - 2023-03-25
......
...@@ -29,7 +29,7 @@ class PortfoliosTest(unittest.TestCase): ...@@ -29,7 +29,7 @@ class PortfoliosTest(unittest.TestCase):
@autowired(names={'hold': 'next-re'}) @autowired(names={'hold': 'next-re'})
def test_build_hold(self, hold: PortfoliosHolder = None): def test_build_hold(self, hold: PortfoliosHolder = None):
hold.build_hold_portfolio(parse_date('2022-11-01'), PortfoliosRisk.FT9) hold.build_hold_portfolio(parse_date('2023-02-23'), PortfoliosRisk.FT9)
@autowired(names={'reportor': 'hold-report'}) @autowired(names={'reportor': 'hold-report'})
def test_hold_report(self, reportor: RoboReportor = None): def test_hold_report(self, reportor: RoboReportor = None):
......
import logging import logging
import unittest import unittest
from dateutil.relativedelta import relativedelta
from dateutil.relativedelta import relativedelta
from py_jftech import autowired, parse_date, to_str, next_workday from py_jftech import autowired, parse_date, to_str, next_workday
from api import RebalanceSignal, PortfoliosRisk, RebalanceRuler, RoboReportor from api import RebalanceSignal, PortfoliosRisk, RebalanceRuler, RoboReportor
......
...@@ -174,7 +174,6 @@ class RealExecutor(RoboExecutor): ...@@ -174,7 +174,6 @@ class RealExecutor(RoboExecutor):
for sync in self._syncs: for sync in self._syncs:
sync.do_sync() sync.do_sync()
date = self.curt_date date = self.curt_date
for date in pd.date_range(start='2008-01-17', end='2008-01-20'):
if is_workday(date) or date in self.include_date: if is_workday(date) or date in self.include_date:
date = prev_workday(date) date = prev_workday(date)
for risk in PortfoliosRisk: for risk in PortfoliosRisk:
......
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