Commit 0f7bec38 authored by wenwen.tang's avatar wenwen.tang 😕
parent 1eff3e20
...@@ -2,6 +2,7 @@ import json ...@@ -2,6 +2,7 @@ import json
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from sys import exception from sys import exception
import numpy as np
import pandas as pd import pandas as pd
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from empyrical import sortino_ratio, annual_volatility from empyrical import sortino_ratio, annual_volatility
...@@ -117,14 +118,15 @@ class FundDividendSortinoAssetOptimize(SortinoAssetOptimize): ...@@ -117,14 +118,15 @@ class FundDividendSortinoAssetOptimize(SortinoAssetOptimize):
sortino = sortino.T sortino = sortino.T
sortino['score'] = sortino.apply(lambda r: sum([x['weight'] * r[x['name']] for x in self._config]), axis=1) sortino['score'] = sortino.apply(lambda r: sum([x['weight'] * r[x['name']] for x in self._config]), axis=1)
sortino.sort_values('score', ascending=False, inplace=True) sortino.sort_values('score', ascending=False, inplace=True)
records = sortino.to_dict(orient='records') records = sortino.to_dict(orient='index')
data = {key: record for record in records for key in fund_ids} data = {fund_ids[k]: v for k, v in records.items()}
self.save_sortino(day, data) self.save_sortino(day, data)
# 取得分数高的前optimize_count个 # 取得分数高的前optimize_count个
return pct_change.columns[sortino.index[0:self.optimize_count]].values, sortino['score'] return pct_change.columns[sortino.index[0:self.optimize_count]].values, sortino['score']
def save_sortino(self, day, datas): def save_sortino(self, day, datas):
for key, record in datas.items(): for key, record in datas.items():
record = {k: v for k, v in record.items() if not (np.isnan(v) or np.isinf(v))}
robo_indicator.update_sortino(key, day, json.dumps(record)) robo_indicator.update_sortino(key, day, json.dumps(record))
def get_optimize_pool(self, day): def get_optimize_pool(self, day):
...@@ -143,6 +145,8 @@ class FundDividendSortinoAssetOptimize(SortinoAssetOptimize): ...@@ -143,6 +145,8 @@ class FundDividendSortinoAssetOptimize(SortinoAssetOptimize):
pool.extend(self.find_optimize(tuple(fund_group), day)[0]) pool.extend(self.find_optimize(tuple(fund_group), day)[0])
elif len(fund_group) <= self.optimize_count: elif len(fund_group) <= self.optimize_count:
pool.extend(fund_group) pool.extend(fund_group)
if len(pool) < get_config('portfolios.solver.asset-count')[0]:
raise ValueError(f"基金优选个数小于{get_config('portfolios.solver.asset-count')[0]},请调整参数")
rop.insert(day, AssetPoolType.OPTIMIZE, sorted(pool)) rop.insert(day, AssetPoolType.OPTIMIZE, sorted(pool))
last_one = rop.get_last_one(day=day, type=AssetPoolType.OPTIMIZE) last_one = rop.get_last_one(day=day, type=AssetPoolType.OPTIMIZE)
return json.loads(last_one['asset_ids']) return json.loads(last_one['asset_ids'])
...@@ -156,10 +160,12 @@ class FundDividendSortinoAssetOptimize(SortinoAssetOptimize): ...@@ -156,10 +160,12 @@ class FundDividendSortinoAssetOptimize(SortinoAssetOptimize):
fund_ids = [fund['id'] for fund in funds] fund_ids = [fund['id'] for fund in funds]
pct_change = pd.DataFrame(self.get_pct_change(fund_ids, day)) pct_change = pd.DataFrame(self.get_pct_change(fund_ids, day))
pct_change.set_index('date', inplace=True) pct_change.set_index('date', inplace=True)
ratio = annual_volatility( pct_change = pct_change.truncate(before=(day - relativedelta(**self.annual_volatility_section[0])))
pct_change.truncate(before=(day - relativedelta(**self.annual_volatility_section[0])))) # 时间未够计算年化波动的直接剔除
funds = [fund for fund in funds if fund['id'] in pct_change.columns]
ratio = annual_volatility(pct_change)
ratio = pd.Series(ratio).to_dict() ratio = pd.Series(ratio).to_dict()
annual = {fund_id: value for fund_id in fund_ids for value in ratio.values()} annual = dict(zip(pct_change.columns, ratio.values()))
self.save_annual(day, annual) self.save_annual(day, annual)
filters = self.annual_volatility_filter filters = self.annual_volatility_filter
for f in filters: for f in filters:
...@@ -167,12 +173,14 @@ class FundDividendSortinoAssetOptimize(SortinoAssetOptimize): ...@@ -167,12 +173,14 @@ class FundDividendSortinoAssetOptimize(SortinoAssetOptimize):
exclude = f.get('exclude') exclude = f.get('exclude')
volatility = f.get('volatility') volatility = f.get('volatility')
records = [fund for fund in funds if fund['customType'] == customType and fund['id'] in annual.keys()] records = [fund for fund in funds if fund['customType'] == customType and fund['id'] in annual.keys()]
if exclude: if exclude is not None:
exclude = exclude if len(records) > exclude else len(records) exclude = exclude if len(records) > exclude else len(records)
records = records[0:-exclude] filtered.extend(records[-exclude:])
if volatility: if volatility is not None:
records = [record for record in records if annual.get(record['id']) > volatility] records = [record for record in records[0:exclude] if annual.get(record['id']) > volatility]
filtered.extend(records) filtered.extend(records)
for f in filtered:
funds.remove(f)
return funds return funds
def save_annual(self, day, annual): def save_annual(self, day, annual):
...@@ -230,6 +238,7 @@ class FundDividendSortinoAssetOptimize(SortinoAssetOptimize): ...@@ -230,6 +238,7 @@ class FundDividendSortinoAssetOptimize(SortinoAssetOptimize):
fund_navs.sort_values('nav_date', inplace=True) 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 = fund_navs.pivot_table(index='nav_date', columns='fund_id', values='nav_cal')
fund_navs.fillna(method='ffill', inplace=True) fund_navs.fillna(method='ffill', inplace=True)
fund_navs.dropna(axis=1, inplace=True)
result = round(fund_navs.pct_change().dropna(), 4) result = round(fund_navs.pct_change().dropna(), 4)
result.reset_index(inplace=True) result.reset_index(inplace=True)
result.rename(columns={'nav_date': 'date'}, inplace=True) result.rename(columns={'nav_date': 'date'}, inplace=True)
......
...@@ -80,11 +80,14 @@ asset-pool: # 资产池模块 ...@@ -80,11 +80,14 @@ asset-pool: # 资产池模块
optimize-count: 3 #基金优选个数 optimize-count: 3 #基金优选个数
annual-volatility-filter: #1各资产年化波动率末exclude位 2各资产年化波动率大于volatility annual-volatility-filter: #1各资产年化波动率末exclude位 2各资产年化波动率大于volatility
- customType: 1 - customType: 1
exclude: 2
volatility: 0
- customType: 2
exclude: 1 exclude: 1
volatility: 0 volatility: 0
- customType: 2
exclude: 3
volatility: 0.05
- customType: 3
exclude: 2
volatility: 0.001
annual-volatility-section: # 波动率时间区间 annual-volatility-section: # 波动率时间区间
- years: 3 - years: 3
portfolios: # 投组模块 portfolios: # 投组模块
...@@ -249,7 +252,7 @@ robo-executor: # 执行器相关 ...@@ -249,7 +252,7 @@ robo-executor: # 执行器相关
use: ${ROBO_EXECUTOR:backtest} # 执行哪个执行器,优先取系统环境变量ROBO_EXECUTOR的值,默认backtest use: ${ROBO_EXECUTOR:backtest} # 执行哪个执行器,优先取系统环境变量ROBO_EXECUTOR的值,默认backtest
sync-data: ${SYNC_DATA:off} # 是否开启同步资料数据 sync-data: ${SYNC_DATA:off} # 是否开启同步资料数据
backtest: # 回测执行器相关 backtest: # 回测执行器相关
start-date: 2023-01-02 # 回测起始日期 start-date: 2013-01-02 # 回测起始日期
end-date: 2023-10-31 # 回测截止日期 end-date: 2023-10-31 # 回测截止日期
sealing-period: 10 #调仓封闭期 sealing-period: 10 #调仓封闭期
start-step: ${BACKTEST_START_STEP:1} # 回测从哪一步开始执行 1:计算资产池;2:计算最优投组:3:计算再平衡信号以及持仓投组 start-step: ${BACKTEST_START_STEP:1} # 回测从哪一步开始执行 1:计算资产池;2:计算最优投组:3:计算再平衡信号以及持仓投组
......
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