Skip to content

Commit

Permalink
ENH: Adds Security cython class in new package 'zipline.assets'
Browse files Browse the repository at this point in the history
The class is not yet used. Adding this class is part of the effort to allow Zipline
simulation of more types of assets than stocks.

DEV: Adds build_ext to .travis.yml
  • Loading branch information
jfkirk committed Mar 4, 2015
1 parent d192b18 commit 0375b35
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 3 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ install:
- grep nose-ignore-docstring== etc/requirements_dev.txt | xargs pip install
- pip install coveralls
- pip install nose-timer
- python setup.py build_ext --inplace
before_script:
- "flake8 zipline tests"
script:
Expand Down
18 changes: 15 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from setuptools import setup, find_packages
from setuptools import setup, find_packages, Extension
from Cython.Build import cythonize
import numpy as np

ext_modules = [
Extension(
'zipline.assets._securities',
['zipline/assets/_securities.pyx'],
include_dirs=[np.get_include()],
),
]

setup(
name='zipline',
Expand All @@ -23,6 +33,7 @@
author='Quantopian Inc.',
author_email='[email protected]',
packages=find_packages(),
ext_modules=cythonize(ext_modules),
scripts=['scripts/run_algo.py'],
include_package_data=True,
license='Apache 2.0',
Expand All @@ -45,9 +56,10 @@
'requests',
'numpy',
'pandas',
'six'
'six',
'Cython==0.20.1'
],
extras_require = {
extras_require={
'talib': ["talib"],
},
url="http://zipline.io"
Expand Down
49 changes: 49 additions & 0 deletions tests/test_assets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#
# Copyright 2015 Quantopian, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Tests for the zipline.assets package
"""

from unittest import TestCase

from zipline.assets._securities import Security


class SecurityTestCase(TestCase):

def test_security_object(self):
self.assertEquals({5061: 'foo'}[Security(5061)], 'foo')
self.assertEquals(Security(5061), 5061)
self.assertEquals(5061, Security(5061))

self.assertEquals(Security(5061), Security(5061))
self.assertEquals(int(Security(5061)), 5061)

self.assertEquals(str(Security(5061)), 'Security(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)

# with self.assertRaises(TypeError):
# print float(Security(5061))

# with self.assertRaises(TypeError):
# print float(Security(5061))
14 changes: 14 additions & 0 deletions zipline/assets/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#
# Copyright 2015 Quantopian, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
174 changes: 174 additions & 0 deletions zipline/assets/_securities.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#
# Copyright 2015 Quantopian, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Cythonized Security object.
"""
cimport cython

import numpy as np
cimport numpy as np


cdef class Security:

cdef readonly int sid
# Cached hash of self.sid
cdef int sid_hash

cdef readonly object symbol
cdef readonly object security_name

# TODO: Maybe declare as pandas Timestamp?
cdef readonly object start_date
cdef readonly object end_date
cdef public object first_traded

cdef readonly object exchange

def __cinit__(self,
int sid, # sid is required
object symbol="",
object security_name="",
object start_date=None,
object end_date=None,
object first_traded=None,
object exchange=""):

self.sid = sid
self.sid_hash = hash(sid)
self.symbol = symbol
self.security_name = security_name
self.exchange = exchange
self.start_date = start_date
self.end_date = end_date
self.first_traded = first_traded

def __int__(self):
return self.sid

def __hash__(self):
return self.sid_hash

property security_start_date:
"""
Alias for start_date to disambiguate from other `start_date`s in the
system.
"""
def __get__(self):
return self.start_date

property security_end_date:
"""
Alias for end_date to disambiguate from other `end_date`s in the
system.
"""
def __get__(self):
return self.end_date

def __richcmp__(self, other, int op):
"""
Cython rich comparison method. This is used in place of various
equality checkers in pure python.
< 0
<= 1
== 2
!= 3
> 4
>= 5
"""
cdef int other_as_int
if isinstance(other, Security):
other_as_int = other.sid
elif isinstance(other, int):
other_as_int = other
else:
retvals = [True, True, False, True, False, False]
return retvals[op]

compared = cmp(self.sid, other_as_int)

# Handle == and != first because they're significantly more common
# operations.
if op == 2:
# Equality
return compared == 0
elif op == 3:
# Non-equality
return compared != 0
elif op == 0:
# <
return compared == -1
elif op == 1:
# <=
return compared == -1 or compared == 0
elif op == 4:
# >
return compared == 1
elif op == 5:
# >=
return compared == -1 or compared == 0

def __str__(self):
if self.symbol:
return 'Security(%d [%s])' % (self.sid, self.symbol)
else:
return 'Security(%d)' % self.sid

def __repr__(self):
attrs = ('symbol', 'security_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 'Security(%d, %s)' % (self.sid, params)

cpdef __reduce__(self):
"""
Function used by pickle to determine how to serialize/deserialize this
class. Should return a tuple whose first element is self.__class__,
and whose second element is a tuple of all the attributes that should
be serialized/deserialized during pickling.
"""
return (self.__class__, (self.sid,
self.symbol,
self.security_name,
self.start_date,
self.end_date,
self.first_traded,
self.exchange,))

cpdef to_dict(self):
"""
Convert to a python dict.
"""
return {
'sid': self.sid,
'symbol': self.symbol,
'security_name': self.security_name,
'start_date': self.start_date,
'end_date': self.end_date,
'first_traded': self.first_traded,
'exchange': self.exchange,
}

@staticmethod
def from_dict(dict_):
"""
Build a Security instance from a dict.
"""
return Security(**dict_)

0 comments on commit 0375b35

Please sign in to comment.