From 2fd2f02c2a02d2752846b73774f463f89b581735 Mon Sep 17 00:00:00 2001 From: jfkirk Date: Mon, 16 Mar 2015 13:34:42 -0400 Subject: [PATCH 1/2] MAINT: Refactors Security to Asset This commit refactors the Security cython class to Asset, and refactors some fields of the class accordingly. This change is so the terminology is consistent and correct when Asset is extended to asset types that are not securities, such as futures. --- setup.py | 4 +- tests/test_assets.py | 63 +++++++++++++------ tests/test_security_object.py | 40 ------------ .../assets/{_securities.pyx => _assets.pyx} | 32 +++++----- 4 files changed, 62 insertions(+), 77 deletions(-) delete mode 100644 tests/test_security_object.py rename zipline/assets/{_securities.pyx => _assets.pyx} (87%) diff --git a/setup.py b/setup.py index acf1c5c4..826bfd7d 100644 --- a/setup.py +++ b/setup.py @@ -20,8 +20,8 @@ import numpy as np ext_modules = [ Extension( - 'zipline.assets._securities', - ['zipline/assets/_securities.pyx'], + 'zipline.assets._assets', + ['zipline/assets/_assets.pyx'], include_dirs=[np.get_include()], ), ] diff --git a/tests/test_assets.py b/tests/test_assets.py index 7c856d93..16205ede 100644 --- a/tests/test_assets.py +++ b/tests/test_assets.py @@ -17,33 +17,58 @@ Tests for the zipline.assets package """ +import sys from unittest import TestCase -from zipline.assets._securities import Security +from zipline.assets._assets import Asset -class SecurityTestCase(TestCase): +class AssetTestCase(TestCase): - def test_security_object(self): - self.assertEquals({5061: 'foo'}[Security(5061)], 'foo') - self.assertEquals(Security(5061), 5061) - self.assertEquals(5061, Security(5061)) + def test_asset_object(self): + self.assertEquals({5061: 'foo'}[Asset(5061)], 'foo') + self.assertEquals(Asset(5061), 5061) + self.assertEquals(5061, Asset(5061)) - self.assertEquals(Security(5061), Security(5061)) - self.assertEquals(int(Security(5061)), 5061) + self.assertEquals(Asset(5061), Asset(5061)) + self.assertEquals(int(Asset(5061)), 5061) - self.assertEquals(str(Security(5061)), 'Security(5061)') + self.assertEquals(str(Asset(5061)), 'Asset(5061)') - # TODO: we can't provide this property while subclassing - # int. Need to subclass object to fix all the following - # cases. - # assert Security(5061) != 5061.0 - # with self.assertRaises(TypeError): - # assert Security(5061) + Security(5061) +class TestSecurityRichCmp(TestCase): - # with self.assertRaises(TypeError): - # print float(Security(5061)) + def test_lt(self): + self.assertTrue(Asset(3) < Asset(4)) + self.assertFalse(Asset(4) < Asset(4)) + self.assertFalse(Asset(5) < Asset(4)) - # with self.assertRaises(TypeError): - # print float(Security(5061)) + def test_le(self): + self.assertTrue(Asset(3) <= Asset(4)) + self.assertTrue(Asset(4) <= Asset(4)) + self.assertFalse(Asset(5) <= Asset(4)) + + def test_eq(self): + self.assertFalse(Asset(3) == Asset(4)) + self.assertTrue(Asset(4) == Asset(4)) + self.assertFalse(Asset(5) == Asset(4)) + + def test_ge(self): + self.assertFalse(Asset(3) >= Asset(4)) + self.assertTrue(Asset(4) >= Asset(4)) + self.assertTrue(Asset(5) >= Asset(4)) + + def test_gt(self): + self.assertFalse(Asset(3) > Asset(4)) + self.assertFalse(Asset(4) > Asset(4)) + self.assertTrue(Asset(5) > Asset(4)) + + def test_type_mismatch(self): + if sys.version_info.major < 3: + self.assertIsNotNone(Asset(3) < 'a') + self.assertIsNotNone('a' < Asset(3)) + else: + with self.assertRaises(TypeError): + Asset(3) < 'a' + with self.assertRaises(TypeError): + 'a' < Asset(3) diff --git a/tests/test_security_object.py b/tests/test_security_object.py deleted file mode 100644 index 528a8bc3..00000000 --- a/tests/test_security_object.py +++ /dev/null @@ -1,40 +0,0 @@ -import sys -from unittest import TestCase -from zipline.assets._securities import Security - - -class TestSecurityRichCmp(TestCase): - def test_lt(self): - self.assertTrue(Security(3) < Security(4)) - self.assertFalse(Security(4) < Security(4)) - self.assertFalse(Security(5) < Security(4)) - - def test_le(self): - self.assertTrue(Security(3) <= Security(4)) - self.assertTrue(Security(4) <= Security(4)) - self.assertFalse(Security(5) <= Security(4)) - - def test_eq(self): - self.assertFalse(Security(3) == Security(4)) - self.assertTrue(Security(4) == Security(4)) - self.assertFalse(Security(5) == Security(4)) - - def test_ge(self): - self.assertFalse(Security(3) >= Security(4)) - self.assertTrue(Security(4) >= Security(4)) - self.assertTrue(Security(5) >= Security(4)) - - def test_gt(self): - self.assertFalse(Security(3) > Security(4)) - self.assertFalse(Security(4) > Security(4)) - self.assertTrue(Security(5) > Security(4)) - - def test_type_mismatch(self): - if sys.version_info.major < 3: - self.assertIsNotNone(Security(3) < 'a') - self.assertIsNotNone('a' < Security(3)) - else: - with self.assertRaises(TypeError): - Security(3) < 'a' - with self.assertRaises(TypeError): - 'a' < Security(3) diff --git a/zipline/assets/_securities.pyx b/zipline/assets/_assets.pyx similarity index 87% rename from zipline/assets/_securities.pyx rename to zipline/assets/_assets.pyx index 30f94401..5a8bb4ae 100644 --- a/zipline/assets/_securities.pyx +++ b/zipline/assets/_assets.pyx @@ -14,7 +14,7 @@ # limitations under the License. """ -Cythonized Security object. +Cythonized Asset object. """ cimport cython @@ -22,14 +22,14 @@ import numpy as np cimport numpy as np -cdef class Security: +cdef class Asset: cdef readonly int sid # Cached hash of self.sid cdef int sid_hash cdef readonly object symbol - cdef readonly object security_name + cdef readonly object asset_name # TODO: Maybe declare as pandas Timestamp? cdef readonly object start_date @@ -41,7 +41,7 @@ cdef class Security: def __cinit__(self, int sid, # sid is required object symbol="", - object security_name="", + object asset_name="", object start_date=None, object end_date=None, object first_traded=None, @@ -50,7 +50,7 @@ cdef class Security: self.sid = sid self.sid_hash = hash(sid) self.symbol = symbol - self.security_name = security_name + self.asset_name = asset_name self.exchange = exchange self.start_date = start_date self.end_date = end_date @@ -62,7 +62,7 @@ cdef class Security: def __hash__(self): return self.sid_hash - property security_start_date: + property asset_start_date: """ Alias for start_date to disambiguate from other `start_date`s in the system. @@ -70,7 +70,7 @@ cdef class Security: def __get__(self): return self.start_date - property security_end_date: + property asset_end_date: """ Alias for end_date to disambiguate from other `end_date`s in the system. @@ -92,14 +92,14 @@ cdef class Security: """ cdef int x_as_int, y_as_int - if isinstance(x, Security): + if isinstance(x, Asset): x_as_int = x.sid elif isinstance(x, int): x_as_int = x else: return NotImplemented - if isinstance(y, Security): + if isinstance(y, Asset): y_as_int = y.sid elif isinstance(y, int): y_as_int = y @@ -131,12 +131,12 @@ cdef class Security: def __str__(self): if self.symbol: - return 'Security(%d [%s])' % (self.sid, self.symbol) + return 'Asset(%d [%s])' % (self.sid, self.symbol) else: - return 'Security(%d)' % self.sid + return 'Asset(%d)' % self.sid def __repr__(self): - attrs = ('symbol', 'security_name', 'exchange', + attrs = ('symbol', 'asset_name', 'exchange', 'start_date', 'end_date', 'first_traded') tuples = ((attr, repr(getattr(self, attr, None))) for attr in attrs) @@ -153,7 +153,7 @@ cdef class Security: """ return (self.__class__, (self.sid, self.symbol, - self.security_name, + self.asset_name, self.start_date, self.end_date, self.first_traded, @@ -166,7 +166,7 @@ cdef class Security: return { 'sid': self.sid, 'symbol': self.symbol, - 'security_name': self.security_name, + 'asset_name': self.asset_name, 'start_date': self.start_date, 'end_date': self.end_date, 'first_traded': self.first_traded, @@ -176,6 +176,6 @@ cdef class Security: @staticmethod def from_dict(dict_): """ - Build a Security instance from a dict. + Build an Asset instance from a dict. """ - return Security(**dict_) + return Asset(**dict_) From 84d4fa3c0897c8f65337805c34b24d17c1d18b87 Mon Sep 17 00:00:00 2001 From: jfkirk Date: Tue, 17 Mar 2015 16:08:53 -0400 Subject: [PATCH 2/2] ENH: Adds Equity and Future classes as extensions of Asset class --- tests/test_assets.py | 19 +++++++++-- zipline/assets/_assets.pyx | 66 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/tests/test_assets.py b/tests/test_assets.py index 16205ede..fea726ab 100644 --- a/tests/test_assets.py +++ b/tests/test_assets.py @@ -20,7 +20,7 @@ Tests for the zipline.assets package import sys from unittest import TestCase -from zipline.assets._assets import Asset +from zipline.assets._assets import Asset, Future class AssetTestCase(TestCase): @@ -36,7 +36,7 @@ class AssetTestCase(TestCase): self.assertEquals(str(Asset(5061)), 'Asset(5061)') -class TestSecurityRichCmp(TestCase): +class TestAssetRichCmp(TestCase): def test_lt(self): self.assertTrue(Asset(3) < Asset(4)) @@ -72,3 +72,18 @@ class TestSecurityRichCmp(TestCase): Asset(3) < 'a' with self.assertRaises(TypeError): 'a' < Asset(3) + + +class testFuture(TestCase): + + def test_repr(self): + + future = Future(2468, + notice_date='2014-01-20', + expiration_date='2014-02-20') + rep = future.__repr__() + + self.assertTrue("Future" in rep) + self.assertTrue("2468" in rep) + self.assertTrue("notice_date='2014-01-20'" in rep) + self.assertTrue("expiration_date='2014-02-20'" in rep) diff --git a/zipline/assets/_assets.pyx b/zipline/assets/_assets.pyx index 5a8bb4ae..16b8ba06 100644 --- a/zipline/assets/_assets.pyx +++ b/zipline/assets/_assets.pyx @@ -21,6 +21,9 @@ cimport cython import numpy as np cimport numpy as np +cdef enum AssetType: + EQUITY = 1 + FUTURE = 2 cdef class Asset: @@ -30,6 +33,7 @@ cdef class Asset: cdef readonly object symbol cdef readonly object asset_name + cdef readonly AssetType asset_type # TODO: Maybe declare as pandas Timestamp? cdef readonly object start_date @@ -45,10 +49,12 @@ cdef class Asset: object start_date=None, object end_date=None, object first_traded=None, - object exchange=""): + object exchange="", + *args, + **kwargs): self.sid = sid - self.sid_hash = hash(sid) + self.sid_hash = hash(sid) self.symbol = symbol self.asset_name = asset_name self.exchange = exchange @@ -142,7 +148,7 @@ cdef class Asset: for attr in attrs) strings = ('%s=%s' % (t[0], t[1]) for t in tuples) params = ', '.join(strings) - return 'Security(%d, %s)' % (self.sid, params) + return 'Asset(%d, %s)' % (self.sid, params) cpdef __reduce__(self): """ @@ -179,3 +185,57 @@ cdef class Asset: Build an Asset instance from a dict. """ return Asset(**dict_) + + +cdef class Equity(Asset): + + def __cinit__(self, + int sid, # sid is required + object symbol="", + object asset_name="", + object start_date=None, + object end_date=None, + object first_traded=None, + object exchange=""): + + self.asset_type = EQUITY + + def __repr__(self): + attrs = ('symbol', 'asset_name', 'exchange', + 'start_date', 'end_date', 'first_traded') + tuples = ((attr, repr(getattr(self, attr, None))) + for attr in attrs) + strings = ('%s=%s' % (t[0], t[1]) for t in tuples) + params = ', '.join(strings) + return 'Equity(%d, %s)' % (self.sid, params) + + +cdef class Future(Asset): + + cdef readonly object notice_date + cdef readonly object expiration_date + + def __cinit__(self, + int sid, # sid is required + object symbol="", + object asset_name="", + object start_date=None, + object end_date=None, + object notice_date=None, + object expiration_date=None, + object first_traded=None, + object exchange=""): + + self.asset_type = FUTURE + self.notice_date = notice_date + self.expiration_date = expiration_date + + def __repr__(self): + attrs = ('symbol', 'asset_name', 'exchange', + 'start_date', 'end_date', 'first_traded', 'notice_date', + 'expiration_date') + tuples = ((attr, repr(getattr(self, attr, None))) + for attr in attrs) + strings = ('%s=%s' % (t[0], t[1]) for t in tuples) + params = ', '.join(strings) + return 'Future(%d, %s)' % (self.sid, params)