Commit f502aea2 authored by wenwen.tang's avatar wenwen.tang 😕

加入risk_parity

parent 5bd442b0
...@@ -51,6 +51,7 @@ class SolveType(Enum): ...@@ -51,6 +51,7 @@ class SolveType(Enum):
INFEASIBLE = 0 INFEASIBLE = 0
MPT = 1 MPT = 1
POEM = 2 POEM = 2
RISK_PARITY = 3
@unique @unique
...@@ -365,6 +366,15 @@ class Solver(ABC): ...@@ -365,6 +366,15 @@ class Solver(ABC):
''' '''
pass pass
@abstractmethod
def solve_risk_parity(self):
'''
risk_parity计算
:return: 投组
'''
pass
@abstractmethod @abstractmethod
def reset_navs(self, day): def reset_navs(self, day):
''' '''
......
...@@ -40,7 +40,7 @@ py-jftech: ...@@ -40,7 +40,7 @@ py-jftech:
backtest: robo_executor.BacktestExecutor backtest: robo_executor.BacktestExecutor
datum: basic.datum.DefaultDatum datum: basic.datum.DefaultDatum
hold-report: portfolios.holder.DivHoldReportor hold-report: portfolios.holder.DivHoldReportor
mpt: portfolios.builder.PoemARCPortfoliosBuilder mpt: portfolios.builder.RiskParityARCPortfoliosBuilder
dividend-holder: portfolios.holder.InvTrustPortfoliosHolder dividend-holder: portfolios.holder.InvTrustPortfoliosHolder
navs-sync: basic.sync.FundNavSync navs-sync: basic.sync.FundNavSync
email: email:
...@@ -115,7 +115,7 @@ portfolios: # 投组模块 ...@@ -115,7 +115,7 @@ portfolios: # 投组模块
checker: #投组检测模块 checker: #投组检测模块
switch: on #是否开启检查 switch: on #是否开启检查
custom-type-priority: [3,2,1,4] # 检测优先级 custom-type-priority: [3,2,1,4] # 检测优先级
month-fund-filter: {2:['TEMSCFA LX Equity'],12:['TEMHYAD LX Equity','TEMFIAI LX Equity']} # 根据月份删除某几档基金 month-fund-filter: {} # 根据月份删除某几档基金
reports: # 报告模块相关 reports: # 报告模块相关
navs: navs:
type: FUND type: FUND
...@@ -243,7 +243,7 @@ robo-executor: # 执行器相关 ...@@ -243,7 +243,7 @@ robo-executor: # 执行器相关
start-date: 2023-01-02 # 回测起始日期 start-date: 2023-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:2} # 回测从哪一步开始执行 1:计算资产池;2:计算最优投组:3:计算再平衡信号以及持仓投组
end-step: ${BACKTEST_END_STEP:3} # 回测从哪一步执行完成后结束执行 1:计算资产池;2:计算最优投组:3:计算再平衡信号以及持仓投组 end-step: ${BACKTEST_END_STEP:3} # 回测从哪一步执行完成后结束执行 1:计算资产池;2:计算最优投组:3:计算再平衡信号以及持仓投组
clean-up: on clean-up: on
real: # 实盘执行器 real: # 实盘执行器
......
...@@ -121,7 +121,7 @@ class MptARCPortfoliosBuilder(MptPortfoliosBuilder): ...@@ -121,7 +121,7 @@ class MptARCPortfoliosBuilder(MptPortfoliosBuilder):
try: try:
portfolio = rmp.get_one(day, type, risk) portfolio = rmp.get_one(day, type, risk)
if not portfolio: if not portfolio:
result, detail = self.build_portfolio(day, type) result = self.build_portfolio(day, type)
for build_risk, datas in result.items(): for build_risk, datas in result.items():
datas['portfolio'] = self._checker.check(day, json.loads(datas['portfolio'])) datas['portfolio'] = self._checker.check(day, json.loads(datas['portfolio']))
try: try:
...@@ -141,7 +141,9 @@ class MptARCPortfoliosBuilder(MptPortfoliosBuilder): ...@@ -141,7 +141,9 @@ class MptARCPortfoliosBuilder(MptPortfoliosBuilder):
return {int(x[0]): x[1] for x in result.items()} return {int(x[0]): x[1] for x in result.items()}
return None return None
except Exception as e: except Exception as e:
logger.exception(f"build protfolio of type[{type.name}] and risk[{risk.name}] with date[{format_date(day)}] failure.", exc_info=e) logger.exception(
f"build protfolio of type[{type.name}] and risk[{risk.name}] with date[{format_date(day)}] failure.",
exc_info=e)
raise e raise e
def build_portfolio(self, day, type: PortfoliosType): def build_portfolio(self, day, type: PortfoliosType):
...@@ -180,6 +182,7 @@ class MptARCPortfoliosBuilder(MptPortfoliosBuilder): ...@@ -180,6 +182,7 @@ class MptARCPortfoliosBuilder(MptPortfoliosBuilder):
@component(bean_name='mpt') @component(bean_name='mpt')
class PoemARCPortfoliosBuilder(MptARCPortfoliosBuilder): class PoemARCPortfoliosBuilder(MptARCPortfoliosBuilder):
def build_portfolio(self, day, type: PortfoliosType): def build_portfolio(self, day, type: PortfoliosType):
result, detail = super(PoemARCPortfoliosBuilder, self).build_portfolio(day, type) result, detail = super(PoemARCPortfoliosBuilder, self).build_portfolio(day, type)
risk = PortfoliosRisk.FT3 risk = PortfoliosRisk.FT3
...@@ -200,3 +203,24 @@ class PoemARCPortfoliosBuilder(MptARCPortfoliosBuilder): ...@@ -200,3 +203,24 @@ class PoemARCPortfoliosBuilder(MptARCPortfoliosBuilder):
} }
detail[risk]['mpt_cvar'] = mpt_cvar detail[risk]['mpt_cvar'] = mpt_cvar
return result, detail return result, detail
@component(bean_name='mpt')
class RiskParityARCPortfoliosBuilder(MptPortfoliosBuilder):
def build_portfolio(self, day, type: PortfoliosType):
result = {}
risk = PortfoliosRisk.FT3
logger.info(
f"start to build protfolio of type[{type.name}] and risk[{risk.name}] with date[{format_date(day)}]")
solver = self._factory.create_solver(risk, type)
solver.reset_navs(day)
portfolio = solver.solve_risk_parity()
result[risk] = {
'solve': SolveType.RISK_PARITY,
'portfolio': json.dumps(portfolio),
} if portfolio else {
'solve': SolveType.INFEASIBLE
}
return result
...@@ -81,6 +81,10 @@ class DefaultSolver(Solver): ...@@ -81,6 +81,10 @@ class DefaultSolver(Solver):
rtn = (self.navs / self.navs.shift(1) - 1)[1:] rtn = (self.navs / self.navs.shift(1) - 1)[1:]
return rtn.cov() * 252 return rtn.cov() * 252
@property
def risk_parity_sigma(self):
return self.navs.cov()
@property @property
def rtn_history(self): def rtn_history(self):
result = self.rtn_matrix * 12 result = self.rtn_matrix * 12
...@@ -188,6 +192,14 @@ class DefaultSolver(Solver): ...@@ -188,6 +192,14 @@ class DefaultSolver(Solver):
self.debug_solve_result(model) self.debug_solve_result(model)
return self.calc_port_weight(model), self.calc_port_cvar(model) return self.calc_port_weight(model), self.calc_port_cvar(model)
def solve_risk_parity(self):
model = self.create_model()
model.objective = Objective(expr=sum(
[(model.z[i] * model.w[i] * (self.risk_parity_sigma.iloc[i] @ model.w) - model.z[j] * model.w[j] * (self.risk_parity_sigma.iloc[j] @ model.w)) ** 2
for i in model.indices for j in model.indices]), sense=minimize)
self._solver.solve(model)
return self.calc_port_weight(model)
def calc_port_weight(self, model): def calc_port_weight(self, model):
id_list = self.navs.columns id_list = self.navs.columns
weight_list = [] weight_list = []
......
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