Commit 2360f393 authored by Dr.李's avatar Dr.李

added long short builder and its corresponding tests

parent 27436224
# -*- coding: utf-8 -*-
"""
Created on 2017-5-9
@author: cheng.li
"""
import numpy as np
from alphamind.utilities import group_mapping
from alphamind.utilities import simple_abssum
from alphamind.utilities import transform
def long_short_build(er: np.ndarray,
leverage: float=1.,
groups: np.ndarray=None) -> np.ndarray:
if er.ndim == 1:
er = er.reshape((-1, 1))
if groups is None:
return er / simple_abssum(er, axis=0) * leverage
else:
groups = group_mapping(groups)
return transform(groups, er, 'scale', scale=leverage)
...@@ -21,6 +21,8 @@ def simple_settle(weights: np.ndarray, ret_series: np.ndarray, groups: np.ndarra ...@@ -21,6 +21,8 @@ def simple_settle(weights: np.ndarray, ret_series: np.ndarray, groups: np.ndarra
groups = group_mapping(groups) groups = group_mapping(groups)
return aggregate(groups, ret_mat, 'sum') return aggregate(groups, ret_mat, 'sum')
else: else:
if ret_mat.ndim == 1:
ret_mat = ret_mat.reshape((-1, 1))
return simple_sum(ret_mat, axis=0) return simple_sum(ret_mat, axis=0)
......
# -*- coding: utf-8 -*-
"""
Created on 2017-5-9
@author: cheng.li
"""
import unittest
import numpy as np
import pandas as pd
from alphamind.portfolio.longshortbulder import long_short_build
class TestLongShortBuild(unittest.TestCase):
def test_long_short_build(self):
x = np.random.randn(3000)
calc_weights = long_short_build(x).flatten()
expected_weights = x / np.abs(x).sum()
np.testing.assert_array_almost_equal(calc_weights, expected_weights)
x = np.random.randn(3000, 10)
calc_weights = long_short_build(x, leverage=2)
expected_weights = x / np.abs(x).sum(axis=0) * 2
np.testing.assert_array_almost_equal(calc_weights, expected_weights)
def test_long_short_build_with_group(self):
x = np.random.randn(3000)
groups = np.random.randint(10, 40, size=3000)
calc_weights = long_short_build(x, groups=groups).flatten()
expected_weights = pd.Series(x).groupby(groups).apply(lambda s: s / np.abs(s).sum())
np.testing.assert_array_almost_equal(calc_weights, expected_weights)
x = np.random.randn(3000, 10)
groups = np.random.randint(10, 40, size=3000)
calc_weights = long_short_build(x, groups=groups)
expected_weights = pd.DataFrame(x).groupby(groups).apply(lambda s: s / np.abs(s).sum(axis=0))
np.testing.assert_array_almost_equal(calc_weights, expected_weights)
if __name__ == '__main__':
unittest.main()
...@@ -14,6 +14,7 @@ from alphamind.utilities import alpha_logger ...@@ -14,6 +14,7 @@ from alphamind.utilities import alpha_logger
from alphamind.tests.data.test_neutralize import TestNeutralize from alphamind.tests.data.test_neutralize import TestNeutralize
from alphamind.tests.data.test_standardize import TestStandardize from alphamind.tests.data.test_standardize import TestStandardize
from alphamind.tests.data.test_winsorize import TestWinsorize from alphamind.tests.data.test_winsorize import TestWinsorize
from alphamind.tests.portfolio.test_longshortbuild import TestLongShortBuild
from alphamind.tests.portfolio.test_rankbuild import TestRankBuild from alphamind.tests.portfolio.test_rankbuild import TestRankBuild
from alphamind.tests.portfolio.test_percentbuild import TestPercentBuild from alphamind.tests.portfolio.test_percentbuild import TestPercentBuild
from alphamind.tests.portfolio.test_linearbuild import TestLinearBuild from alphamind.tests.portfolio.test_linearbuild import TestLinearBuild
...@@ -25,6 +26,7 @@ if __name__ == '__main__': ...@@ -25,6 +26,7 @@ if __name__ == '__main__':
runner = TestRunner([TestNeutralize, runner = TestRunner([TestNeutralize,
TestStandardize, TestStandardize,
TestWinsorize, TestWinsorize,
TestLongShortBuild,
TestRankBuild, TestRankBuild,
TestPercentBuild, TestPercentBuild,
TestLinearBuild, TestLinearBuild,
......
...@@ -63,6 +63,25 @@ def simple_sum(x, axis=0): ...@@ -63,6 +63,25 @@ def simple_sum(x, axis=0):
return res return res
@nb.njit(nogil=True, cache=True)
def simple_abssum(x, axis=0):
length, width = x.shape
if axis == 0:
res = np.zeros(width)
for i in range(length):
for j in range(width):
res[j] += abs(x[i, j])
elif axis == 1:
res = np.zeros(length)
for i in range(length):
for j in range(width):
res[i] += abs(x[i, j])
return res
@nb.njit(nogil=True, cache=True) @nb.njit(nogil=True, cache=True)
def simple_mean(x, axis=0): def simple_mean(x, axis=0):
length, width = x.shape length, width = x.shape
...@@ -182,7 +201,22 @@ def copy_value(groups, source): ...@@ -182,7 +201,22 @@ def copy_value(groups, source):
return destination return destination
def transform(groups, x, func, ddof=1): @nb.njit(nogil=True, cache=True)
def scale_value(groups, source, x, scale):
length, width = x.shape
destination = x.copy()
for i in range(length):
k = groups[i]
for j in range(width):
destination[i, j] /= source[k, j] / scale
return destination
def transform(groups: np.ndarray,
x: np.ndarray,
func: str,
ddof: int=1,
scale: float=1.) -> np.ndarray:
if func == 'mean': if func == 'mean':
value_data = agg_mean(groups, x) value_data = agg_mean(groups, x)
...@@ -190,12 +224,15 @@ def transform(groups, x, func, ddof=1): ...@@ -190,12 +224,15 @@ def transform(groups, x, func, ddof=1):
value_data = agg_std(groups, x, ddof=ddof) value_data = agg_std(groups, x, ddof=ddof)
elif func == 'sum': elif func == 'sum':
value_data = agg_sum(groups, x) value_data = agg_sum(groups, x)
elif func =='abssum': elif func == 'abssum' or func == 'scale':
value_data = agg_abssum(groups, x) value_data = agg_abssum(groups, x)
else: else:
raise ValueError('({0}) is not recognized as valid functor'.format(func)) raise ValueError('({0}) is not recognized as valid functor'.format(func))
return copy_value(groups, value_data) if func == 'scale':
return scale_value(groups, value_data, x, scale)
else:
return copy_value(groups, value_data)
def aggregate(groups, x, func, ddof=1): def aggregate(groups, x, func, ddof=1):
...@@ -205,7 +242,7 @@ def aggregate(groups, x, func, ddof=1): ...@@ -205,7 +242,7 @@ def aggregate(groups, x, func, ddof=1):
value_data = agg_std(groups, x, ddof=ddof) value_data = agg_std(groups, x, ddof=ddof)
elif func == 'sum': elif func == 'sum':
value_data = agg_sum(groups, x) value_data = agg_sum(groups, x)
elif func =='abssum': elif func == 'abssum' or func == 'scale':
value_data = agg_abssum(groups, x) value_data = agg_abssum(groups, x)
else: else:
raise ValueError('({0}) is not recognized as valid functor'.format(func)) raise ValueError('({0}) is not recognized as valid functor'.format(func))
......
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