Commit 938ef10c authored by jichao's avatar jichao

回测报告增加基金池

parent 22435898
...@@ -89,11 +89,11 @@ class CvarEwmaAssetRisk(AssetRisk): ...@@ -89,11 +89,11 @@ class CvarEwmaAssetRisk(AssetRisk):
start_date = last_start['date'] if last_start else self.risk_start_date start_date = last_start['date'] if last_start else self.risk_start_date
rtns = pd.DataFrame(self.get_income_return(asset_id, min_date=start_date, max_date=day)) rtns = pd.DataFrame(self.get_income_return(asset_id, min_date=start_date, max_date=day))
risk_rtns = rtns[rtns.date <= last['date']] risk_rtns = rtns[rtns.date <= last['date']]
cvar_start_date = risk_rtns.loc[risk_rtns.rtn.idxmin()].date cvar_start_date = risk_rtns.loc[risk_rtns.nav.idxmin()].date
for index, row in rtns[rtns.date >= cvar_start_date].iterrows(): for index, row in rtns[rtns.date >= cvar_start_date].iterrows():
tigger = False tigger = False
cvar_rtns = rtns[(rtns.date >= cvar_start_date) & (rtns.date <= row['date'])] cvar_rtns = rtns[(rtns.date >= cvar_start_date) & (rtns.date <= row['date'])]
if row['rtn'] < rtns[rtns.date == cvar_start_date].iloc[0].rtn: if row.nav < rtns[rtns.date == cvar_start_date].iloc[0].nav:
# 当日回报率跌破最低点, 则直接触发 # 当日回报率跌破最低点, 则直接触发
tigger = True tigger = True
elif row['rtn'] <= self._config['cvar']['threshold'] and len(cvar_rtns) >= self._config['cvar']['min-volume']: elif row['rtn'] <= self._config['cvar']['threshold'] and len(cvar_rtns) >= self._config['cvar']['min-volume']:
...@@ -136,7 +136,7 @@ class CvarEwmaAssetRisk(AssetRisk): ...@@ -136,7 +136,7 @@ class CvarEwmaAssetRisk(AssetRisk):
fund_navs.dropna(inplace=True) fund_navs.dropna(inplace=True)
if min_date: if min_date:
fund_navs = fund_navs[fund_navs.nav_date >= pd.to_datetime(min_date)] fund_navs = fund_navs[fund_navs.nav_date >= pd.to_datetime(min_date)]
fund_navs.rename(columns={'nav_date': 'date'}, inplace=True) fund_navs.rename(columns={'nav_date': 'date', 'nav_cal': 'nav'}, inplace=True)
fund_navs = fund_navs[['date', 'rtn']] fund_navs = fund_navs[['date', 'nav', 'rtn']]
return fund_navs.to_dict('records') return fund_navs.to_dict('records')
return [] return []
...@@ -3,7 +3,7 @@ import unittest ...@@ -3,7 +3,7 @@ import unittest
from py_jftech import autowired, parse_date, to_str from py_jftech import autowired, parse_date, to_str
from api import AssetPool, RoboReportor from api import AssetPool, RoboReportor, AssetRisk
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -19,6 +19,10 @@ class AssetPoolTest(unittest.TestCase): ...@@ -19,6 +19,10 @@ class AssetPoolTest(unittest.TestCase):
result = report.load_report(max_date=parse_date('2009-12-31')) result = report.load_report(max_date=parse_date('2009-12-31'))
logger.info(to_str(result)) logger.info(to_str(result))
@autowired
def test_next_risk_date(self, risk: AssetRisk = None):
risk.build_risk_date(asset_id=46)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -208,23 +208,24 @@ reports: # 报告模块相关 ...@@ -208,23 +208,24 @@ 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:navs} file-name: ${EXPORT_FILENAME:asset-pool}
include-report: # 需要导出的报告类型列表,下面的顺序,也代表了excel中sheet的顺序 include-report: # 需要导出的报告类型列表,下面的顺序,也代表了excel中sheet的顺序
# - navs-report # 净值报告 # - navs-report # 净值报告
- hold-report # 持仓报告 # - hold-report # 持仓报告
- signal-report # 信号报告 # - signal-report # 信号报告
- asset-pool-report # 基金池报告 - asset-pool-report # 基金池报告
- benckmark-report # benckmark报告 # - benckmark-report # benckmark报告
- indicators-report # 各种特殊指标报告 # - indicators-report # 各种特殊指标报告
- 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:backtest} #执行哪个执行器,优先取系统环境变量ROBO_EXECUTOR的值,默认backtest
sync-data: ${SYNC_DATA:on} sync-data: ${SYNC_DATA:off}
backtest: # 回测执行器相关 backtest: # 回测执行器相关
start-date: 2008-01-02 # 回测起始日期 start-date: 2008-01-02 # 回测起始日期
end-date: 2022-11-01 # 回测截止日期 end-date: 2022-11-01 # 回测截止日期
start-step: 4 # 回测从哪一步开始执行 1:计算资产ewma;2:计算资产池;3:计算最优投组:4:计算再平衡信号以及持仓投组 start-step: 1 # 回测从哪一步开始执行 1:计算资产ewma;2:计算资产池;3:计算最优投组:4:计算再平衡信号以及持仓投组
end-step: 2 # 回测从哪一步执行完成后结束执行 1:计算资产ewma;2:计算资产池;3:计算最优投组:4:计算再平衡信号以及持仓投组
clean-up: on clean-up: on
real: # 实盘执行器 real: # 实盘执行器
start-date: 2022-11-01 # 实盘开始时间 start-date: 2022-11-01 # 实盘开始时间
......
...@@ -30,6 +30,9 @@ class BacktestStep(Enum): ...@@ -30,6 +30,9 @@ class BacktestStep(Enum):
def within(self, step: Enum): def within(self, step: Enum):
return self.value <= step.value return self.value <= step.value
def without(self, step: Enum):
return self.value >= step.value
@component(bean_name='backtest') @component(bean_name='backtest')
class BacktestExecutor(RoboExecutor): class BacktestExecutor(RoboExecutor):
...@@ -55,6 +58,10 @@ class BacktestExecutor(RoboExecutor): ...@@ -55,6 +58,10 @@ class BacktestExecutor(RoboExecutor):
def start_step(self) -> BacktestStep: def start_step(self) -> BacktestStep:
return BacktestStep(self._config['start-step']) return BacktestStep(self._config['start-step'])
@property
def end_step(self) -> BacktestStep:
return BacktestStep(self._config['end-step']) if 'end-step' in self._config else BacktestStep.HOLD_PORTFOLIO
@property @property
def is_sync_data(self): def is_sync_data(self):
return get_config(__name__)['sync-data'] return get_config(__name__)['sync-data']
...@@ -68,16 +75,16 @@ class BacktestExecutor(RoboExecutor): ...@@ -68,16 +75,16 @@ class BacktestExecutor(RoboExecutor):
return self._config['clean-up'] if 'clean-up' in self._config else True return self._config['clean-up'] if 'clean-up' in self._config else True
def clear_datas(self): def clear_datas(self):
if self.start_step.within(BacktestStep.EWMA_VALUE): if self.start_step.within(BacktestStep.EWMA_VALUE) and self.end_step.without(BacktestStep.EWMA_VALUE):
logger.info('start to clear fund ewma value'.center(50, '-')) logger.info('start to clear fund ewma value'.center(50, '-'))
self._risk.clear() self._risk.clear()
if self.start_step.within(BacktestStep.ASSET_POOL): if self.start_step.within(BacktestStep.ASSET_POOL) and self.end_step.without(BacktestStep.ASSET_POOL):
logger.info('start to clear asset pool'.center(50, '-')) logger.info('start to clear asset pool'.center(50, '-'))
self._pool.clear() self._pool.clear()
if self.start_step.within(BacktestStep.NORMAL_PORTFOLIO): if self.start_step.within(BacktestStep.NORMAL_PORTFOLIO) and self.end_step.without(BacktestStep.NORMAL_PORTFOLIO):
logger.info('start to clear normal portfolios'.center(50, '-')) logger.info('start to clear normal portfolios'.center(50, '-'))
self._builder.clear() self._builder.clear()
if self.start_step.within(BacktestStep.HOLD_PORTFOLIO): if self.start_step.within(BacktestStep.HOLD_PORTFOLIO) and self.end_step.without(BacktestStep.HOLD_PORTFOLIO):
logger.info('start to clear hold portfolios'.center(50, '-')) logger.info('start to clear hold portfolios'.center(50, '-'))
self._hold.clear() self._hold.clear()
self._rule.clear_signal() self._rule.clear_signal()
...@@ -88,12 +95,12 @@ class BacktestExecutor(RoboExecutor): ...@@ -88,12 +95,12 @@ class BacktestExecutor(RoboExecutor):
sync.do_sync() sync.do_sync()
if self.is_clean_up: if self.is_clean_up:
self.clear_datas() self.clear_datas()
if self.start_step.within(BacktestStep.EWMA_VALUE): if self.start_step.within(BacktestStep.EWMA_VALUE) and self.end_step.without(BacktestStep.EWMA_VALUE):
logger.info("start to build fund ewma value.".center(50, '-')) logger.info("start to build fund ewma value.".center(50, '-'))
now = dt.now() now = dt.now()
wait([self.async_build_risk_date(x['id']) for x in self._datum.get_datums(type=DatumType.FUND, risk=(3, 4, 5))]) wait([self.async_build_risk_date(x['id']) for x in self._datum.get_datums(type=DatumType.FUND, risk=(3, 4, 5))])
logger.info(f"build fund ewma value success, use[{(dt.now() - now).seconds}s]") logger.info(f"build fund ewma value success, use[{(dt.now() - now).seconds}s]")
if self.start_step.within(BacktestStep.ASSET_POOL): if self.start_step.within(BacktestStep.ASSET_POOL) and self.end_step.without(BacktestStep.ASSET_POOL):
logger.info("start to build asset pool".center(50, '-')) logger.info("start to build asset pool".center(50, '-'))
now = dt.now() now = dt.now()
workdays = workday_range(self.start_date, self.end_date) workdays = workday_range(self.start_date, self.end_date)
...@@ -102,12 +109,12 @@ class BacktestExecutor(RoboExecutor): ...@@ -102,12 +109,12 @@ class BacktestExecutor(RoboExecutor):
for date in workdays: for date in workdays:
self._pool.get_pool(date) self._pool.get_pool(date)
logger.info(f"build asset pool success, use[{(dt.now() - now).seconds}s]") logger.info(f"build asset pool success, use[{(dt.now() - now).seconds}s]")
if self.start_step.within(BacktestStep.NORMAL_PORTFOLIO): if self.start_step.within(BacktestStep.NORMAL_PORTFOLIO) and self.end_step.without(BacktestStep.NORMAL_PORTFOLIO):
logger.info("start to build normal portfolios".center(50, '-')) logger.info("start to build normal portfolios".center(50, '-'))
now = dt.now() now = dt.now()
wait([self.async_build_portfolios(day, risk) for risk in PortfoliosRisk for day in workday_range(self.start_date, self.end_date)]) wait([self.async_build_portfolios(day, risk) for risk in PortfoliosRisk for day in workday_range(self.start_date, self.end_date)])
logger.info(f"build normal portfolios success, use[{(dt.now() - now).seconds}s]") logger.info(f"build normal portfolios success, use[{(dt.now() - now).seconds}s]")
if self.start_step.within(BacktestStep.HOLD_PORTFOLIO): if self.start_step.within(BacktestStep.HOLD_PORTFOLIO) and self.end_step.without(BacktestStep.HOLD_PORTFOLIO):
logger.info("start to build hold portfolios".center(50, '-')) logger.info("start to build hold portfolios".center(50, '-'))
now = dt.now() now = dt.now()
wait([self.async_build_hold(x) for x in PortfoliosRisk]) wait([self.async_build_hold(x) for x 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