Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trying to build ta-lib from source if the library is not found #110

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ build
*.so
.*
*~

*.egg-info/
15 changes: 3 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,6 @@ $ python setup.py install

### Troubleshooting

Sometimes installation will produce build errors like this:

```
func.c:256:28: fatal error: ta-lib/ta_libc.h: No such file or directory
compilation terminated.
```

This typically means that it can't find the underlying ``TA-Lib`` library,
a dependency which needs to be installed.

Sometimes installation will fail with errors like this:

```
Expand All @@ -61,8 +51,9 @@ $ sudo apt-get install python3-dev

### Dependencies

To use TA-Lib for python, you need to have the
[TA-Lib](http://ta-lib.org/hdr_dw.html) already installed:
To use TA-Lib for python, you'd better have the
[TA-Lib](http://ta-lib.org/hdr_dw.html) already installed.
If not so, it'll be built from source.

##### Mac OS X

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
numpy
93 changes: 73 additions & 20 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
#!/usr/bin/env python

import sys
import os
import warnings
import sys
import glob
from os.path import join as join_path

from distutils.dist import Distribution
from setuptools.command.test import test as TestCommand


class PyTest(TestCommand):
user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]

def initialize_options(self):
TestCommand.initialize_options(self)
self.pytest_args = ['tests']

def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True

def run_tests(self):
# import here, cause outside the eggs aren't loaded
import pytest
errno = pytest.main(self.pytest_args)
sys.exit(errno)


PRJ_DIR = os.path.dirname(os.path.abspath(__file__))
display_option_names = Distribution.display_option_names + ['help', 'help-commands']
query_only = any('--' + opt in sys.argv for opt in display_option_names) or len(sys.argv) < 2 or sys.argv[1] == 'egg_info'

Expand Down Expand Up @@ -66,6 +89,10 @@
except ImportError:
has_cython = False

vendor_include_dir = join_path(PRJ_DIR, "vendor", "include")
vendor_talib_dir = join_path(PRJ_DIR, "vendor", "ta-lib")

libraries = [lib_talib_name]
for lib_talib_dir in lib_talib_dirs:
try:
files = os.listdir(lib_talib_dir)
Expand All @@ -74,32 +101,58 @@
except OSError:
pass
else:
warnings.warn('Cannot find ta-lib library, installation may fail.')
include_dirs.append(vendor_include_dir)
include_dirs.append(join_path(vendor_talib_dir, "include"))
libraries = []


cmdclass = {}
cmdclass = {"test": PyTest}
if has_cython:
cmdclass['build_ext'] = build_ext

ext_modules = []
for name in ['common', 'func', 'abstract', 'stream']:
local_include_dirs = include_dirs[:]

sources = [('talib/%s.pyx' if has_cython else 'talib/%s.c') % name]
if not libraries:
ta_common_dir = join_path(vendor_talib_dir, "src", "ta_common")
ta_abstract_dir = join_path(vendor_talib_dir, "src", "ta_abstract")
ta_func_dir = join_path(vendor_talib_dir, "src", "ta_func")
if name == "common":
local_include_dirs.append(ta_common_dir)
sources.extend(glob.glob(join_path(ta_common_dir, "*.c")))
sources.extend(glob.glob(join_path(ta_func_dir, "*.c")))
elif name == "abstract":
local_include_dirs.append(ta_common_dir)
local_include_dirs.append(ta_abstract_dir)
local_include_dirs.append(join_path(ta_abstract_dir, "frames"))
sources.extend(glob.glob(join_path(ta_abstract_dir, "*.c")))
sources.remove(join_path(ta_abstract_dir, "excel_glue.c"))
sources.extend(glob.glob(join_path(ta_abstract_dir, "*", "*.c")))
elif name == "func":
local_include_dirs.append(ta_common_dir)
sources.extend(glob.glob(join_path(ta_func_dir, "*.c")))

ext = Extension(
'talib.%s' % name,
[('talib/%s.pyx' if has_cython else 'talib/%s.c') % name],
include_dirs = include_dirs,
library_dirs = lib_talib_dirs,
libraries = [lib_talib_name]
sources,
include_dirs=local_include_dirs,
library_dirs=lib_talib_dirs,
libraries=libraries,
)
ext_modules.append(ext)


setup(
name = 'TA-Lib',
version = '0.4.10',
description = 'Python wrapper for TA-Lib',
author = 'John Benediktsson',
author_email = '[email protected]',
url = 'http://github.com/mrjbq7/ta-lib',
download_url = 'https://github.com/mrjbq7/ta-lib/releases',
classifiers = [
name='TA-Lib',
version='0.4.10',
description='Python wrapper for TA-Lib',
author='John Benediktsson',
author_email='[email protected]',
url='http://github.com/mrjbq7/ta-lib',
download_url='https://github.com/mrjbq7/ta-lib/releases',
classifiers=[
"License :: OSI Approved :: BSD License",
"Development Status :: 4 - Beta",
"Operating System :: Unix",
Expand All @@ -116,8 +169,8 @@
"Intended Audience :: Science/Research",
"Intended Audience :: Financial and Insurance Industry",
],
packages = ['talib'],
ext_modules = ext_modules,
cmdclass = cmdclass,
requires = ['numpy'],
packages=['talib'],
ext_modules=ext_modules,
cmdclass=cmdclass,
requires=['numpy'],
)
69 changes: 33 additions & 36 deletions talib/test_abstract.py → tests/test_abstract.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import numpy as np
from nose.tools import (
assert_equals,
assert_true,
assert_false,
assert_raises,
)
import pytest

try:
from collections import OrderedDict
Expand All @@ -14,7 +9,7 @@
import talib
from talib import func
from talib import abstract
from talib.test_data import ford_2012, assert_np_arrays_equal, assert_np_arrays_not_equal
from test_data import ford_2012, assert_np_arrays_equal, assert_np_arrays_not_equal


def test_pandas():
Expand All @@ -24,20 +19,20 @@ def test_pandas():

expected_k, expected_d = func.STOCH(ford_2012['high'], ford_2012['low'], ford_2012['close']) # 5, 3, 0, 3, 0
output = abstract.Function('stoch', input_df).outputs
assert_true(isinstance(output, pandas.DataFrame))
assert isinstance(output, pandas.DataFrame)
assert_np_arrays_equal(expected_k, output['slowk'])
assert_np_arrays_equal(expected_d, output['slowd'])
output = abstract.Function('stoch', input_dict).outputs
assert_true(isinstance(output, list))
assert isinstance(output, list)
assert_np_arrays_equal(expected_k, output[0])
assert_np_arrays_equal(expected_d, output[1])

expected = func.SMA(ford_2012['close'], 10)
output = abstract.Function('sma', input_df, 10).outputs
assert_true(isinstance(output, pandas.Series))
assert isinstance(output, pandas.Series)
assert_np_arrays_equal(expected, output)
output = abstract.Function('sma', input_dict, 10).outputs
assert_true(isinstance(output, np.ndarray))
assert isinstance(output, np.ndarray)
assert_np_arrays_equal(expected, output)


Expand Down Expand Up @@ -86,12 +81,13 @@ def test_doji_candle():

def test_MAVP():
mavp = abstract.MAVP
assert_raises(Exception, mavp.set_input_arrays, ford_2012)
with pytest.raises(Exception):
mavp.set_input_arrays(ford_2012)
input_d = {}
input_d['close'] = ford_2012['close']
input_d['periods'] = np.arange(30)
assert_true(mavp.set_input_arrays(input_d))
assert_equals(mavp.input_arrays, input_d)
assert mavp.set_input_arrays(input_d)
assert mavp.input_arrays == input_d

def test_info():
stochrsi = abstract.Function('STOCHRSI')
Expand All @@ -115,7 +111,7 @@ def test_info():
('fastd_matype', 1),
]),
}
assert_equals(expected, stochrsi.info)
assert expected == stochrsi.info

expected = {
'display_name': 'Bollinger Bands',
Expand All @@ -136,11 +132,11 @@ def test_info():
('matype', 0),
]),
}
assert_equals(expected, abstract.Function('BBANDS').info)
assert expected == abstract.Function('BBANDS').info

def test_input_names():
expected = OrderedDict([('price', 'close')])
assert_equals(expected, abstract.Function('MAMA').input_names)
assert expected == abstract.Function('MAMA').input_names

# test setting input_names
obv = abstract.Function('OBV')
Expand All @@ -149,13 +145,13 @@ def test_input_names():
('prices', ['volume']),
])
obv.input_names = expected
assert_equals(obv.input_names, expected)
assert obv.input_names == expected

obv.input_names = {
'price': 'open',
'prices': ['volume'],
}
assert_equals(obv.input_names, expected)
assert obv.input_names == expected

def test_input_arrays():
mama = abstract.Function('MAMA')
Expand All @@ -167,33 +163,34 @@ def test_input_arrays():
'close': None,
'volume': None,
}
assert_equals(expected, mama.get_input_arrays())
assert expected == mama.get_input_arrays()
# test setting/getting input_arrays
assert_true(mama.set_input_arrays(ford_2012))
assert_equals(mama.get_input_arrays(), ford_2012)
assert_raises(Exception,
mama.set_input_arrays, {'hello': 'fail', 'world': 'bye'})
assert mama.set_input_arrays(ford_2012)
assert mama.get_input_arrays() == ford_2012
with pytest.raises(Exception):
mama.set_input_arrays({'hello': 'fail', 'world': 'bye'})

# test only required keys are needed
willr = abstract.Function('WILLR')
reqd = willr.input_names['prices']
input_d = dict([(key, ford_2012[key]) for key in reqd])
assert_true(willr.set_input_arrays(input_d))
assert_equals(willr.input_arrays, input_d)
assert willr.set_input_arrays(input_d)
assert willr.input_arrays == input_d

# test extraneous keys are ignored
input_d['extra_stuffs'] = 'you should never see me'
input_d['date'] = np.random.rand(100)
assert_true(willr.set_input_arrays(input_d))
assert willr.set_input_arrays(input_d)

# test missing keys get detected
input_d['open'] = ford_2012['open']
input_d.pop('close')
assert_raises(Exception, willr.set_input_arrays, input_d)
with pytest.raises(Exception):
willr.set_input_arrays(input_d)

# test changing input_names on the Function
willr.input_names = {'prices': ['high', 'low', 'open']}
assert_true(willr.set_input_arrays(input_d))
assert willr.set_input_arrays(input_d)

def test_parameters():
stoch = abstract.Function('STOCH')
Expand All @@ -204,31 +201,31 @@ def test_parameters():
('slowd_period', 3),
('slowd_matype', 0),
])
assert_equals(expected, stoch.parameters)
assert expected == stoch.parameters

stoch.parameters = {'fastk_period': 10}
expected['fastk_period'] = 10
assert_equals(expected, stoch.parameters)
assert expected == stoch.parameters

stoch.parameters = {'slowk_period': 8, 'slowd_period': 5}
expected['slowk_period'] = 8
expected['slowd_period'] = 5
assert_equals(expected, stoch.parameters)
assert expected == stoch.parameters

stoch.parameters = {'slowd_matype': talib.MA_Type.T3}
expected['slowd_matype'] = 8
assert_equals(expected, stoch.parameters)
assert expected == stoch.parameters

stoch.parameters = {
'slowk_matype': talib.MA_Type.WMA,
'slowd_matype': talib.MA_Type.EMA,
}
expected['slowk_matype'] = 2
expected['slowd_matype'] = 1
assert_equals(expected, stoch.parameters)
assert expected == stoch.parameters

def test_lookback():
assert_equals(abstract.Function('SMA', 10).lookback, 9)
assert abstract.Function('SMA', 10).lookback == 9

stochrsi = abstract.Function('stochrsi', 20, 5, 3)
assert_equals(stochrsi.lookback, 26)
assert stochrsi.lookback == 26
9 changes: 4 additions & 5 deletions talib/test_data.py → tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import numpy as np

from nose.tools import assert_equal, assert_not_equal, assert_true

ford_2012_dates = np.asarray([ 20120103, 20120104, 20120105, 20120106, 20120109,
20120110, 20120111, 20120112, 20120113, 20120117, 20120118, 20120119,
Expand Down Expand Up @@ -217,9 +216,9 @@
def assert_np_arrays_equal(expected, got):
for i, value in enumerate(expected):
if np.isnan(value):
assert_true(np.isnan(got[i]))
assert np.isnan(got[i])
else:
assert_equal(value, got[i])
assert value == got[i]

def assert_np_arrays_not_equal(expected, got):
''' Verifies expected and got have the same number of leading nan fields,
Expand All @@ -229,11 +228,11 @@ def assert_np_arrays_not_equal(expected, got):
equals = []
for i, value in enumerate(expected):
if np.isnan(value):
assert_true(np.isnan(got[i]))
assert np.isnan(got[i])
nans.append(value)
else:
try:
assert_not_equal(value, got[i])
assert value != got[i]
except AssertionError:
equals.append(got[i])
if len(equals) == len(expected[len(nans):]):
Expand Down
Loading