Commit e9e7fc79 authored by Dr.李's avatar Dr.李

1. added new constraints implementation;

2. bump version to 0.1.1
parent fa7f2d68
......@@ -6,4 +6,4 @@ Created on 2017-4-25
"""
__version__ = "0.1.0"
__version__ = "0.1.1"
......@@ -5,17 +5,133 @@ Created on 2017-7-21
@author: cheng.li
"""
from deprecated import deprecated
from math import inf
import numpy as np
import pandas as pd
from enum import IntEnum
from typing import Tuple
from typing import Optional
from typing import Dict
from typing import List
from typing import Union
from typing import Iterable
from PyFin.api import pyFinAssert
class BoundaryDirection(IntEnum):
LOWER = -1
UPPER = 1
class BoundaryType(IntEnum):
ABSOLUTE = 0
RELATIVE = 1
class BoundaryImpl(object):
def __init__(self,
direction: BoundaryDirection,
b_type: BoundaryType,
val: float):
self.direction = direction
self.b_type = b_type
self.val = val
self._validation()
def _validation(self):
pyFinAssert(self.b_type == BoundaryType.ABSOLUTE or self.b_type == BoundaryType.RELATIVE,
ValueError,
"Boundary Type {0} is not recognized".format(self.b_type))
pyFinAssert(self.direction == BoundaryDirection.LOWER or self.direction == BoundaryDirection.UPPER,
ValueError,
"Boundary direction {0} is not recognized".format(self.direction))
def __call__(self, center: float):
if self.b_type == BoundaryType.ABSOLUTE:
return self.val + center
else:
pyFinAssert(center > 0., ValueError, "relative bounds only support positive back bone value")
return self.val * center
class BoxBoundary(object):
def __init__(self,
lower_bound: BoundaryImpl,
upper_bound: BoundaryImpl):
self.lower = lower_bound
self.upper = upper_bound
def bounds(self, center):
l_b, u_b = self.lower(center), self.upper(center)
pyFinAssert(l_b <= u_b, ValueError, "lower bound should be lower then upper bound")
return l_b, u_b
def create_box_bounds(names: List[str],
b_type: BoundaryType,
l_val: Union[Iterable[float], float],
u_val: Union[Iterable[float], float]) -> Dict[str, BoxBoundary]:
"""
helper function to quickly create a series of bounds
"""
bounds = dict()
if not hasattr(l_val, '__iter__'):
l_val = np.array([l_val] * len(names))
if not hasattr(u_val, '__iter__'):
u_val = np.array([u_val] * len(names))
for i, name in enumerate(names):
lower = BoundaryImpl(BoundaryDirection.LOWER,
b_type,
l_val[i])
upper = BoundaryImpl(BoundaryDirection.UPPER,
b_type,
u_val[i])
bounds[name] = BoxBoundary(lower, upper)
return bounds
class LinearConstraints(object):
def __init__(self,
bounds: Dict[str, BoxBoundary],
cons_mat: pd.DataFrame,
backbone: np.ndarray):
pyFinAssert(len(bounds) == cons_mat.shape[1], "Number of bounds should be same as number of col of cons_mat")
pyFinAssert(cons_mat.shape[0] == len(backbone),
"length of back bond should be same as number of rows of cons_mat")
self.names = list(bounds.keys())
self.bounds = bounds
self.cons_mat = cons_mat
self.backbone = backbone
def risk_targets(self) -> Tuple[np.ndarray, np.ndarray]:
lower_bounds = []
upper_bounds = []
for name in self.names:
center = self.backbone @ self.cons_mat[name].values
l, u = self.bounds[name].bounds(center)
lower_bounds.append(l)
upper_bounds.append(u)
return np.array(lower_bounds), np.array(upper_bounds)
def risk_exp(self) -> np.ndarray:
return self.cons_mat[self.names].values
@deprecated(reason="Constraints is deprecated in alpha-mind 0.1.1. Please use LinearConstraints instead.")
class Constraints(object):
def __init__(self,
risk_exp: Optional[np.ndarray]=None,
risk_names: Optional[np.ndarray]=None):
risk_exp: Optional[np.ndarray] = None,
risk_names: Optional[np.ndarray] = None):
self.risk_exp = risk_exp
if risk_names is not None:
......@@ -68,4 +184,4 @@ if __name__ == '__main__':
cons = Constraints(risk_exp, risk_names)
cons.set_constraints('b', 0.0, 0.1)
print(cons.risk_targets())
\ No newline at end of file
print(cons.risk_targets())
......@@ -7,7 +7,14 @@ Created on 2017-7-20
import unittest
import numpy as np
import pandas as pd
from alphamind.portfolio.constraints import Constraints
from alphamind.portfolio.constraints import BoxBoundary
from alphamind.portfolio.constraints import BoundaryImpl
from alphamind.portfolio.constraints import BoundaryDirection
from alphamind.portfolio.constraints import BoundaryType
from alphamind.portfolio.constraints import create_box_bounds
from alphamind.portfolio.constraints import LinearConstraints
class TestConstraints(unittest.TestCase):
......@@ -43,6 +50,100 @@ class TestConstraints(unittest.TestCase):
np.testing.assert_array_almost_equal(risk_targets[0], np.array([-0.1, -np.inf, -0.1]))
np.testing.assert_array_almost_equal(risk_targets[1], np.array([0.1, np.inf, 0.1]))
def test_absolute_box_boundary(self):
lower = BoundaryImpl(BoundaryDirection.LOWER,
BoundaryType.ABSOLUTE,
-0.8)
upper = BoundaryImpl(BoundaryDirection.UPPER,
BoundaryType.ABSOLUTE,
1.1)
bound = BoxBoundary(lower, upper)
center = 2.2
l, u = bound.bounds(center)
self.assertAlmostEqual(l, 1.4)
self.assertAlmostEqual(u, 3.3)
def test_relative_box_boundary(self):
lower = BoundaryImpl(BoundaryDirection.LOWER,
BoundaryType.RELATIVE,
0.8)
upper = BoundaryImpl(BoundaryDirection.UPPER,
BoundaryType.RELATIVE,
1.1)
bound = BoxBoundary(lower, upper)
center = 2.2
l, u = bound.bounds(center)
self.assertAlmostEqual(l, 1.76)
self.assertAlmostEqual(u, 2.42)
def test_create_box_bounds_single_value(self):
names = ['a', 'b', 'c']
b_type = BoundaryType.RELATIVE
l_val = 0.8
u_val = 1.1
bounds = create_box_bounds(names,
b_type,
l_val,
u_val)
for key, bound in bounds.items():
l_bound = bound.lower
u_bound = bound.upper
self.assertEqual(l_bound.b_type, b_type)
self.assertEqual(u_bound.b_type, b_type)
self.assertAlmostEqual(l_bound.val, l_val)
self.assertAlmostEqual(u_bound.val, u_val)
def test_create_box_bounds_multiple_values(self):
names = ['a', 'b', 'c']
b_type = BoundaryType.RELATIVE
l_val = [0.9, 0.8, 1.1]
u_val = [1.1, 1.2, 1.3]
bounds = create_box_bounds(names,
b_type,
l_val,
u_val)
for i, name in enumerate(names):
bound = bounds[name]
l_bound = bound.lower
u_bound = bound.upper
self.assertEqual(l_bound.b_type, b_type)
self.assertEqual(u_bound.b_type, b_type)
self.assertAlmostEqual(l_bound.val, l_val[i])
self.assertAlmostEqual(u_bound.val, u_val[i])
def test_linear_constraints(self):
cons_mat = np.random.randn(100, 3)
backbone = np.random.randn(100)
names = ['a', 'b', 'c']
cons_mat = pd.DataFrame(cons_mat, columns=names)
b_type = BoundaryType.ABSOLUTE
l_val = -0.8
u_val = 1.1
bounds = create_box_bounds(names,
b_type,
l_val,
u_val)
constraints = LinearConstraints(bounds=bounds,
cons_mat=cons_mat,
backbone=backbone)
l_bounds, u_bounds = constraints.risk_targets()
risk_exp = constraints.risk_exp()
for i, name in enumerate(names):
center = risk_exp[:, i] @ backbone
self.assertAlmostEqual(center + l_val, l_bounds[i])
self.assertAlmostEqual(center + u_val, u_bounds[i])
if __name__ == '__main__':
unittest.main()
arrow >= 0.10.0
cython >= 0.25.2
deprecated >= 1.1.0
numpy >= 1.12.1
pandas >= 0.19.2
scikit-learn >= 0.18.1
......
......@@ -14,7 +14,7 @@ from Cython.Build import cythonize
from distutils.extension import Extension
import numpy as np
VERSION = "0.1.0"
VERSION = "0.1.1"
if platform.system() != "Windows":
import multiprocessing
......
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