ENH rename strategy() to study() MAINT refactoring DOC update

This commit is contained in:
Kevin Johnson
2022-01-16 15:56:12 -08:00
parent 993a265c10
commit 26a6ffd3d0
13 changed files with 3896 additions and 3546 deletions
+3 -3
View File
@@ -4,7 +4,7 @@ all:
make test_metrics
make test_ta
make test_ext
make test_strats
make test_studies
caches:
find ./pandas_ta | grep -E "(__pycache__|\.pyc|\.pyo$\)"
@@ -21,8 +21,8 @@ test_ext:
test_metrics:
python -m unittest -v -f tests/test_utils_metrics.py
test_strats:
python -m unittest -v -f tests/test_strategy.py
test_studies:
python -m unittest -v -f tests/test_study.py
test_ta:
python -m unittest -v -f tests/test_indicator_*.py
+90 -68
View File
@@ -46,9 +46,9 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever
* [Programming Conventions](#programming-conventions)
* [Standard](#standard)
* [Pandas TA DataFrame Extension](#pandas-ta-dataframe-extension)
* [Pandas TA Strategy](#pandas-ta-strategy)
* [Pandas TA Strategies](#pandas-ta-strategies)
* [Types of Strategies](#types-of-strategies)
* [Pandas TA Study](#pandas-ta-study)
* [Pandas TA Studies](#pandas-ta-studies)
* [Types of Studies](#types-of-studies)
* [Multiprocessing](#multiprocessing)
* [DataFrame Properties](#dataframe-properties)
* [DataFrame Methods](#dataframe-methods)
@@ -81,25 +81,25 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever
# **Features**
* Has 140+ indicators and utility functions.
* **BETA**: Pandas TA will run most of TA Lib's indicators, this includes TA Lib's 63 Chart Patterns.
* Indicators in Python are tightly correlated with the _de facto_ [TA Lib](https://github.com/mrjbq7/ta-lib) if they share common indicators.
* If TA Lib is also installed, TA Lib computations are **enabled** by default but can be disabled disabled per indicator by using the argument ```talib=False```.
* For example to disable TA Lib calculation for **stdev**: ```ta.stdev(df["close"], length=30, talib=False)```.
* **NEW** Include External Custom Indicators independent of the builtin Pandas TA indicators. For more information, see ```import_dir``` documentation under ```/pandas_ta/custom.py```.
* **NEW** Generate Sample Realizations if the [stochastic](https://github.com/crflynn/stochastic) package is installed. ```pip install stochastic```. See the [Stochastic Samples](#stochastic-samples) section below.
* Example Jupyter Notebook with **vectorbt** Portfolio Backtesting with Pandas TA's ```ta.tsignals``` method.
* Have the need for speed? By using the DataFrame _strategy_ method, you get **multiprocessing** for free! __Conditions permitting__.
* Easily add _prefixes_ or _suffixes_ or _both_ to columns names. Useful for Custom Chained Strategies.
* Example Jupyter Notebooks under the [examples](https://github.com/twopirllc/pandas-ta/tree/main/examples) directory, including how to create Custom Strategies using the new [__Strategy__ Class](https://github.com/twopirllc/pandas-ta/tree/main/examples/PandasTA_Strategy_Examples.ipynb)
* Potential Data Leaks: **dpo** and **ichimoku**. See indicator list below for details. Set ```lookahead=False``` to disable.
* Over 140 indicators and utility functions.
* **TA Lib** indicators (```pip install ta-lib```).
* TA Lib's 63 Chart Patterns
* Python Indicators are tightly correlated with the _de facto_ [TA Lib](https://github.com/mrjbq7/ta-lib).
* TA Lib computations are by default **enabled**. They can be disabled disabled per indicator by using the argument ```talib=False```.
* For example to disable TA Lib calculation for **stdev**: ```ta.stdev(df["close"], length=30, talib=False)```.
* **Stochastic Sample Realizations** with the [stochastic](https://github.com/crflynn/stochastic) package (```pip install stochastic```). See the [Stochastic Samples](#stochastic-samples) section below.
* **External Custom Indicators Directory** independent of the builtin Pandas TA indicators. For more information, see ```import_dir``` documentation under ```/pandas_ta/custom.py```.
* **Example Jupyter Notebooks** with **vectorbt** Portfolio Backtesting with Pandas TA's ```ta.tsignals``` method.
* **Multiprocessing** Support for Sets of Indicators called ```Study``` (formerly ```Strategy```) when using the DataFrame Extension method ```df.ta.study()```.
* Have the need for speed? By using the DataFrame _study_ method, you get **multiprocessing** for free! __Conditions permitting__.
* **Study Customizations** like applying _prefixes_ or _suffixes_ or _both_ to column/indicators names. Useful for Custom Chained Studies.
* **Example Jupyter Notebooks** under the [examples](https://github.com/twopirllc/pandas-ta/tree/main/examples) directory, including how to create Custom Studies using the new [__Study__ Class](https://github.com/twopirllc/pandas-ta/tree/main/examples/PandasTA_Study_Examples.ipynb)
<br/>
**Under Development**
===================
**Pandas TA** can also leverage other common or popular trading packages you may have installed in your environment. Current packages include: [**TA Lib**](https://mrjbq7.github.io/ta-lib/), [**Vector BT**](https://github.com/polakowo/vectorbt), [**YFinance**](https://github.com/ranaroussi/yfinance), [**Polygon API**](https://github.com/pssolanki111/polygon), [**Stochastic**](https://github.com//stochastic). Much of which is _experimental_ and likely to break until it stabilizes more.
* If **TA Lib** installed, existing indicators will _eventually_ get a **TA Lib** version.
**Pandas TA** can also leverage other common or popular trading packages you may have installed in your environment. Current packages include: [**TA Lib**](https://mrjbq7.github.io/ta-lib/), [**Vector BT**](https://github.com/polakowo/vectorbt), [**YFinance**](https://github.com/ranaroussi/yfinance), [**Polygon API**](https://github.com/pssolanki111/polygon), and [**Stochastic**](https://github.com//stochastic). Much is _experimental_.
* Easily download _ohlcv_ data from [yfinance](https://github.com/ranaroussi/yfinance) or [Polygon API](https://github.com/pssolanki111/polygon).
* See ```help(ta.ticker)```, ```help(ta.yf)```, ```help(ta.polygon_api)``` and examples below.
* Some Common Performance Metrics
@@ -118,7 +118,7 @@ $ pip install pandas_ta
Latest Version
--------------
Best choice! Version: *0.3.44b*
Best choice! Version: *0.3.45b*
* Includes all fixes and updates between **pypi** and what is covered in this README.
```sh
$ pip install -U git+https://github.com/twopirllc/pandas-ta
@@ -198,7 +198,7 @@ Thanks for using **Pandas TA**!
* Did they help?
* What is missing?
* Could you help improve them?
* Did you know you can easily build _Custom Strategies_ with the **[Strategy](https://github.com/twopirllc/pandas-ta/blob/main/examples/PandasTA_Strategy_Examples.ipynb) Class**?
* Did you know you can easily build _Custom Strategies_ with the **[Study](https://github.com/twopirllc/pandas-ta/blob/main/examples/PandasTA_Study_Examples.ipynb) Class**?
* Documentation could _always_ be improved. Can you help contribute?
* ### [Bugs, Indicators or Feature Requests](https://github.com/twopirllc/pandas-ta/issues)
@@ -226,7 +226,7 @@ _Thank you for your contributions!_
**Programming Conventions**
===========================
**Pandas TA** has three primary "styles" of processing Technical Indicators for your use case and/or requirements. They are: _Standard_, _DataFrame Extension_, and the _Pandas TA Strategy_. Each with increasing levels of abstraction for ease of use. As you become more familiar with **Pandas TA**, the simplicity and speed of using a _Pandas TA Strategy_ may become more apparent. Furthermore, you can create your own indicators through Chaining or Composition. Lastly, each indicator either returns a _Series_ or a _DataFrame_ in Uppercase Underscore format regardless of style.
**Pandas TA** has three primary "styles" of processing Technical Indicators for your use case and/or requirements. They are: _Standard_, _DataFrame Extension_, and the _Pandas TA Study_. Each with increasing levels of abstraction for ease of use. As you become more familiar with **Pandas TA**, the simplicity and speed of using a _Pandas TA Study_ may become more apparent. Furthermore, you can create your own indicators through Chaining or Composition. Lastly, each indicator either returns a _Series_ or a _DataFrame_ in Uppercase Underscore format regardless of style.
<br/>
@@ -266,17 +266,17 @@ Same as the last three examples, but appending the results directly to the DataF
<br/>
_Pandas TA Strategy_
_Pandas TA Study_
====================
A **Pandas TA** Strategy is a named group of indicators to be run by the _strategy_ method. All Strategies use **mulitprocessing** _except_ when using the ```col_names``` parameter (see [below](#multiprocessing)). There are different types of _Strategies_ listed in the following section.
A **Pandas TA** Study is a named group of indicators to be run by the _study_ method. All Studies use **mulitprocessing** _except_ when using the ```col_names``` parameter (see [below](#multiprocessing)). There are different types of _Studies_ listed in the following section.
<br/>
### Here are the previous _Styles_ implemented using a Strategy Class:
### Here are the previous _Styles_ implemented using a Study Class:
```python
# (1) Create the Strategy
MyStrategy = ta.Strategy(
# (1) Create the Study
MyStudy = ta.Study(
name="DCSMA10",
ta=[
{"kind": "ohlc4"},
@@ -286,47 +286,51 @@ MyStrategy = ta.Strategy(
]
)
# (2) Run the Strategy
df.ta.strategy(MyStrategy, **kwargs)
# (2) Run the Study
df.ta.study(MyStudy, **kwargs)
```
<br/><br/>
# **Pandas TA** _Strategies_
# **Pandas TA** _Studies_
The _Strategy_ Class is a simple way to name and group your favorite TA Indicators by using a _Data Class_. **Pandas TA** comes with two prebuilt basic Strategies to help you get started: __AllStrategy__ and __CommonStrategy__. A _Strategy_ can be as simple as the __CommonStrategy__ or as complex as needed using Composition/Chaining.
* :stop_sign: **The Study Class and corresponding method have replaced the Strategy Class and method.**
* When using the _strategy_ method, **all** indicators will be automatically appended to the DataFrame ```df```.
* You are using a Chained Strategy when you have the output of one indicator as input into one or more indicators in the same _Strategy_.
<br/>
The _Study_ Class is a simple way to name and group your favorite TA Indicators by using a _Data Class_. **Pandas TA** comes with two prebuilt basic Strategies to help you get started: __AllStudy__ and __CommonStudy__. A _Study_ can be as simple as the __CommonStudy__ or as complex as needed using Composition/Chaining.
* When using the _study_ method, **all** indicators will be automatically appended to the DataFrame ```df```.
* You are using a Chained Study when you have the output of one indicator as input into one or more indicators in the same _Study_.
* **Note:** Use the 'prefix' and/or 'suffix' keywords to distinguish the composed indicator from it's default Series.
See the [Pandas TA Strategy Examples Notebook](https://github.com/twopirllc/pandas-ta/blob/main/examples/PandasTA_Strategy_Examples.ipynb) for examples including _Indicator Composition/Chaining_.
See the [Pandas TA Study Examples Notebook](https://github.com/twopirllc/pandas-ta/blob/main/examples/PandasTA_Study_Examples.ipynb) for examples including _Indicator Composition/Chaining_.
Strategy Requirements
Study Requirements
---------------------
- _name_: Some short memorable string. _Note_: Case-insensitive "All" is reserved.
- _ta_: A list of dicts containing keyword arguments to identify the indicator and the indicator's arguments
- **Note:** A Strategy will fail when consumed by Pandas TA if there is no ```{"kind": "indicator name"}``` attribute. _Remember_ to check your spelling.
- **Note:** A Study will fail when consumed by Pandas TA if there is no ```{"kind": "indicator name"}``` attribute. _Remember_ to check your spelling.
Optional Parameters
-------------------
- _description_: A more detailed description of what the Strategy tries to capture. Default: None
- _description_: A more detailed description of what the Study tries to capture. Default: None
- _created_: At datetime string of when it was created. Default: Automatically generated.
<br/>
Types of Strategies
Types of Studies
=======================
## _Builtin_
```python
# Running the Builtin CommonStrategy as mentioned above
df.ta.strategy(ta.CommonStrategy)
# Running the Builtin CommonStudy as mentioned above
df.ta.study(ta.CommonStudy)
# The Default Strategy is the ta.AllStrategy. The following are equivalent:
df.ta.strategy()
df.ta.strategy("All")
df.ta.strategy(ta.AllStrategy)
# The Default Study is the ta.AllStudy. The following are equivalent:
df.ta.study()
df.ta.study("All")
df.ta.study(ta.AllStudy)
```
## _Categorical_
@@ -334,15 +338,15 @@ df.ta.strategy(ta.AllStrategy)
# List of indicator categories
df.ta.categories
# Running a Categorical Strategy only requires the Category name
df.ta.strategy("Momentum") # Default values for all Momentum indicators
df.ta.strategy("overlap", length=42) # Override all Overlap 'length' attributes
# Running a Categorical Study only requires the Category name
df.ta.study("Momentum") # Default values for all Momentum indicators
df.ta.study("overlap", length=42) # Override all Overlap 'length' attributes
```
## _Custom_
```python
# Create your own Custom Strategy
CustomStrategy = ta.Strategy(
# Create your own Custom Study
CustomStudy = ta.Study(
name="Momo and Volatility",
description="SMA 50,200, BBANDS, RSI, MACD and Volume SMA 20",
ta=[
@@ -354,8 +358,8 @@ CustomStrategy = ta.Strategy(
{"kind": "sma", "close": "volume", "length": 20, "prefix": "VOLUME"},
]
)
# To run your "Custom Strategy"
df.ta.strategy(CustomStrategy)
# To run your "Custom Study"
df.ta.study(CustomStudy)
```
<br/>
@@ -363,7 +367,7 @@ df.ta.strategy(CustomStrategy)
**Multiprocessing**
=======================
The **Pandas TA** _strategy_ method utilizes **multiprocessing** for bulk indicator processing of all Strategy types with **ONE EXCEPTION!** When using the ```col_names``` parameter to rename resultant column(s), the indicators in ```ta``` array will be ran in order.
The **Pandas TA** _study_ method utilizes **multiprocessing** for bulk indicator processing of all Study types with **ONE EXCEPTION!** When using the ```col_names``` parameter to rename resultant column(s), the indicators in ```ta``` array will be ran in order.
```python
# VWAP requires the DataFrame index to be a DatetimeIndex.
@@ -372,17 +376,17 @@ df.set_index(pd.DatetimeIndex(df["datetime"]), inplace=True)
# Runs and appends all indicators to the current DataFrame by default
# The resultant DataFrame will be large.
df.ta.strategy()
df.ta.study()
# Or the string "all"
df.ta.strategy("all")
# Or the ta.AllStrategy
df.ta.strategy(ta.AllStrategy)
df.ta.study("all")
# Or the ta.AllStudy
df.ta.study(ta.AllStudy)
# Use verbose if you want to make sure it is running.
df.ta.strategy(verbose=True)
df.ta.study(verbose=True)
# Use timed if you want to see how long it takes to run.
df.ta.strategy(timed=True)
df.ta.study(timed=True)
# Choose the number of cores to use. Default is all available cores.
# For no multiprocessing, set this value to 0.
@@ -390,12 +394,12 @@ df.ta.cores = 4
# Maybe you do not want certain indicators.
# Just exclude (a list of) them.
df.ta.strategy(exclude=["bop", "mom", "percent_return", "wcp", "pvi"], verbose=True)
df.ta.study(exclude=["bop", "mom", "percent_return", "wcp", "pvi"], verbose=True)
# Perhaps you want to use different values for indicators.
# This will run ALL indicators that have fast or slow as parameters.
# Check your results and exclude as necessary.
df.ta.strategy(fast=10, slow=50, verbose=True)
df.ta.study(fast=10, slow=50, verbose=True)
# Sanity check. Make sure all the columns are there
df.columns
@@ -403,12 +407,12 @@ df.columns
<br/>
## Custom Strategy without Multiprocessing
## Custom Study without Multiprocessing
**Remember** These will not be utilizing **multiprocessing**
```python
NonMPStrategy = ta.Strategy(
NonMPStudy = ta.Study(
name="EMAs, BBs, and MACD",
description="Non Multiprocessing Strategy by rename Columns",
description="Non Multiprocessing Study by rename Columns",
ta=[
{"kind": "ema", "length": 8},
{"kind": "ema", "length": 21},
@@ -417,7 +421,7 @@ NonMPStrategy = ta.Strategy(
]
)
# Run it
df.ta.strategy(NonMPStrategy)
df.ta.study(NonMPStudy)
```
<br/><br/>
@@ -446,7 +450,7 @@ df.ta.categories
## **cores**
```python
# Set the number of cores to use for strategy multiprocessing
# Set the number of cores to use for Study multiprocessing
# Defaults to the number of cpus you have.
df.ta.cores = 4
@@ -466,13 +470,24 @@ df.ta.cores
df.ta.datetime_ordered
```
## **ds**
```python
# Gets the Data Source (ds). Default: "yf"
df.ta.ds
# Set the Data Source (ds) so that df.ta.ticker() can download ohlcv data.
# Available Data Sources: "yf", "polgon"
df.ta.ds = "yf"
```
## **exchange**
```python
# Sets the Exchange to use when calculating the last_run property. Default: "NYSE"
df.ta.exchange
# Set the Exchange to use.
# Set the Exchange.
# Available Exchanges: "ASX", "BMF", "DIFX", "FWB", "HKE", "JSE", "LSE", "NSE", "NYSE", "NZSX", "RTS", "SGX", "SSE", "TSE", "TSX"
df.ta.exchange = "LSE"
```
@@ -590,6 +605,13 @@ df = df.ta.ticker("aapl", period="1y") # Gets this past year
df = df.ta.ticker("aapl", period="1y", interval="1wk") # Gets this past year in weeks
df = df.ta.ticker("aapl", period="1mo", interval="1h") # Gets this past month in hours
# A Ticker & DataFrame Dictionary with a Study applied
tickers = ["SPY", "AAPL", "SQ"]
s = ta.CommonStudy
asset = {f"{t}_D": ta.df.ta.ticker(t, cores=0, study=s, ds="yf") for t in tickers}
print(asset.keys())
spydf = asset["SPY_D]
# BUT WAIT!! THERE'S MORE!!
help(ta.yf)
```
@@ -739,7 +761,7 @@ df = df.ta.cdl_pattern(name=["doji", "inside"])
* _Fast Stochastic Oscillator_: **stochf**
* _Stochastic RSI_: **stochrsi**
* _TD Sequential_: **td_seq**
* Excluded from ```df.ta.strategy()```.
* Excluded from ```df.ta.study()```.
* _Trix_: **trix**
* _True strength index_: **tsi**
* _Ultimate Oscillator_: **uo**
@@ -1014,11 +1036,11 @@ help(ta.sample)
# **Changes**
## **General**
* A __Strategy__ Class to help name and group your favorite indicators.
* A __Study__ Class to help name and group your favorite indicators.
* If a **TA Lib** is already installed, Pandas TA will run TA Lib's version. (**BETA**)
* Some indicators have had their ```mamode``` _kwarg_ updated with more _moving average_ choices with the **Moving Average Utility** function ```ta.ma()```. For simplicity, all _choices_ are single source _moving averages_. This is primarily an internal utility used by indicators that have a ```mamode``` _kwarg_. This includes indicators: _accbands_, _amat_, _aobv_, _atr_, _bbands_, _bias_, _efi_, _hilo_, _kc_, _natr_, _qqe_, _rvi_, and _thermo_; the default ```mamode``` parameters have not changed. However, ```ta.ma()``` can be used by the user as well if needed. For more information: ```help(ta.ma)```
* **Moving Average Choices**: dema, ema, fwma, hma, linreg, midpoint, pwma, rma, sinwma, sma, swma, t3, tema, trima, vidya, wma, zlma.
* An _experimental_ and independent __Watchlist__ Class located in the [Examples](https://github.com/twopirllc/pandas-ta/tree/main/examples/watchlist.py) Directory that can be used in conjunction with the new __Strategy__ Class.
* An _experimental_ and independent __Watchlist__ Class located in the [Examples](https://github.com/twopirllc/pandas-ta/tree/main/examples/watchlist.py) Directory that can be used in conjunction with the new __Study__ Class.
* _Linear Regression_ (**linear_regression**) is a new utility method for Simple Linear Regression using _Numpy_ or _Scikit Learn_'s implementation.
* Added utility/convience function, ```to_utc```, to convert the DataFrame index to UTC. See: ```help(ta.to_utc)``` **Now** as a Pandas TA DataFrame Property to easily convert the DataFrame index to UTC.
@@ -1040,7 +1062,7 @@ help(ta.sample)
* _Smoothed Moving Average_ (**smma**) can be used to confirm trends and define areas of support and resistance. See: ```help(ta.smma)```
* _Fast Stochastic Oscillator_ (**stochf**) is related to **stoch** but is less rarely used since it is more volatile. See: ```help(ta.stochf)```
* _Squeeze Pro_ (**squeeze_pro**) is an extended version of "TTM Squeeze" from John Carter. See ```help(ta.squeeze_pro)```
* _Tom DeMark's Sequential_ (**td_seq**) attempts to identify a price point where an uptrend or a downtrend exhausts itself and reverses. Currently exlcuded from ```df.ta.strategy()``` for performance reasons. See ```help(ta.td_seq)```
* _Tom DeMark's Sequential_ (**td_seq**) attempts to identify a price point where an uptrend or a downtrend exhausts itself and reverses. Currently exlcuded from ```df.ta.study()``` for performance reasons. See ```help(ta.td_seq)```
* _Think or Swim Standard Deviation All_ (**tos_stdevall**) indicator which returns the standard deviation of data for the entire plot or for the interval of the last bars defined by the length parameter. See ```help(ta.tos_stdevall)```
* _Worden Brothers Time Segmented Value_ (**wb_tsv**) is an oscillator indicator that attempts to indentify money flow in a stock, similar to On Balance Volume (**obv**). See ```help(ta.wb_tsv)```
+170 -169
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+68 -61
View File
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
{
"name": "Sample JSON Strategy",
"description": "Sample JSON Strategy",
"name": "Sample JSON Study",
"description": "Sample JSON Study",
"ta": [
{"kind": "ema", "length": 8, "sma": true},
{"kind": "ema", "length": 21, "talib": true},
+24 -12
View File
@@ -60,9 +60,9 @@ class Watchlist(object):
"""
# Watchlist Class (** This is subject to change! **)
A simple Class to load/download financial market data and automatically
apply Technical Analysis indicators with a Pandas TA Strategy.
apply Technical Analysis indicators with a Pandas TA Study.
Default Strategy: pandas_ta.CommonStrategy
Default Study: pandas_ta.CommonStudy
## Package Support:
### Data Source (Default: AlphaVantage)
@@ -79,18 +79,19 @@ class Watchlist(object):
def __init__(self,
tickers: list, tf: str = None, name: str = None,
strategy: ta.Strategy = None, ds_name: str = "av", **kwargs,
study: ta.Study = None, ds_name: str = "av", **kwargs,
):
self.verbose = kwargs.pop("verbose", False)
self.debug = kwargs.pop("debug", False)
self.timed = kwargs.pop("timed", False)
self.strategy = kwargs.pop("strategy", study) # Temporary
self.tickers = tickers
self.tf = tf
self.name = name if isinstance(name, str) else f"Watch: {', '.join(tickers)}"
self.data = None
self.kwargs = kwargs
self.strategy = strategy
self.study = self.strategy
self._init_data_source(ds_name)
@@ -148,7 +149,7 @@ class Watchlist(object):
col = kwargs.pop("close", "close")
if mas:
# df.ta.strategy(self.strategy, append=True)
# df.ta.study(self.study, append=True)
price = df[[col, "SMA_10", "SMA_20", "SMA_50", "SMA_200"]]
else:
price = df[col]
@@ -198,18 +199,17 @@ class Watchlist(object):
if not df.ta.datetime_ordered:
df = df.set_index(pd.DatetimeIndex(df[index]))
if self.ds_name == "yahoo":
yf_data = self.ds.Ticker(ticker)
df = yf_data.history(period="max")
df = ta.df.ta.ticker(ticker, lc_cols=True, returns=True)
to_save = f"{self.file_path}/{ticker}_{tf}.csv"
print(f"[+] Saving: {to_save}")
df.to_csv(to_save)
df.to_csv(to_save, mode="a")
# Remove select columns
df = self._drop_columns(df, drop)
if kwargs.pop("analyze", True):
if self.debug: print(f"[+] TA[{len(self.strategy.ta)}]: {self.strategy.name}")
df.ta.strategy(self.strategy, timed=self.timed, **kwargs)
if self.debug: print(f"[+] TA[{len(self.study.ta)}]: {self.study.name}")
df.ta.study(self.study, timed=self.timed, **kwargs)
df.ticker = ticker # Attach ticker to the DataFrame
df.tf = tf
@@ -254,7 +254,19 @@ class Watchlist(object):
if value is not None and isinstance(value, ta.Strategy):
self._strategy = value
else:
self._strategy = ta.CommonStrategy
self._strategy = ta.CommonStudy
@property
def study(self) -> ta.Study:
"""Sets a valid Study. Default: pandas_ta.CommonStudy"""
return self._study
@study.setter
def study(self, value: ta.Study) -> None:
if value is not None and isinstance(value, ta.Study):
self._study = value
else:
self._study = ta.CommonStudy
@property
def tf(self) -> str:
@@ -307,7 +319,7 @@ class Watchlist(object):
pd.DataFrame().ta.indicators(*args, **kwargs)
def __repr__(self) -> str:
s = f"Watch(name='{self.name}', ds_name='{self.ds_name}', tickers[{len(self.tickers)}]='{', '.join(self.tickers)}', tf='{self.tf}', strategy[{self.strategy.total_ta()}]='{self.strategy.name}'"
s = f"Watch(name='{self.name}', ds_name='{self.ds_name}', tickers[{len(self.tickers)}]='{', '.join(self.tickers)}', tf='{self.tf}', study[{self.study.total_ta()}]='{self.study.name}'"
if self.data is not None:
s += f", data[{len(self.data.keys())}])"
return s
+107 -61
View File
@@ -23,16 +23,16 @@ from pandas_ta.utils import *
df = pd.DataFrame()
# Strategy DataClass
# Study DataClass
@dataclass
class Strategy:
"""Strategy DataClass
A way to name and group your favorite indicators
class Study:
"""Study DataClass
Class to name and group indicators for processing
Args:
name (str): Some short memorable string. Note: Case-insensitive "All" is reserved.
ta (list of dicts): A list of dicts containing keyword arguments where "kind" is the indicator.
description (str): A more detailed description of what the Strategy tries to capture. Default: None
description (str): A more detailed description of what the Study tries to capture. Default: None
created (str): At datetime string of when it was created. Default: Automatically generated. *Subject to change*
Example TA:
@@ -45,55 +45,52 @@ class Strategy:
{"kind": "sma", "close": "volume", "length": 20, "prefix": "VOLUME"},
]
"""
name: str # = None # Required.
ta: List = field(default_factory=list) # Required.
# Helpful. More descriptive version or notes or w/e.
description: str = ""
# Optional. Gets Exchange Time and Local Time execution time
created: str = get_time(to_string=True)
description: str = "" # Helpful. More descriptive version or notes or w/e.
created: str = get_time(to_string=True) # Optional. Gets Exchange Time and Local Time execution time
def __post_init__(self):
has_name = True
is_ta = False
required_args = ["[X] Strategy requires the following argument(s):"]
req_args = ["[X] Study requires the following argument(s):"]
name_is_str = isinstance(self.name, str)
ta_is_list = isinstance(self.ta, list)
if self.name is None or not name_is_str:
required_args.append(' - name. Must be a string. Example: "My TA". Note: "all" is reserved.')
has_name != has_name
if self._is_name():
req_args.append(' - name. Must be a string. Example: "My TA". Note: "all" is reserved.')
if self.ta is None:
self.ta = None
elif self.ta is not None and ta_is_list and self.total_ta() > 0:
elif not self._is_ta():
s = " - ta. Format is a list of dicts. Example: [{'kind': 'sma', 'length': 10}]"
s += "\n Check the indicator for the correct arguments if you receive this error."
req_args.append(s)
if len(req_args) > 1:
[print(_) for _ in req_args]
return None
def _is_name(self):
return self.name is None or not isinstance(self.name, str)
def _is_ta(self):
if isinstance(self.ta, list) and self.total_ta() > 0:
# Check that all elements of the list are dicts.
# Does not check if the dicts values are valid indicator kwargs
# User must check indicator documentation for all indicators args.
is_ta = all([isinstance(_, dict) and len(_.keys()) > 0 for _ in self.ta])
else:
s = " - ta. Format is a list of dicts. Example: [{'kind': 'sma', 'length': 10}]"
s += "\n Check the indicator for the correct arguments if you receive this error."
required_args.append(s)
if len(required_args) > 1:
[print(_) for _ in required_args]
return None
return all([isinstance(_, dict) and len(_.keys()) > 0 for _ in self.ta])
return False
def total_ta(self):
return len(self.ta) if self.ta is not None else 0
# All Default Strategy
AllStrategy = Strategy(
# All Study
AllStudy = Study(
name="All",
description="All the indicators with their default settings. Pandas TA default.",
ta=None,
)
# Default (Example) Strategy.
CommonStrategy = Strategy(
# Default (Example) Study.
CommonStudy = Study(
name="Common Price and Volume SMAs",
description="Common Price SMAs: 10, 20, 50, 200 and Volume SMA: 20.",
ta=[
@@ -105,6 +102,11 @@ CommonStrategy = Strategy(
]
)
# Temporary Strategy DataClass Alias
Strategy = Study
AllStrategy = AllStudy
CommonStrategy = CommonStudy
# Base Class for extending a Pandas DataFrame
class BasePandasObject(PandasObject):
@@ -119,6 +121,7 @@ class BasePandasObject(PandasObject):
def __init__(self, df, **kwargs):
if df.empty: return
print(f"\n[!] kwargs: {kwargs}\n")
if len(df.columns) > 0:
common_names = {
"Date": "date",
@@ -168,7 +171,7 @@ class AnalysisIndicators(BasePandasObject):
though 'ta' is a Pandas DataFrame Extension, you can still call Technical
Analysis indicators individually if you are more comfortable with that
approach or it allows you to easily and automatically apply the indicators
with the strategy method. See: help(ta.strategy).
with the study method. See: help(ta.study).
By default, the 'ta' extension uses lower case column names: open, high,
low, close, and volume. You can override the defaults by providing the it's
@@ -228,9 +231,9 @@ class AnalysisIndicators(BasePandasObject):
>>> df.ta.apo()
4c. DataFrame Extension (kind): Calling APO using 'kind'
>>> df.ta(kind="apo")
4d. Strategy:
>>> df.ta.strategy("All") # Default
>>> df.ta.strategy(ta.Strategy("My Strat", ta=[{"kind": "apo"}])) # Custom
4d. Study:
>>> df.ta.study("All") # Default
>>> df.ta.study(ta.Study("My Strat", ta=[{"kind": "apo"}])) # Custom
5. Working with kwargs
5a. Append the result to the working df.
@@ -243,6 +246,7 @@ class AnalysisIndicators(BasePandasObject):
_adjusted = None
_cores = cpu_count()
_df = DataFrame()
_ds = "yf"
_exchange = "NYSE"
_time_range = "years"
_last_run = get_time(_exchange, to_string=True)
@@ -314,6 +318,17 @@ class AnalysisIndicators(BasePandasObject):
else:
self._cores = cpus
@property
def ds(self) -> str:
"""Returns the current Data Source. Default: "yf"."""
return self._ds
@ds.setter
def ds(self, value: str) -> None:
"""property: df.ta.ds = "yf" """
if isinstance(value, str) and len(value):
self._ds = value
@property
def exchange(self) -> str:
"""Returns the current Exchange. Default: "NYSE"."""
@@ -487,7 +502,7 @@ class AnalysisIndicators(BasePandasObject):
return result
def _strategy_mode(self, *args) -> tuple:
"""Helper method to determine the mode and name of the strategy. Returns tuple: (name:str, mode:dict)"""
"""Helper method to determine the mode and name of the study. Returns tuple: (name:str, mode:dict)"""
name = "All"
mode = {"all": False, "category": False, "custom": False}
@@ -500,14 +515,14 @@ class AnalysisIndicators(BasePandasObject):
if args[0].lower() in self.categories:
name, mode["category"] = args[0], True
if isinstance(args[0], Strategy):
strategy_ = args[0]
if strategy_.ta is None or strategy_.name.lower() == "all":
if isinstance(args[0], Study):
study_ = args[0]
if study_.ta is None or study_.name.lower() == "all":
name, mode["all"] = name, True
elif strategy_.name.lower() in self.categories:
name, mode["category"] = strategy_.name, True
elif study_.name.lower() in self.categories:
name, mode["category"] = study_.name, True
else:
name, mode["custom"] = strategy_.name, True
name, mode["custom"] = study_.name, True
return name, mode
@@ -564,13 +579,14 @@ class AnalysisIndicators(BasePandasObject):
"""
as_list = kwargs.setdefault("as_list", False)
# Public non-indicator methods
helper_methods = ["constants", "indicators", "strategy"]
helper_methods = ["constants", "indicators", "strategy", "study"]
# Public df.ta.properties
ta_properties = [
"adjusted",
"categories",
"cores",
"datetime_ordered",
"ds",
"exchange",
"last_run",
"reverse",
@@ -638,10 +654,13 @@ class AnalysisIndicators(BasePandasObject):
"performance", "statistics", "trend", "volatility", "volume", or
"all". Default: "all"
ordered (bool): Whether to run "all" in order. Default: True
timed (bool): Show the process time of the strategy().
timed (bool): Show the process time of the study().
Default: False
verbose (bool): Provide some additional insight on the progress of
the strategy() execution. Default: False
the study() execution. Default: False
warning (bool): Disables depreciation message. Automatically
disabled when using it's replacement method: df.ta.study().
Default: True
"""
# If True, it returns the resultant DataFrame. Default: False
returns = kwargs.pop("returns", False)
@@ -650,6 +669,10 @@ class AnalysisIndicators(BasePandasObject):
kwargs["append"] = True
all_ordered = kwargs.pop("ordered", True)
mp_chunksize = kwargs.pop("chunksize", self.cores)
_depwarning = kwargs.pop("warning", True)
if _depwarning:
print(f"\n[!] DEPRECIATION WARNING:\n Use study() instead of strategy().\n")
# Initialize
initial_column_count = len(self._df.columns)
@@ -668,7 +691,7 @@ class AnalysisIndicators(BasePandasObject):
"xsignals",
]
# Get the Strategy Name and mode
# Get the Study Name and mode
name, mode = self._strategy_mode(*args)
# If All or a Category, exclude user list if any
@@ -687,7 +710,7 @@ class AnalysisIndicators(BasePandasObject):
elif mode["all"]:
ta = self.indicators(as_list=True, exclude=excluded)
else:
print(f"[X] Not an available strategy.")
print(f"[X] Not an available study.")
return None
# Remove Custom indicators with "length" keyword when larger than the DataFrame
@@ -701,7 +724,7 @@ class AnalysisIndicators(BasePandasObject):
verbose = kwargs.pop("verbose", False)
if verbose:
print(f"[+] Strategy: {name}\n[i] Indicator arguments: {kwargs}")
print(f"[+] Study: {name}\n[i] Indicator arguments: {kwargs}")
if mode["all"] or mode["category"]:
excluded_str = ", ".join(excluded)
print(f"[i] Excluded[{len(excluded)}]: {excluded_str}")
@@ -764,7 +787,7 @@ class AnalysisIndicators(BasePandasObject):
else:
results = pool.imap_unordered(self._mp_worker, default_ta, _chunksize) # Speed over Order
if results is None:
print(f"[X] ta.strategy('{name}') has no results.")
print(f"[X] ta.study('{name}') has no results.")
return
pool.close()
@@ -812,11 +835,36 @@ class AnalysisIndicators(BasePandasObject):
if returns: return self._df
def study(self, *args, **kwargs):
"""Study Method
An experimental method that by default runs all applicable indicators.
Kwargs:
chunksize (bool): Adjust the chunksize for the Multiprocessing Pool.
Default: Number of cores of the OS
exclude (list): List of indicator names to exclude. Some are
excluded by default for various reasons; they require additional
sources, performance (td_seq), not a time series chart (vp) etc.
name (str): Select all indicators or indicators by
Category such as: "candles", "cycles", "momentum", "overlap",
"performance", "statistics", "trend", "volatility", "volume", or
"all". Default: "all"
ordered (bool): Whether to run "all" in order. Default: True
timed (bool): Show the process time of the study().
Default: False
verbose (bool): Provide some additional insight on the progress of
the study() execution. Default: False
"""
kwargs.update({"warning": False})
return self.strategy(*args, **kwargs)
def ticker(self, ticker: str, ds: str = None, **kwargs):
"""ticker
This method downloads Historical Data if the package yfinance is
installed. Additionally it can run a ta.Strategy; Builtin or Custom. It
installed. Additionally it can run a ta.Study; Builtin or Custom. It
returns a DataFrame if there the DataFrame is not empty, otherwise it
exits. For additional yfinance arguments, use help(ta.yf).
Alternatively, if you have a Polygon API Key, you can use it as well;
@@ -848,27 +896,26 @@ class AnalysisIndicators(BasePandasObject):
ds (str): Options: "polygon" and "yahoo". Default: "yahoo"
Kwargs:
kind (str): Options see above. Default: "history"
strategy (str | ta.Strategy): Which strategy to apply after
study (str | ta.Study): Which study to apply after
downloading chart history. Default: None
See help(ta.yf) for additional kwargs
See help(ta.yf) or help(ta.polygon_api) for additional kwargs
Returns:
Exits if the DataFrame is empty or None
Otherwise it returns a DataFrame
"""
# _frequencies = ["1s", "5s", "15s", "30s", "1m", "5m", "15m", "30m", "45m", "1h", "2h", "4h", "D", "W", "M"]
_ds = "yahoo"
ds = f"{ds.lower()}" if ds is not None and isinstance(ds, str) else _ds
ds = ds.lower() if isinstance(ds, str) else self.ds
strategy = kwargs.pop("strategy", None)
study = kwargs.pop("study", strategy)
if isinstance(ticker, str):
tickers = [ticker]
# Fetch the Data
# Fetch Data
if ds == "polygon":
df = polygon_api(ticker, **kwargs)
elif ds == "yahoo":
elif ds in ["yahoo", "yf"]:
df = yf(ticker, **kwargs)
else: return
@@ -882,8 +929,7 @@ class AnalysisIndicators(BasePandasObject):
df.columns = df.columns.str.lower()
self._df = df
# if strategy is not None: self.strategy(strategy, **kwargs)
if strategy is not None: return self.strategy(strategy, returns=True, **kwargs)
if study is not None: return self.study(study, returns=True, **kwargs)
return df
+1 -1
View File
@@ -15,7 +15,7 @@ def yf(ticker: str, **kwargs):
Other options of 'kind' include:
* All: "all"
- Prints everything below but only returns Chart History to Pandas TA
- Prints everything below but only returns Chart History DataFrame
* Company Information: "info"
* Institutional Holders: "institutional_holders" or "ih"
* Major Holders: "major_holders" or "mh"
+10 -5
View File
@@ -19,7 +19,7 @@ setup(
"pandas_ta.volatility",
"pandas_ta.volume"
],
version=".".join(("0", "3", "44b")),
version=".".join(("0", "3", "45b")),
description=long_description,
long_description=long_description,
author="Kevin Johnson",
@@ -28,7 +28,12 @@ setup(
maintainer="Kevin Johnson",
maintainer_email="appliedmathkj@gmail.com",
download_url="https://github.com/twopirllc/pandas-ta.git",
keywords=["technical analysis", "trading", "python3", "pandas"],
keywords=[
"technical analysis", "trading", "backtest", "trading bot",
"features",
"pandas", "numpy", "vectorbt", "yfinance",
"python3"
],
license="The MIT License (MIT)",
classifiers=[
"Development Status :: 4 - Beta",
@@ -56,9 +61,9 @@ setup(
# $ pip install -e .[dev,test]
extras_require={
"dev": [
"alphaVantage-api", "matplotlib", "mplfinance", "scipy",
"sklearn", "statsmodels", "stochastic",
"talib", "tqdm", "vectorbt", "yfinance",
"alphaVantage-api", "matplotlib", "mplfinance", "polygon"
"scipy", "sklearn", "statsmodels", "stochastic", "ta-lib", "tqdm",
"vectorbt", "yfinance",
],
"test": ["ta-lib"],
},
+37 -35
View File
@@ -11,7 +11,7 @@ from unittest import skip, skipUnless, TestCase
from pandas import DataFrame
# Strategy Testing Parameters
# Testing Parameters
cores = cpu_count() - 1
cumulative = False
speed_table = False
@@ -19,7 +19,7 @@ strategy_timed = False
timed = True
verbose = False
class TestStrategyMethods(TestCase):
class TestStudyMethods(TestCase):
@classmethod
def setUpClass(cls):
@@ -75,47 +75,47 @@ class TestStrategyMethods(TestCase):
# @skip
def test_all(self):
self.category = "All"
self.data.ta.strategy(verbose=verbose, timed=strategy_timed)
self.data.ta.study(verbose=verbose, timed=strategy_timed)
# @skipUnless(verbose, "verbose mode only")
def test_all_multiparams_strategy(self):
def test_all_multiparams_study(self):
self.category = "All"
self.data.ta.strategy(self.category, length=10, verbose=verbose, timed=strategy_timed)
self.data.ta.strategy(self.category, length=50, verbose=verbose, timed=strategy_timed)
self.data.ta.strategy(self.category, fast=5, slow=10, verbose=verbose, timed=strategy_timed)
self.data.ta.study(self.category, length=10, verbose=verbose, timed=strategy_timed)
self.data.ta.study(self.category, length=50, verbose=verbose, timed=strategy_timed)
self.data.ta.study(self.category, fast=5, slow=10, verbose=verbose, timed=strategy_timed)
self.category = "All Multiruns with diff Args" # Rename for Speed Table
@skipUnless(verbose, "verbose mode only")
def test_all_name_strategy(self):
def test_all_name_study(self):
self.category = "All"
self.data.ta.strategy(self.category, verbose=verbose, timed=strategy_timed)
self.data.ta.study(self.category, verbose=verbose, timed=strategy_timed)
# def test_all_no_append(self):
# self.category = "All"
# self.data.ta.strategy(verbose=verbose, timed=strategy_timed)
# self.data.ta.study(verbose=verbose, timed=strategy_timed)
def test_all_ordered(self):
self.category = "All"
self.data.ta.strategy(ordered=True, verbose=verbose, timed=strategy_timed)
self.data.ta.study(ordered=True, verbose=verbose, timed=strategy_timed)
self.category = "All Ordered" # Rename for Speed Table
@skipUnless(verbose, "verbose mode only")
def test_all_strategy(self):
self.data.ta.strategy(pandas_ta.AllStrategy, verbose=verbose, timed=strategy_timed)
def test_all_study(self):
self.data.ta.study(pandas_ta.AllStrategy, verbose=verbose, timed=strategy_timed)
# @skip
def test_candles_category(self):
self.category = "Candles"
self.data.ta.strategy(self.category, verbose=verbose, timed=strategy_timed)
self.data.ta.study(self.category, verbose=verbose, timed=strategy_timed)
# @skip
def test_common(self):
self.category = "Common"
self.data.ta.strategy(pandas_ta.CommonStrategy, verbose=verbose, timed=strategy_timed)
self.data.ta.study(pandas_ta.CommonStrategy, verbose=verbose, timed=strategy_timed)
def test_cycles_category(self):
self.category = "Cycles"
self.data.ta.strategy(self.category, verbose=verbose, timed=strategy_timed)
self.data.ta.study(self.category, verbose=verbose, timed=strategy_timed)
# @skip
def test_custom_a(self):
@@ -133,12 +133,12 @@ class TestStrategyMethods(TestCase):
{"kind": "ema", "close": "CUMLOGRET_1", "length": 5, "suffix": "CLR"} # 1
]
custom = pandas_ta.Strategy(
custom = pandas_ta.Study(
"Commons with Cumulative Log Return EMA Chain", # name
momo_bands_sma_ta, # ta
"Common indicators with specific lengths and a chained indicator", # description
)
self.data.ta.strategy(custom, verbose=verbose, timed=strategy_timed)
self.data.ta.study(custom, verbose=verbose, timed=strategy_timed)
self.assertEqual(len(self.data.columns), 18)
# @skipUnless(verbose, "verbose mode only")
@@ -159,12 +159,14 @@ class TestStrategyMethods(TestCase):
{"kind": "ema", "close": "CUMLOGRET_1", "length": 5, "suffix": "CLR"}
]
custom = pandas_ta.Strategy(
custom = pandas_ta.Study(
"Commons with Cumulative Log Return EMA Chain", # name
momo_bands_sma_ta, # ta
"Common indicators with specific lengths and a chained indicator", # description
)
# Depreciation warning test
self.data.ta.strategy(custom, verbose=verbose, timed=strategy_timed)
# self.data.ta.study(custom, verbose=verbose, timed=strategy_timed)
self.data.ta.cores = cores
# @skip
@@ -176,24 +178,24 @@ class TestStrategyMethods(TestCase):
{"kind": "fisher", "params": (13, 7)}
]
custom = pandas_ta.Strategy(
custom = pandas_ta.Study(
"Custom Args Tuple",
custom_args_ta,
"Allow for easy filling in indicator arguments by argument placement."
)
self.data.ta.strategy(custom, verbose=verbose, timed=strategy_timed)
self.data.ta.study(custom, verbose=verbose, timed=strategy_timed)
def test_custom_col_names_tuple(self):
self.category = "Custom C"
custom_args_ta = [{"kind": "bbands", "col_names": ("LB", "MB", "UB", "BW", "BP")}]
custom = pandas_ta.Strategy(
custom = pandas_ta.Study(
"Custom Col Numbers Tuple",
custom_args_ta,
"Allow for easy renaming of resultant columns",
)
self.data.ta.strategy(custom, verbose=verbose, timed=strategy_timed)
self.data.ta.study(custom, verbose=verbose, timed=strategy_timed)
# @skip
def test_custom_col_numbers_tuple(self):
@@ -201,12 +203,12 @@ class TestStrategyMethods(TestCase):
custom_args_ta = [{"kind": "macd", "col_numbers": (1,)}]
custom = pandas_ta.Strategy(
custom = pandas_ta.Study(
"Custom Col Numbers Tuple",
custom_args_ta,
"Allow for easy selection of resultant columns",
)
self.data.ta.strategy(custom, verbose=verbose, timed=strategy_timed)
self.data.ta.study(custom, verbose=verbose, timed=strategy_timed)
# @skip
def test_custom_e(self):
@@ -218,49 +220,49 @@ class TestStrategyMethods(TestCase):
{"kind": "ema", "close": "CUMLOGRET_1", "length": 5} # 1
]
custom = pandas_ta.Strategy(
custom = pandas_ta.Study(
"AMAT Log Returns", # name
amat_logret_ta, # ta
"AMAT Log Returns", # description
)
self.data.ta.strategy(custom, verbose=verbose, timed=strategy_timed, ordered=True)
self.data.ta.study(custom, verbose=verbose, timed=strategy_timed, ordered=True)
self.data.ta.tsignals(trend=self.data["AMATe_LR_20_50_2"], append=True)
self.assertEqual(len(self.data.columns), 13)
# @skip
def test_momentum_category(self):
self.category = "Momentum"
self.data.ta.strategy(self.category, verbose=verbose, timed=strategy_timed)
self.data.ta.study(self.category, verbose=verbose, timed=strategy_timed)
# @skip
def test_overlap_category(self):
self.category = "Overlap"
self.data.ta.strategy(self.category, verbose=verbose, timed=strategy_timed)
self.data.ta.study(self.category, verbose=verbose, timed=strategy_timed)
# @skip
def test_performance_category(self):
self.category = "Performance"
self.data.ta.strategy(self.category, verbose=verbose, timed=strategy_timed)
self.data.ta.study(self.category, verbose=verbose, timed=strategy_timed)
# @skip
def test_statistics_category(self):
self.category = "Statistics"
self.data.ta.strategy(self.category, verbose=verbose, timed=strategy_timed)
self.data.ta.study(self.category, verbose=verbose, timed=strategy_timed)
# @skip
def test_trend_category(self):
self.category = "Trend"
self.data.ta.strategy(self.category, verbose=verbose, timed=strategy_timed)
self.data.ta.study(self.category, verbose=verbose, timed=strategy_timed)
# @skip
def test_volatility_category(self):
self.category = "Volatility"
self.data.ta.strategy(self.category, verbose=verbose, timed=strategy_timed)
self.data.ta.study(self.category, verbose=verbose, timed=strategy_timed)
# @skip
def test_volume_category(self):
self.category = "Volume"
self.data.ta.strategy(self.category, verbose=verbose, timed=strategy_timed)
self.data.ta.study(self.category, verbose=verbose, timed=strategy_timed)
# @skipUnless(verbose, "verbose mode only")
def test_all_no_multiprocessing(self):
@@ -268,5 +270,5 @@ class TestStrategyMethods(TestCase):
cores = self.data.ta.cores
self.data.ta.cores = 0
self.data.ta.strategy(verbose=verbose, timed=strategy_timed)
self.data.ta.study(verbose=verbose, timed=strategy_timed)
self.data.ta.cores = cores