DOC readme update ENH performance notebook BUG #480

This commit is contained in:
Kevin Johnson
2022-02-11 16:20:54 -08:00
parent ed7ddac93a
commit b273081734
19 changed files with 1383 additions and 231 deletions
+26 -22
View File
@@ -24,6 +24,7 @@ Pandas TA - A Technical Analysis Library in Python 3
<br/>
![Example Chart](/images/TA_Chart.png)
![Example Chart](/images/TA_Volume.png)
<br/>
@@ -81,9 +82,9 @@ _Pandas Technical Analysis_ (**Pandas TA**) is a free, Open Source, and easy to
Large & Lite Weight Library
---------------------------
* Over 200 Indicators, Statistics and Candlestick Patterns
* Over 60 Candlestick Patterns with **[TA Lib](https://github.com/mrjbq7/ta-lib)** indicator integration
* Flat library structure similar to **TA Lib**
* Over 200 Indicators, Statistics and Candlestick Patterns.
* Over 60 Candlestick Patterns with **[TA Lib](https://github.com/mrjbq7/ta-lib)** indicator integration.
* Flat library structure similar to **TA Lib**.
* Single dependency: [Pandas](https://pandas.pydata.org/)
Accuracy
@@ -97,19 +98,20 @@ Performance
* **TA Lib** computations are **enabled** by default. They can be disabled per indicator.
* The library includes a performance method, ```help(ta.performance)```, to check runtime indicator performance for a given _ohlcv_ DataFrame.
* Optionable **Multiprocessing** for a Pandas TA ```Study```.
* Check your Indicator Performance with the [Indicator Performance Notebook](https://github.com/twopirllc/pandas-ta/tree/main/examples/Performance_Check.ipynb).
Bulk Processing
---------------
* Easily process many indicators using the DataFrame Extension method ```df.ta.study()```
* Supports two kinds of Studies
* **Builtin**: All, Categorical ("candles", "momentum", ...), and Common
* Easily process many indicators using the DataFrame Extension method ```df.ta.study()```.
* Supports two kinds of Studies.
* **Builtin**: All, Categorical ("candles", "momentum", ...), and Common.
* **Custom**: User Defined ```Study``` (formerly ```Strategy```).
Additional Features
-------------------
* **Examples**
* Basic usage and workflows. See the [**Example Jupyter Notebooks**](https://github.com/twopirllc/pandas-ta/tree/main/examples)
* Creating Custom Studies using the [__Study__ Class](https://github.com/twopirllc/pandas-ta/tree/main/examples/PandasTA_Study_Examples.ipynb)
* Basic usage and workflows. See the [**Example Jupyter Notebooks**](https://github.com/twopirllc/pandas-ta/tree/main/examples).
* Creating Custom Studies using the [__Study__ Class](https://github.com/twopirllc/pandas-ta/tree/main/examples/PandasTA_Study_Examples.ipynb).
* **Study Customizations** including, but not limited to, applying _prefixes_ or _suffixes_ or _both_ to column/indicators names.
* Composition/Chained Studies like putting **bbands** on **macd**.
* **Custom Indicators Directory**
@@ -899,7 +901,7 @@ Back to [Contents](#contents)
* _Weighted Moving Average_: **wma**
* _Zero Lag Moving Average_: **zlma**
| _Simple Moving Averages_ (SMA) and _Bollinger Bands_ (BBANDS) |
| _Exponential Moving Averages_ (EMA) and _Keltner Channels_ (BBANDS) |
|:--------:|
| ![Example Chart](/images/TA_Chart.png) |
@@ -915,9 +917,9 @@ Use parameter: cumulative=**True** for cumulative results.
* _Log Return_: **log_return**
* _Percent Return_: **percent_return**
| _Percent Return_ (Cumulative) with _Simple Moving Average_ (SMA) |
| _Log Returns_ (Cumulative) with _Simple Moving Average_ (SMA) |
|:--------:|
| ![Example Cumulative Percent Return](/images/SPY_CumulativePercentReturn.png) |
| ![Example Cumulative Percent Return](/images/SPY_CumLogReturns.png) |
<br/>
@@ -1047,7 +1049,7 @@ Back to [Contents](#contents)
# **Backtesting**
While Pandas TA is not a backtesting application, Pandas TA does provide _two_ methods to help generate trading signals for backtesting purposes: **Trend Signals** (```ta.tsignals()```) and **Cross Signals** (```ta.xsignals()```). Both Signal methods return a DataFrame with columns for the Trend, Trades, Entries and Exits.
A simple manual backtest using **Trend Signals** can be found in the [AI Example Notebook](https://github.com/twopirllc/pandas-ta/blob/main/examples/AIExample.ipynb) starting at _Trend Creation_ cell.
A simple manual backtest using **Trend Signals** can be found in the [TA Analysis Notebook](https://github.com/twopirllc/pandas-ta/blob/main/examples/TA_Analysis.ipynb) starting at _Trend Creation_ cell.
<br/>
@@ -1180,8 +1182,8 @@ Back to [Contents](#contents)
TODO
----
**Complete remaining TA Lib Indicators**
|||
| **Status** | **Remaining TA Lib Indicators** |
| - | - |
| &#9744; | Candlesticks |
| &#9744; | Indicators: ```ht_dcperiod```, ```ht_dcphase```, ```ht_phasor```, ```ht_sine```, ```ht_trendline```, ```ht_trendmode```, ```mama``` |
@@ -1189,24 +1191,26 @@ TODO
<br/>
**Config System**
|||
| **Status** | **Config System** |
| - | - |
| &#9744; | JSON Config File |
| | &#9744; JSON Config File Format |
| &#9744; | DataFrame Extension property: ```config``` |
| &#9744; | Features |
**Data Acquisition**
|||
<br/>
| **Status** | **Data Acquisition** |
| - | - |
| &#10004; | [Yahoo Finance]() _Default_ |
| &#10004; | [Polygon]() |
| &#10004; | [Yahoo Finance](https://github.com/ranaroussi/yfinance) _Default_ |
| &#10004; | [Polygon](https://github.com/pssolanki111/polygon) |
| &#9744; | [AlphaVantage](https://github.com/twopirllc/AlphaVantageAPI) |
| &#9744; | [Financial Modeling Prep](https://github.com/daxm/fmpsdk) |
**Stabilize**
|||
<br/>
| **Status** | **Stabilize** |
| - | - |
| &#9744; | Trading Signals |
| | &#9744; Trend Signals |
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+67 -61
View File
File diff suppressed because one or more lines are too long
Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

+5 -52
View File
@@ -15,7 +15,6 @@ from pandas_ta import *
# Pandas TA - DataFrame Extension Analysis Indicators
@register_dataframe_accessor("ta")
class AnalysisIndicators(object):
@@ -564,19 +563,7 @@ class AnalysisIndicators(object):
# Initialize
initial_column_count = len(self._df.columns)
excluded = [
"above",
"above_value",
"below",
"below_value",
"cross",
"cross_value",
# "data", # reserved
"long_run",
"short_run",
"tsignals",
"xsignals",
]
excluded = ["long_run", "short_run", "tsignals", "xsignals"]
# Get the Study Name and mode
name, mode = self._study_mode(*args)
@@ -1076,9 +1063,9 @@ class AnalysisIndicators(object):
use_tr=use_tr, mamode=mamode, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def stc(self, ma1=None, ma2=None, osc=None, tclength=None, fast=None, slow=None, factor=None, offset=None, **kwargs):
def stc(self, tclength=None, ma1=None, ma2=None, osc=None, fast=None, slow=None, factor=None, offset=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
result = stc(close=close, ma1=ma1, ma2=ma2, osc=osc, tclength=tclength, fast=fast, slow=slow, factor=factor,
result = stc(close=close, tclength=tclength, ma1=ma1, ma2=ma2, osc=osc, fast=fast, slow=slow, factor=factor,
offset=offset, **kwargs)
return self._post_process(result, **kwargs)
@@ -1444,11 +1431,11 @@ class AnalysisIndicators(object):
result = aroon(high=high, low=low, length=length, scalar=scalar, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def chop(self, length=None, atr_length=None, scalar=None, drift=None, offset=None, **kwargs):
def chop(self, length=None, atr_length=None, ln=None, scalar=None, drift=None, offset=None, **kwargs):
high = self._get_column(kwargs.pop("high", "high"))
low = self._get_column(kwargs.pop("low", "low"))
close = self._get_column(kwargs.pop("close", "close"))
result = chop(high=high, low=low, close=close, length=length, atr_length=atr_length, scalar=scalar,
result = chop(high=high, low=low, close=close, length=length, atr_length=atr_length, ln=ln, scalar=scalar,
drift=drift, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
@@ -1553,40 +1540,6 @@ class AnalysisIndicators(object):
trend_offset=trend_offset, trend_reset=trend_reset, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
# Utility
def above(self, asint=True, offset=None, **kwargs):
a = self._get_column(kwargs.pop("close", "a"))
b = self._get_column(kwargs.pop("close", "b"))
result = above(series_a=a, series_b=b, asint=asint, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def above_value(self, value=None, asint=True, offset=None, **kwargs):
a = self._get_column(kwargs.pop("close", "a"))
result = above_value(series_a=a, value=value, asint=asint, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def below(self, asint=True, offset=None, **kwargs):
a = self._get_column(kwargs.pop("close", "a"))
b = self._get_column(kwargs.pop("close", "b"))
result = below(series_a=a, series_b=b, asint=asint, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def below_value(self, value=None, asint=True, offset=None, **kwargs):
a = self._get_column(kwargs.pop("close", "a"))
result = below_value(series_a=a, value=value, asint=asint, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def cross(self, above=True, asint=True, offset=None, **kwargs):
a = self._get_column(kwargs.pop("close", "a"))
b = self._get_column(kwargs.pop("close", "b"))
result = cross(series_a=a, series_b=b, above=above, asint=asint, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def cross_value(self, value=None, above=True, asint=True, offset=None, **kwargs):
a = self._get_column(kwargs.pop("close", "a"))
result = cross_value(series_a=a, value=value, above=above, asint=asint, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
# Volatility
def aberration(self, length=None, atr_length=None, offset=None, **kwargs):
high = self._get_column(kwargs.pop("high", "high"))
+7 -4
View File
@@ -56,10 +56,13 @@ def stc(
pd.DataFrame: stc, macd, stoch
"""
# Validate
tclength = int(tclength) if tclength and tclength > 0 else 10
fast = int(fast) if fast and fast > 0 else 12
slow = int(slow) if slow and slow > 0 else 26
factor = float(factor) if factor and factor > 0 else 0.5
if isinstance(tclength, int) and tclength > 0:
tclength = int(tclength)
else:
tclength = 10
fast = int(fast) if isinstance(fast, int) and fast > 0 else 12
slow = int(slow) if isinstance(slow, int) and slow > 0 else 26
factor = float(factor) if isinstance(factor, int) and factor > 0 else 0.5
if slow < fast: # mandatory condition, but might be confusing
fast, slow = slow, fast
_length = max(tclength, fast, slow)
+5 -2
View File
@@ -38,8 +38,11 @@ def supertrend(
SUPERTl (long), SUPERTs (short) columns.
"""
# Validate
length = int(length) if length and length > 0 else 7
multiplier = float(multiplier) if multiplier and multiplier > 0 else 3.0
length = int(length) if isinstance(length, int) and length > 0 else 7
if isinstance(multiplier, float) and multiplier > 0:
multiplier = float(multiplier)
else:
multiplier = 3.0
high = verify_series(high, length)
low = verify_series(low, length)
close = verify_series(close, length)
+40 -38
View File
@@ -20,6 +20,44 @@ from .vidya import vidya
from .wma import wma
# Not ideal but it works. Submit a PR for a better solution. =)
# This design pattern is undesirable
def _ma(mamode, **kwargs):
if mamode == "dema":
return dema(**kwargs)
elif mamode == "fwma":
return fwma(**kwargs)
elif mamode == "hma":
return hma(**kwargs)
elif mamode == "linreg":
return linreg(**kwargs)
elif mamode == "midpoint":
return midpoint(**kwargs)
elif mamode == "pwma":
return pwma(**kwargs)
elif mamode == "rma":
return rma(**kwargs)
elif mamode == "sinwma":
return sinwma(**kwargs)
elif mamode == "sma":
return sma(**kwargs)
elif mamode == "ssf":
return ssf(**kwargs)
elif mamode == "swma":
return swma(**kwargs)
elif mamode == "t3":
return t3(**kwargs)
elif mamode == "tema":
return tema(**kwargs)
elif mamode == "trima":
return trima(**kwargs)
elif mamode == "vidya":
return vidya(**kwargs)
elif mamode == "wma":
return wma(**kwargs)
else:
return ema(**kwargs)
def zlma(
close: Series, length: int = None, mamode: str = None,
offset: int = None, **kwargs
@@ -62,45 +100,9 @@ def zlma(
kwargs.update({"close": close_})
kwargs.update({"length": length})
# Not ideal but it works. Submit a PR for a better solution. =)
# This design pattern is undesirable
def _ma(**kwargs):
if mamode == "dema":
return dema(**kwargs)
elif mamode == "fwma":
return fwma(**kwargs)
elif mamode == "hma":
return hma(**kwargs)
elif mamode == "linreg":
return linreg(**kwargs)
elif mamode == "midpoint":
return midpoint(**kwargs)
elif mamode == "pwma":
return pwma(**kwargs)
elif mamode == "rma":
return rma(**kwargs)
elif mamode == "sinwma":
return sinwma(**kwargs)
elif mamode == "sma":
return sma(**kwargs)
elif mamode == "ssf":
return ssf(**kwargs)
elif mamode == "swma":
return swma(**kwargs)
elif mamode == "t3":
return t3(**kwargs)
elif mamode == "tema":
return tema(**kwargs)
elif mamode == "trima":
return trima(**kwargs)
elif mamode == "vidya":
return vidya(**kwargs)
elif mamode == "wma":
return wma(**kwargs)
else:
return ema(**kwargs)
zlma = _ma(**kwargs)
zlma = _ma(mamode, **kwargs)
# Offset
if offset != 0:
+1 -8
View File
@@ -177,14 +177,7 @@ def performance(df: DataFrame,
stats = bool(stats) if isinstance(stats, bool) and stats else False
verbose = bool(verbose) if isinstance(verbose, bool) and verbose else False
_ex = [
"above",
"above_value",
"below",
"below_value",
"cross",
"cross_value",
"ichimoku"]
_ex = ["ichimoku"]
if isinstance(excluded, list) and len(excluded) > 0:
_ex += excluded
indicators = df.ta.indicators(as_list=True, exclude=_ex)
+35 -37
View File
@@ -11,7 +11,7 @@ from .context import pandas_ta
# Testing Parameters
cores = cpu_count() - 1
cores = 4# cpu_count() - 1
cumulative = False
speed_table = False
timed_test = False
@@ -80,7 +80,7 @@ class TestStudyMethods(TestCase):
self.category = "All: TA Lib"
def test_all_no_talib(self):
"""Study: Sans TA Lib"""
"""Study: All Sans TA Lib"""
self.category = "All"
self.data.ta.study(talib=False, verbose=verbose, timed=timed_test)
self.category = "All: Sans TA Lib"
@@ -92,7 +92,7 @@ class TestStudyMethods(TestCase):
self.data.ta.study(self.category, length=10, verbose=verbose, timed=timed_test)
self.data.ta.study(self.category, length=50, verbose=verbose, timed=timed_test)
self.data.ta.study(self.category, fast=5, slow=10, verbose=verbose, timed=timed_test)
self.category = "All: Multiruns with diff Args" # Rename for Speed Table
self.category = "All: Multiruns with Multiparameters" # Rename for Speed Table
@skipUnless(verbose, "verbose mode only")
def test_all_name_study(self):
@@ -108,17 +108,17 @@ class TestStudyMethods(TestCase):
@skipUnless(verbose, "verbose mode only")
def test_all_study(self):
"""Study: All"""
self.category = "Candles"
self.data.ta.study(pandas_ta.AllStudy, verbose=verbose, timed=timed_test)
def test_all_without_append(self):
"""Study: All sans append"""
self.category = "All: Sans Append"
self.category = "All: Sans append"
self.data.ta.study(append=False, verbose=verbose, timed=timed_test)
# @skip
def test_candles_category(self):
"""Category: Candles"""
"""Candles"""
self.category = "Candles"
self.data.ta.study(self.category, verbose=verbose, timed=timed_test)
@@ -129,16 +129,16 @@ class TestStudyMethods(TestCase):
self.data.ta.study(pandas_ta.CommonStudy, verbose=verbose, timed=timed_test)
def test_cycles_category(self):
"""Category: Cycles"""
"""Cycles"""
self.category = "Cycles"
self.data.ta.study(self.category, verbose=verbose, timed=timed_test)
# @skip
def test_custom_a_with_multiprocessing(self):
"""Custom A: With Multiprocessing"""
self.category = "Custom A"
"""Custom A: Multiprocessing"""
self.category = "Custom A: Multiprocessing"
momo_bands_sma_ta = [
cta = [
{"kind": "cdl_pattern", "name": "tristar"}, # 1
{"kind": "rsi"}, # 1
{"kind": "macd"}, # 3
@@ -148,28 +148,21 @@ class TestStudyMethods(TestCase):
{"kind": "log_return", "cumulative": True}, # 1
{"kind": "ema", "close": "CUMLOGRET_1", "length": 5, "suffix": "CLR"} # 1
]
# total_columns = len(self.data.columns)
custom = pandas_ta.Study(
name="Commons with Cumulative Log Return EMA Chain", # name
ta=momo_bands_sma_ta, # ta
ta=cta, # ta
description="Common indicators with specific lengths and a chained indicator", # description
cores=cores
)
self.data.ta.study(custom, cores=0, verbose=verbose, timed=timed_test)
# Note: Will not find column 'CUMLOGRET_1' with mp, use cores=0 instead
if "adj close" in self.data.columns or "adj_close" in self.data.columns:
self.assertEqual(len(self.data.columns), 21)
else:
self.assertEqual(len(self.data.columns), 19)
self.data.ta.study(custom, verbose=verbose, timed=timed_test)
self.assertEqual(len(self.data.columns), 21)
# @skipUnless(verbose, "verbose mode only")
def test_custom_a_without_multiprocessing(self):
"""Custom A: Without Multiprocessing"""
"""Custom A: Sans Multiprocessing"""
self.category = "Custom A: Sans Multiprocessing"
_cores = self.data.ta.cores
momo_bands_sma_ta = [
{"kind": "rsi"}, # 1
{"kind": "macd"}, # 3
@@ -188,13 +181,13 @@ class TestStudyMethods(TestCase):
cores=0
)
# Depreciation warning test
self.data.ta.strategy(custom, cores=4, verbose=verbose, timed=timed_test)
self.data.ta.cores = _cores
self.data.ta.strategy(custom, cores=0, verbose=verbose, timed=timed_test)
self.data.ta.cores = cores
# @skip
def test_custom_args_tuple(self):
"""Custom B: Tuple Arguments"""
self.category = "Custom B"
self.category = "Custom B: Tuple Arguments"
custom_args_ta = [
{"kind": "ema", "params": (5,)},
@@ -210,7 +203,7 @@ class TestStudyMethods(TestCase):
def test_custom_col_names_tuple(self):
"""Custom C: Column Name Tuple"""
self.category = "Custom C"
self.category = "Custom C: Column Name Tuple"
custom_args_ta = [{"kind": "bbands", "col_names": ("LB", "MB", "UB", "BW", "BP")}]
@@ -224,7 +217,7 @@ class TestStudyMethods(TestCase):
# @skip
def test_custom_col_numbers_tuple(self):
"""Custom D: Column Number Tuple"""
self.category = "Custom D"
self.category = "Custom D: Column Number Tuple"
custom_args_ta = [{"kind": "macd", "col_numbers": (1,)}]
@@ -261,51 +254,56 @@ class TestStudyMethods(TestCase):
# @skip
def test_momentum_category(self):
"""Category: Momentum"""
"""Momentum"""
self.category = "Momentum"
self.data.ta.study(self.category, verbose=verbose, timed=timed_test)
# @skip
def test_overlap_category(self):
"""Category: Overlap"""
"""Overlap"""
self.category = "Overlap"
self.data.ta.study(self.category, verbose=verbose, timed=timed_test)
# @skip
def test_performance_category(self):
"""Category: Performance"""
"""Performance"""
self.category = "Performance"
self.data.ta.study(self.category, verbose=verbose, timed=timed_test)
# @skip
def test_statistics_category(self):
"""Category: Statistics"""
"""Statistics"""
self.category = "Statistics"
self.data.ta.study(self.category, verbose=verbose, timed=timed_test)
# @skip
def test_transform_category(self):
"""Transform"""
self.category = "Transform"
self.data.ta.study(self.category, verbose=verbose, timed=timed_test)
# @skip
def test_trend_category(self):
"""Category: Trend"""
"""Trend"""
self.category = "Trend"
self.data.ta.study(self.category, verbose=verbose, timed=timed_test)
# @skip
def test_volatility_category(self):
"""Category: Volume"""
"""Volatility"""
self.category = "Volatility"
self.data.ta.study(self.category, verbose=verbose, timed=timed_test)
# @skip
def test_volume_category(self):
"""Category: Volume"""
"""Volume"""
self.category = "Volume"
self.data.ta.study(self.category, verbose=verbose, timed=timed_test)
# @skipUnless(verbose, "verbose mode only")
def test_all_without_multiprocessing(self):
"""Study: All without Multiprocessing"""
self.category = "All: Sans Multiprocessing"
"""Study: All sans Multiprocessing"""
self.category = "Study: All sans Multiprocessing"
cores = self.data.ta.cores
self.data.ta.cores = 0
self.data.ta.study(verbose=verbose, timed=timed_test)