def benchmark_build_linear(n_samples: int, n_risks: int, n_loop: int) -> None: print("-" * 60) print("Starting portfolio construction by linear programming") print("Parameters(n_samples: {0}, n_risks: {1}, n_loop: {2})".format( n_samples, n_risks, n_loop)) er = np.random.randn(n_samples) risk_exp = np.random.randn(n_samples, n_risks) bm = np.random.rand(n_samples) bm /= bm.sum() lbound = -0.04 ubound = 0.05 risk_lbound = bm @ risk_exp risk_ubound = bm @ risk_exp start = dt.datetime.now() for _ in range(n_loop): status, v, x = linear_builder(er, lbound, ubound, risk_exp, risk_target=(risk_lbound, risk_ubound)) impl_model_time = dt.datetime.now() - start print('{0:20s}: {1}'.format('Implemented model (ECOS)', impl_model_time)) c = -er bounds = [(lbound, ubound) for _ in range(n_samples)] a_eq = np.ones((1, n_samples)) a_eq = np.vstack((a_eq, risk_exp.T)) b_eq = np.hstack((np.array([1.]), risk_exp.T @ bm)) start = dt.datetime.now() for _ in range(n_loop): res = linprog(c, A_eq=a_eq, b_eq=b_eq, bounds=bounds, options={'maxiter': 10000}) benchmark_model_time = dt.datetime.now() - start print('{0:20s}: {1}'.format('Benchmark model (scipy)', benchmark_model_time)) np.testing.assert_array_almost_equal(x, res['x']) c = matrix(-er) aneq = matrix(a_eq) b = matrix(b_eq) g = matrix( np.vstack((np.diag(np.ones(n_samples)), -np.diag(np.ones(n_samples))))) h = matrix( np.hstack((ubound * np.ones(n_samples), -lbound * np.ones(n_samples)))) solvers.lp(c, g, h, solver='glpk') start = dt.datetime.now() for _ in range(n_loop): res2 = solvers.lp(c, g, h, aneq, b, solver='glpk') benchmark_model_time = dt.datetime.now() - start print('{0:20s}: {1}'.format('Benchmark model (glpk)', benchmark_model_time)) np.testing.assert_array_almost_equal(x, np.array(res2['x']).flatten())
def test_linear_build_with_to_constraint(self): bm = self.bm / self.bm.sum() eplson = 1e-6 turn_over_target = 0.1 risk_lbound = bm @ self.risk_exp risk_ubound = bm @ self.risk_exp risk_tolerance = 0.01 * np.abs(risk_lbound[:-1]) risk_lbound[:-1] = risk_lbound[:-1] - risk_tolerance risk_ubound[:-1] = risk_ubound[:-1] + risk_tolerance status, _, w = linear_builder(self.er, 0., 0.01, self.risk_exp, risk_target=(risk_lbound, risk_ubound), turn_over_target=turn_over_target, current_position=self.current_pos) self.assertEqual(status, 'optimal') self.assertAlmostEqual(np.sum(w), 1.) self.assertTrue(np.all(w <= 0.01 + eplson)) self.assertTrue(np.all(w >= -eplson)) self.assertAlmostEqual( np.abs(w - self.current_pos).sum(), turn_over_target) calc_risk = (w - bm) @ self.risk_exp / np.abs(bm @ self.risk_exp) self.assertTrue(np.all(np.abs(calc_risk) <= 1.0001e-2))
def test_linear_build(self): bm = self.bm / self.bm.sum() eplson = 1e-6 status, _, w = linear_builder(self.er, 0., 0.01, self.risk_exp, (bm @ self.risk_exp, bm @ self.risk_exp)) self.assertEqual(status, 'optimal') self.assertAlmostEqual(np.sum(w), 1.) self.assertTrue(np.all(w <= 0.01 + eplson)) self.assertTrue(np.all(w >= -eplson)) calc_risk = (w - bm) @ self.risk_exp expected_risk = np.zeros(self.risk_exp.shape[1]) np.testing.assert_array_almost_equal(calc_risk, expected_risk)
def test_linear_build_with_inequality_constraints(self): bm = self.bm / self.bm.sum() eplson = 1e-6 risk_lbound = bm @ self.risk_exp risk_ubound = bm @ self.risk_exp risk_tolerance = 0.01 * np.abs(risk_lbound[:-1]) risk_lbound[:-1] = risk_lbound[:-1] - risk_tolerance risk_ubound[:-1] = risk_ubound[:-1] + risk_tolerance status, _, w = linear_builder(self.er, 0., 0.01, self.risk_exp, risk_target=(risk_lbound, risk_ubound)) self.assertEqual(status, 'optimal') self.assertAlmostEqual(np.sum(w), 1.) self.assertTrue(np.all(w <= 0.01 + eplson)) self.assertTrue(np.all(w >= -eplson)) calc_risk = (w - bm) @ self.risk_exp / np.abs(bm @ self.risk_exp) self.assertTrue(np.all(np.abs(calc_risk) <= 1.01e-2))
def er_portfolio_analysis( er: np.ndarray, industry: np.ndarray, dx_return: np.ndarray, constraints: Optional[Union[LinearConstraints, Constraints]] = None, detail_analysis=True, benchmark: Optional[np.ndarray] = None, is_tradable: Optional[np.ndarray] = None, method='risk_neutral', **kwargs) -> Tuple[pd.DataFrame, Optional[pd.DataFrame]]: er = er.flatten() def create_constraints(benchmark, **kwargs): if 'lbound' in kwargs: lbound = kwargs['lbound'].copy() del kwargs['lbound'] else: lbound = np.maximum(0., benchmark - 0.01) if 'ubound' in kwargs: ubound = kwargs['ubound'].copy() del kwargs['ubound'] else: ubound = 0.01 + benchmark if is_tradable is not None: ubound[~is_tradable] = np.minimum(lbound, ubound)[~is_tradable] risk_lbound, risk_ubound = constraints.risk_targets() cons_exp = constraints.risk_exp return lbound, ubound, cons_exp, risk_lbound, risk_ubound if method == 'risk_neutral': lbound, ubound, cons_exp, risk_lbound, risk_ubound = create_constraints( benchmark, **kwargs) turn_over_target = kwargs.get('turn_over_target') current_position = kwargs.get('current_position') status, _, weights = linear_builder(er, risk_constraints=cons_exp, lbound=lbound, ubound=ubound, risk_target=(risk_lbound, risk_ubound), turn_over_target=turn_over_target, current_position=current_position) if status != 'optimal': raise ValueError( 'linear programming optimizer in status: {0}'.format(status)) elif method == 'rank': weights = rank_build( er, use_rank=kwargs['use_rank'], masks=is_tradable).flatten() * benchmark.sum() / kwargs['use_rank'] elif method == 'ls' or method == 'long_short': weights = long_short_builder(er).flatten() elif method == 'mv' or method == 'mean_variance': lbound, ubound, cons_exp, risk_lbound, risk_ubound = create_constraints( benchmark, **kwargs) cov = kwargs['cov'] if 'lam' in kwargs: lam = kwargs['lam'] else: lam = 1. status, _, weights = mean_variance_builder(er, cov=cov, bm=benchmark, lbound=lbound, ubound=ubound, risk_exposure=cons_exp, risk_target=(risk_lbound, risk_ubound), lam=lam) if status != 'optimal': raise ValueError( 'mean variance optimizer in status: {0}'.format(status)) elif method == 'tv' or method == 'target_vol': lbound, ubound, cons_exp, risk_lbound, risk_ubound = create_constraints( benchmark, **kwargs) cov = kwargs['cov'] if 'target_vol' in kwargs: target_vol = kwargs['target_vol'] else: target_vol = 1. status, _, weights = target_vol_builder(er, cov=cov, bm=benchmark, lbound=lbound, ubound=ubound, risk_exposure=cons_exp, risk_target=(risk_lbound, risk_ubound), vol_low=0, vol_high=target_vol) else: raise ValueError("Unknown building type ({0})".format(method)) if detail_analysis: analysis = simple_settle(weights, dx_return, industry, benchmark) else: analysis = None return pd.DataFrame({'weight': weights, 'industry': industry, 'er': er}), \ analysis