MAINT minor refactoring

This commit is contained in:
Kevin Johnson
2021-07-26 14:27:53 -07:00
parent 1d237ad684
commit e68f84017a
+89 -86
View File
@@ -1,24 +1,92 @@
# -*- coding: utf-8 -*-
import importlib
import os
import sys
from os.path import abspath, join, exists, basename, splitext
from glob import glob
import types
import importlib
import pandas_ta
import pandas as pd
from pandas_ta import AnalysisIndicators
def import_dir(dir_path, verbose=True):
def bind(function_name, function, method):
"""
Helper function to bind the function and class method defined in a custom
indicator module to the active pandas_ta instance.
Args:
function_name (str): The name of the indicator within pandas_ta
function (fcn): The indicator function
method (fcn): The class method corresponding to the passed function
"""
setattr(pandas_ta, function_name, function)
setattr(AnalysisIndicators, function_name, method)
def create_dir(path, create_categories=True, verbose=True):
"""
Helper function to setup a suitable folder structure for working with
custom indicators. You only need to call this once whenever you want to
setup a new custom indicators folder.
Args:
path (str): Full path to where you want your indicator tree
create_categories (bool): If True create category sub-folders
verbose (bool): If True print verbose output of results
"""
# ensure that the passed directory exists / is readable
if not exists(dir_path):
print(f"[X] Unable to read the directory '{dir_path}'.")
if not exists(path):
os.makedirs(path)
if verbose:
print(f"[i] Created main directory '{path}'.")
# list the contents of the directory
# dirs = glob(abspath(join(path, '*')))
# optionally add any missing category subdirectories
if create_categories:
for sd in [*pandas_ta.Category]:
d = abspath(join(path, sd))
if not exists(d):
os.makedirs(d)
if verbose:
dirname = basename(d)
print(f"[i] Created an empty sub-directory '{dirname}'.")
def get_module_functions(module):
"""
Helper function to get the functions of an imported module as a dictionary.
Args:
module: python module
Returns:
dict: module functions mapping
{
"func1_name": func1,
"func2_name": func2,...
}
"""
module_functions = {}
for name, item in vars(module).items():
if isinstance(item, types.FunctionType):
module_functions[name] = item
return module_functions
def import_dir(path, verbose=True):
# ensure that the passed directory exists / is readable
if not exists(path):
print(f"[X] Unable to read the directory '{path}'.")
return
# list the contents of the directory
dirs = glob(abspath(join(dir_path, '*')))
dirs = glob(abspath(join(path, '*')))
# traverse full directory, importing all modules found there
for d in dirs:
@@ -31,20 +99,20 @@ def import_dir(dir_path, verbose=True):
continue
# for each module found in that category (directory)...
for module in glob(abspath(join(dir_path, dirname, '*.py'))):
for module in glob(abspath(join(path, dirname, '*.py'))):
module_name = splitext(basename(module))[0]
# ensure that the supplied path is included in our python path
if d not in sys.path:
sys.path.append(d)
# (re)load the indicator module
# (re)load the indicator module
module_functions = load_indicator_module(module_name)
# figure out which of the modules functions to bind to pandas_ta
fcn_callable = module_functions.get(module_name, None)
fcn_method_callable = module_functions.get(module_name + "_method", None)
if fcn_callable == None:
print(f"[X] Unable to find a function named '{module_name}' in the module '{module_name}.py'.")
continue
@@ -61,17 +129,18 @@ def import_dir(dir_path, verbose=True):
if verbose:
print(f"[i] Successfully imported the custom indicator '{module}' into category '{dirname}'.")
import_dir.__doc__ = \
"""
Import a directory of custom indicators into pandas_ta
Args:
dir_path (str): Full path to your indicator tree
path (str): Full path to your indicator tree
verbose (bool): If True verbose output of results
This method allows you to experiment and develop your own technical analysis
indicators in a separate local directory of your choice but use them seamlessly
together with the existing pandas_ta functions just like if they were part of
indicators in a separate local directory of your choice but use them seamlessly
together with the existing pandas_ta functions just like if they were part of
pandas_ta.
If you at some late point would like to push them into the pandas_ta library
@@ -85,7 +154,7 @@ A brief example of usage:
>>> import pandas_ta as ta
2. Create an empty directory on your machine where you want to work with your
indicators. Invoke pandas_ta.custom.import_dir once to pre-populate it with
indicators. Invoke pandas_ta.custom.import_dir once to pre-populate it with
sub-folders for all available indicator categories, e.g.:
>>> import os
@@ -94,22 +163,22 @@ sub-folders for all available indicator categories, e.g.:
>>> my_dir = abspath(join(expanduser("~"), "my_indicators"))
>>> create_dir(my_dir)
3. You can now create your own custom indicator e.g. by copying existing
ones from pandas_ta core module and modifying them.
3. You can now create your own custom indicator e.g. by copying existing
ones from pandas_ta core module and modifying them.
IMPORTANT: Each custom indicator should have a unique name and have both
IMPORTANT: Each custom indicator should have a unique name and have both
a) a function named exactly as the module, e.g. 'ni' if the module is ni.py
b) a matching method used by AnalysisIndicators named as the module but
ending with '_method'. E.g. 'ni_method'
In essence these modules should look exactly like the standard indicators
In essence these modules should look exactly like the standard indicators
available in categories under the pandas_ta-folder. The only difference will
be an addition of a matching class method.
For an example of the correct structure, look at the example ni.py in the
For an example of the correct structure, look at the example ni.py in the
examples folder.
The ni.py indicator is a trend indicator so therefore we drop it into the
The ni.py indicator is a trend indicator so therefore we drop it into the
sub-folder named trend. Thus we have a folder structure like this:
~/my_indicators/
@@ -131,18 +200,6 @@ If your custom indicator(s) loaded succesfully then it should behave exactly
like all other native indicators in pandas_ta, including help functions.
"""
def bind(function_name, function, method):
"""
Helper function to bind the function and class method defined in a custom
indicator module to the active pandas_ta instance.
Args:
function_name (str): The name of the indicator within pandas_ta
function (fcn): The indicator function
method (fcn): The class method corresponding to the passed function
"""
setattr(pandas_ta, function_name, function)
setattr(AnalysisIndicators, function_name, method)
def load_indicator_module(module_name):
"""
@@ -165,58 +222,4 @@ def load_indicator_module(module_name):
# reload to refresh previously loaded module
module = importlib.reload(module)
return get_module_functions(module)
def get_module_functions(module):
"""
Helper function to get the functions of an imported module as a dictionary.
Args:
module: python module
Returns:
dict: module functions mapping
{
"func1_name": func1,
"func2_name": func2,...
}
"""
module_functions = {}
for name, item in vars(module).items():
if isinstance(item, types.FunctionType):
module_functions[name] = item
return module_functions
def create_dir(dir_path, create_categories=True, verbose=True):
"""
Helper function to setup a suitable folder structure for working with
custom indicators. You only need to call this once whenever you want to
setup a new custom indicators folder.
Args:
dir_path (str): Full path to where you want your indicator tree
create_categories (bool): If True create category sub-folders
verbose (bool): If True print verbose output of results
"""
# ensure that the passed directory exists / is readable
if not exists(dir_path):
os.makedirs(dir_path)
if verbose:
print(f"[i] Created main directory '{dir_path}'.")
# list the contents of the directory
dirs = glob(abspath(join(dir_path, '*')))
# optionally add any missing category subdirectories
if create_categories:
for sd in [*pandas_ta.Category]:
d = abspath(join(dir_path, sd))
if not exists(d):
os.makedirs(d)
if verbose:
dirname = basename(d)
print(f"[i] Created an empty sub-directory '{dirname}'.")
return get_module_functions(module)