import json import logging from py_jftech import component, autowired, format_date from pymysql import IntegrityError, constants from api import PortfoliosBuilder, PortfoliosRisk, AssetPool, Navs, PortfoliosType, Datum, SolveType, SolverFactory from portfolios.dao import robo_mpt_portfolios as rmp logger = logging.getLogger(__name__) @component(bean_name='mpt') class MptPortfoliosBuilder(PortfoliosBuilder): @autowired def __init__(self, assets: AssetPool = None, navs: Navs = None, datum: Datum = None, factory: SolverFactory = None): self._assets = assets self._navs = navs self._datum = datum self._factory = factory def get_portfolios(self, day, risk: PortfoliosRisk, type: PortfoliosType = PortfoliosType.NORMAL): try: portfolio = rmp.get_one(day, type, risk) if not portfolio: result, detail = self.build_portfolio(day, type) for build_risk, datas in result.items(): try: rmp.insert({ **datas, 'risk': build_risk, 'type': type, 'date': day }) except IntegrityError as e: code, msg = e.args if code != constants.ER.DUP_ENTRY: raise e portfolio = rmp.get_one(day, type, risk) if SolveType(portfolio['solve']) is not SolveType.INFEASIBLE: result = json.loads(portfolio['portfolio']) return {int(x[0]): x[1] for x in result.items()} return None except Exception as e: logger.exception(f"build protfolio of type[{type.name}] and risk[{risk.name}] with date[{format_date(day)}] failure.", e) raise e def build_portfolio(self, day, type: PortfoliosType): result = {} detail = {} for risk in PortfoliosRisk: 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) logger.debug({ 'Khist': len(solver.rtn_history), 'beta': solver.get_config('mpt.cvar-beta'), 'Kbeta': solver.k_beta, }) max_rtn, max_var, minCVaR_whenMaxR = solver.solve_max_rtn() min_rtn, min_var, maxCVaR_whenMinV = solver.solve_min_rtn() portfolio, cvar = solver.solve_mpt(min_rtn, max_rtn) result[risk] = { 'solve': SolveType.MPT, 'portfolio': json.dumps(portfolio), 'cvar': cvar } if portfolio else { 'solve': SolveType.INFEASIBLE } detail[risk] = { 'max_rtn': max_rtn, 'max_var': max_var, 'minCVaR_whenMaxR': minCVaR_whenMaxR, 'min_rtn': min_rtn, 'min_var': min_var, 'maxCVaR_whenMinV': maxCVaR_whenMinV, } return result, detail def clear(self, day=None, risk: PortfoliosRisk = None): rmp.delete(min_date=day, risk=risk) @component(bean_name='poem') class PoemPortfoliosBuilder(MptPortfoliosBuilder): def build_portfolio(self, day, type: PortfoliosType): result, detail = super(PoemPortfoliosBuilder, self).build_portfolio(day, type) for risk in PortfoliosRisk: if result[risk]['solve'] is SolveType.INFEASIBLE: continue solver = self._factory.create_solver(risk, type) solver.reset_navs(day) min_rtn = detail[risk]['min_rtn'] max_rtn = detail[risk]['max_rtn'] mpt_cvar = result[risk]['cvar'] maxCVaR_whenMinV = detail[risk]['maxCVaR_whenMinV'] portfolio, cvar = solver.solve_poem(min_rtn, max_rtn, mpt_cvar, maxCVaR_whenMinV) if portfolio: result[risk] = { 'solve': SolveType.POEM, 'portfolio': json.dumps(portfolio), 'cvar': cvar } detail[risk]['mpt_cvar'] = mpt_cvar return result, detail