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, \ PortfoliosChecker 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, checker: PortfoliosChecker = None): self._assets = assets self._navs = navs self._datum = datum self._factory = factory self._checker = checker def get_portfolios(self, day, risk: PortfoliosRisk, type: PortfoliosType = PortfoliosType.NORMAL): try: portfolio = rmp.get_one(day, type, risk) if not portfolio: result = self.build_portfolio(day, type) for build_risk, datas in result.items(): datas['portfolio'] = self._checker.check(day, json.loads(datas['portfolio'])) 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 portfolio 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 = {} portfolios = {} 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) navs_group = solver.reset_navs(day) for category, navs in navs_group.items(): # count = solver.get_config('asset-count')[0] # nav_count = len(navs.columns) # if count <= nav_count: # pass solver.set_navs(navs) solver.set_category(category) 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) portfolios = {**portfolios, **portfolio} result[risk] = { 'solve': SolveType.MPT, 'portfolio': json.dumps(portfolios), } if portfolios else { 'solve': SolveType.INFEASIBLE } return result def clear(self, day=None, risk: PortfoliosRisk = None): rmp.delete(min_date=day, risk=risk) @component(bean_name='mpt') class PoemPortfoliosBuilder(MptPortfoliosBuilder): def build_portfolio(self, day, type: PortfoliosType): result = {} portfolios = {} for risk in PortfoliosRisk: solver = self._factory.create_solver(risk, type) navs_group = solver.reset_navs(day) for category, navs in navs_group.items(): solver.set_navs(navs) solver.set_category(category) max_rtn, max_var, minCVaR_whenMaxR = solver.solve_max_rtn() min_rtn, min_var, maxCVaR_whenMinV = solver.solve_min_rtn() mpt_portfolio, mpt_cvar = solver.solve_mpt(min_rtn, max_rtn) portfolio, cvar = solver.solve_poem(min_rtn, max_rtn, mpt_cvar, maxCVaR_whenMinV) if not portfolio: portfolio = mpt_portfolio portfolios = {**portfolios, **portfolio} if portfolios: result[risk] = { 'solve': SolveType.POEM, 'portfolio': json.dumps(portfolios), } return result @component(bean_name='mpt') class MptARCPortfoliosBuilder(MptPortfoliosBuilder): def get_portfolios(self, day, risk: PortfoliosRisk, type: PortfoliosType = PortfoliosType.NORMAL): try: portfolio = rmp.get_one(day, type, risk) if not portfolio: result = self.build_portfolio(day, type) for build_risk, datas in result.items(): datas['portfolio'] = self._checker.check(day, json.loads(datas['portfolio'])) 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.", exc_info=e) raise e def build_portfolio(self, day, type: PortfoliosType): result = {} detail = {} 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) 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 @component(bean_name='mpt') class PoemARCPortfoliosBuilder(MptARCPortfoliosBuilder): def build_portfolio(self, day, type: PortfoliosType): result, detail = super(PoemARCPortfoliosBuilder, self).build_portfolio(day, type) risk = PortfoliosRisk.FT3 # 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 @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