From b356fca04d537e9ea9530c9dc086b2a1a39c0b44 Mon Sep 17 00:00:00 2001 From: Kevin Johnson Date: Tue, 21 Jul 2020 10:49:33 -0700 Subject: [PATCH] MAINT BBANDS RMA tests ENH Strategy + Watchlist Class + New Notebook --- .gitignore | 3 + README.md | 133 +- examples/PandasTA_Strategy_Examples.ipynb | 3040 +++++++++++++++++++++ examples/watchlist.py | 202 ++ pandas_ta/core.py | 269 +- pandas_ta/overlap/rma.py | 3 +- pandas_ta/utils.py | 3 +- pandas_ta/volatility/bbands.py | 8 +- tests/config.py | 2 +- tests/context.py | 2 +- tests/test_indicator_candle.py | 12 +- tests/test_indicator_candle_ext.py | 4 +- tests/test_indicator_momentum.py | 86 +- tests/test_indicator_momentum_ext.py | 62 +- tests/test_indicator_overlap.py | 74 +- tests/test_indicator_overlap_ext.py | 50 +- tests/test_indicator_performance.py | 22 +- tests/test_indicator_performance_ext.py | 18 +- tests/test_indicator_statistics.py | 30 +- tests/test_indicator_statistics_ext.py | 16 +- tests/test_indicator_trend.py | 44 +- tests/test_indicator_trend_ext.py | 36 +- tests/test_indicator_volatility.py | 42 +- tests/test_indicator_volatility_ext.py | 26 +- tests/test_indicator_volume.py | 40 +- tests/test_indicator_volume_ext.py | 34 +- 26 files changed, 3827 insertions(+), 434 deletions(-) create mode 100644 examples/PandasTA_Strategy_Examples.ipynb create mode 100644 examples/watchlist.py diff --git a/.gitignore b/.gitignore index 1e31be3..dd90ffa 100644 --- a/.gitignore +++ b/.gitignore @@ -119,10 +119,13 @@ pandas_ta/_wrapper.py data/datas.csv data/SPY_5min.csv data/SPY_1min.csv +data/similang-ch.csv data/tulip.csv examples/taplot.py +examples/charting.ipynb examples/ib_trader.ipynb examples/example2.ipynb +examples/*.csv setup.cfg note.md driver.py diff --git a/README.md b/README.md index 6d6b294..5166135 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ All the indicators return a named Series or a DataFrame in uppercase underscore * Has 100+ indicators and utility functions. * Option to use __multiprocessing__ when using df.ta.strategy(). See below. -* Example Jupyter Notebook under the [examples](https://github.com/twopirllc/pandas-ta/tree/master/examples) directory. +* Example Jupyter Notebooks under the [examples](https://github.com/twopirllc/pandas-ta/tree/master/examples) directory, including how to create Custom Strategies using the new [__Strategy__ Class](https://github.com/twopirllc/pandas-ta/tree/master/examples/PandaTA_Strategy_Examples.ipynb) * A new 'ta' method called 'strategy'. By default, it runs __all__ the indicators. * Abbreviated Indicator names as listed below. * __Extended Pandas DataFrame__ as 'ta'. @@ -25,50 +25,12 @@ All the indicators return a named Series or a DataFrame in uppercase underscore ## __Recent Changes__ +* A __Strategy__ Class to help name and group your favorite indicators. +* An experimental and independent __Watchlist__ Class located in the [Examples](https://github.com/twopirllc/pandas-ta/tree/master/examples/watchlist.py) Directory that can be used in conjunction with the new __Strategy__ Class. * Improved the calculation performance of indicators: _Exponential Moving Averagage_ and _Weighted Moving Average_. * Removed internal core optimizations when running ```df.ta.strategy('all')``` with multiprocessing. See the ```ta.strategy()``` method for more details. -### __New DataFrame Method:__ - strategy (strategy) - -### __Added indicators:__ - Bias (bias) - Choppiness Index (chop) - Chande Kroll Stop (cksp) - Doji (cdl_doji) - Entropy (entropy) - Heikin-Ashi Candles (ha) - Inertia (inertia) - KDJ (kdj) - Parabolic Stop and Reverse (psar) - Price Distance (pdist) - Psycholigical Line (psl) - Percentage Volume Oscillator (pvo) - Relative Volatility Index (rvi) - Supertrend (supertrend) - Weighted Closing Price (wcp) -### __Added utilities:__ - Above (above) - Above Value (above_value) - Below (below) - Below Value (below_value) - Cross Value (cross_value) -### __User Added Indicators:__ - Aberration (aberration) - BRAR (brar) -### __Corrected Indicators:__ - Absolute Price Oscillator (apo) - Aroon & Aroon Oscillator (aroon) - * Fixed indicator and included oscillator in returned dataframe - Bollinger Bands (bbands) - Commodity Channel Index (cci) - Chande Momentum Oscillator (cmo) - Exponential Moving Average (ema) - Moving Average Convergence Divergence (macd) - Relative Vigor Index (rvgi) - Symmetric Weighted Moving Average (swma) - Weighted Moving Average (wma) ## What is a Pandas DataFrame Extension? @@ -127,9 +89,63 @@ pd.DataFrame().ta.indicators() help(ta.log_return) ``` -## __New DataFrame Method__: _strategy_ with Multiprocessing +## New Class: __Strategy__ +### What is a Pandas TA Strategy? +A _Strategy_ is a simple way to name and group your favorite TA indicators. Technically, a _Strategy_ is a simple Data Class to contain list of indicators and their parameters. __Note__: _Strategy_ is experimental and subject to change. Pandas TA comes with two basic Strategies: __AllStrategy__ and __CommonStrategy__. -Strategy is a new __Pandas (TA)__ method to facilitate bulk indicator processing. By default, running ```df.ta.strategy()``` will append __all +* See the [Pandas TA Strategy Examples](https://github.com/twopirllc/pandas-ta/tree/master/examples/PandasTA_Strategy_Examples.ipynb) Notebook for more Examples including _Indicator Composition/Chaining_. + +### Strategy 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 + +### Optional Requirements: +- _description_: A more detailed description of what the Strategy tries to capture. Default: None +- _created_: At datetime string of when it was created. Default: Automatically generated. + +#### Things to note: +- A Strategy will __fail__ when consumed by Pandas TA if there is no {"kind": "indicator name"} attribute. __Remember__ to check your spelling. + +#### Brief Examples +```python +# Builtin All Default Strategy +AllStrategy = Strategy( + name="All", + description="All the indicators with their default settings. Pandas TA default.", + ta=None +) + +# Builtin Default (Example) Strategy. +CommonStrategy = Strategy( + name="Common Price and Volume SMAs", + description="Common Price SMAs: 10, 20, 50, 200 and Volume SMA: 20.", + ta=[ + {"kind": "sma", "length": 10}, + {"kind": "sma", "length": 20}, + {"kind": "sma", "length": 50}, + {"kind": "sma", "length": 200}, + {"kind": "sma", "close": "volume", "length": 20, "prefix": "VOL"} + ] +) + +# Your Custom Strategy or whatever your TA composition +CustomStrategy = Strategy( + name="Momo and Volatility", + description="SMA 50,200, BBANDS, RSI, MACD and Volume SMA 20", + ta=[ + {"kind": "sma", "length": 50}, + {"kind": "sma", "length": 200}, + {"kind": "bbands", "length": 20}, + {"kind": "rsi"}, + {"kind": "macd", "fast": 8, "slow": 21}, + {"kind": "sma", "close": "volume", "length": 20, "prefix": "VOLUME"}, + ] +) +``` + +## __DataFrame Method__: _strategy_ with Multiprocessing + +The new __Pandas (TA)__ method __strategy__ is used to facilitate bulk indicator processing. By default, running ```df.ta.strategy()``` will append __all applicable__ indicators to DataFrame ```df```. Utility methods like ```above```, ```below``` et al are not included. * The ```ta.strategy()``` method is still __under development__. Future iterations will allow you to load a ```ta.json``` config file with your specific strategy name and parameters to automatically run you bulk indicators. @@ -171,7 +187,32 @@ df.ta.strategy(fast=10, slow=50, verbose=True) df.columns ``` -## __New DataFrame kwargs__: _prefix_ and _suffix_ +### Running a Custom Strategy +While the _Strategy_ Class it has not been fully integrated with the __strategy__ method yet. For now, the following can be done to implement your Custom Strategy. + +```python +# Create a Strategy +CustomStrategy = Strategy( + name="Momo and Volatility", + description="SMA 50,200, BBANDS, RSI, MACD and Volume SMA 20", + ta=[ + {"kind": "sma", "length": 50}, + {"kind": "sma", "length": 200}, + {"kind": "bbands", "length": 20}, + {"kind": "rsi"}, + {"kind": "macd", "fast": 8, "slow": 21}, + {"kind": "sma", "close": "volume", "length": 20, "prefix": "VOLUME"}, + ] +) + +#Running it requires the name and ta properties +df.ta.strategy(name=CustomStrategy.name, ta=CustomStrategy.ta) + +# Sanity check. Make sure all the columns are there +df.columns +``` + +## __DataFrame kwargs__: _prefix_ and _suffix_ ```python prehl2 = df.ta.hl2(prefix="pre") @@ -184,7 +225,7 @@ bothhl2 = df.ta.hl2(prefix="pre", suffix="post") print(bothhl2.name) # "pre_HL2_post" ``` -## __New DataFrame Properties__: _reverse_ & _datetime_ordered_ +## __DataFrame Properties__: _reverse_ & _datetime_ordered_ ```python # The 'reverse' is a helper property that returns the DataFrame @@ -193,7 +234,7 @@ df = df.ta.reverse # The 'datetime_ordered' property returns True if the DataFrame # index is of Pandas datetime64 and df.index[0] < df.index[-1] -# Otherwise it return False +# Otherwise it returns False time_series_in_order = df.ta.datetime_ordered ``` diff --git a/examples/PandasTA_Strategy_Examples.ipynb b/examples/PandasTA_Strategy_Examples.ipynb new file mode 100644 index 0000000..acd857a --- /dev/null +++ b/examples/PandasTA_Strategy_Examples.ipynb @@ -0,0 +1,3040 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Pandas TA ([pandas_ta](https://github.com/twopirllc/pandas-ta)) Strategies for Custom Technical Analysis\n", + "\n", + "## Topics\n", + "- What is a Pandas TA Strategy?\n", + " - Builtin Strategies: __AllStrategy__ and __CommonStrategy__\n", + " - Creating Strategies\n", + "- Watchlist Class\n", + " - Strategy Management and Execution\n", + "- Indicator Composition/Chaining for more Complex Strategies\n", + " - Comprehensive Example: _MACD and RSI Momo with BBANDS and SMAs 50 & 200 and Cumulative Log Returns_" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Populating the interactive namespace from numpy and matplotlib\n" + ] + } + ], + "source": [ + "%matplotlib inline\n", + "import datetime as dt\n", + "\n", + "# import matplotlib.pyplot as plt\n", + "# import mplfinance as mpf\n", + "\n", + "import pandas as pd\n", + "import pandas_ta as ta\n", + "from alphaVantageAPI.alphavantage import AlphaVantage # pip install alphaVantage-api\n", + "\n", + "from watchlist import Watchlist\n", + "%pylab inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# What is a Pandas TA Strategy?\n", + "A _Strategy_ is a simple way to name and group your favorite TA indicators. Technically, a _Strategy_ is a simple Data Class to contain list of indicators and their parameters. __Note__: _Strategy_ is experimental and subject to change. Pandas TA comes with two basic Strategies: __AllStrategy__ and __CommonStrategy__.\n", + "\n", + "## Strategy Requirements:\n", + "- _name_: Some short memorable string. _Note_: Case-insensitive \"All\" is reserved.\n", + "- _ta_: A list of dicts containing keyword arguments to identify the indicator and the indicator's arguments\n", + "\n", + "## Optional Requirements:\n", + "- _description_: A more detailed description of what the Strategy tries to capture. Default: None\n", + "- _created_: At datetime string of when it was created. Default: Automatically generated.\n", + "\n", + "### Things to note:\n", + "- A Strategy will __fail__ when consumed by Pandas TA if there is no {\"kind\": \"indicator name\"} attribute." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# (Builtin) Examples" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### All" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "name = All\n", + "description = All the indicators with their default settings. Pandas TA default.\n", + "created = 07/21/2020, 10:42:07\n", + "ta = None\n" + ] + } + ], + "source": [ + "AllStrategy = ta.AllStrategy\n", + "print(\"name =\", AllStrategy.name)\n", + "print(\"description =\", AllStrategy.description)\n", + "print(\"created =\", AllStrategy.created)\n", + "print(\"ta =\", AllStrategy.ta)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Common" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "name = Common Price and Volume SMAs\n", + "description = Common Price SMAs: 10, 20, 50, 200 and Volume SMA: 20.\n", + "created = 07/21/2020, 10:42:07\n", + "ta = [{'kind': 'sma', 'length': 10}, {'kind': 'sma', 'length': 20}, {'kind': 'sma', 'length': 50}, {'kind': 'sma', 'length': 200}, {'kind': 'sma', 'close': 'volume', 'length': 20, 'prefix': 'VOL'}]\n" + ] + } + ], + "source": [ + "CommonStrategy = ta.CommonStrategy\n", + "print(\"name =\", CommonStrategy.name)\n", + "print(\"description =\", CommonStrategy.description)\n", + "print(\"created =\", CommonStrategy.created)\n", + "print(\"ta =\", CommonStrategy.ta)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Creating Strategies" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Simple Strategy A" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Strategy(name='A', ta=[{'kind': 'sma', 'length': 50}, {'kind': 'sma', 'length': 200}], description=None, created='07/21/2020, 10:42:07', last_run=None, run_time=None)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "custom_a = ta.Strategy(name=\"A\", ta=[{\"kind\": \"sma\", \"length\": 50}, {\"kind\": \"sma\", \"length\": 200}])\n", + "custom_a" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Simple Strategy B" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Strategy(name='B', ta=[{'kind': 'ema', 'length': 8}, {'kind': 'ema', 'length': 21}, {'kind': 'log_return', 'cumulative': True}, {'kind': 'rsi'}, {'kind': 'supertrend'}], description=None, created='07/21/2020, 10:42:07', last_run=None, run_time=None)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "custom_b = ta.Strategy(name=\"B\", ta=[{\"kind\": \"ema\", \"length\": 8}, {\"kind\": \"ema\", \"length\": 21}, {\"kind\": \"log_return\", \"cumulative\": True}, {\"kind\": \"rsi\"}, {\"kind\": \"supertrend\"}])\n", + "custom_b" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Bad Strategy. (Misspelled Indicator)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Strategy(name='Runtime Failure', ta=[{'kind': 'percet_return'}], description=None, created='07/21/2020, 10:42:07', last_run=None, run_time=None)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Misspelled indicator, will fail later when ran with Pandas\n", + "custom_run_failure = ta.Strategy(name=\"Runtime Failure\", ta=[{\"kind\": \"percet_return\"}])\n", + "custom_run_failure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Strategy Management and Execution with _Watchlist_" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Initialize AlphaVantage Data Source" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AlphaVantage(\n", + " end_point:str = https://www.alphavantage.co/query,\n", + " api_key:str = YOUR API KEY,\n", + " export:bool = True,\n", + " export_path:str = .,\n", + " output_size:str = full,\n", + " output:str = csv,\n", + " datatype:str = json,\n", + " clean:bool = True,\n", + " proxy:dict = {}\n", + ")" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "AV = AlphaVantage(\n", + " api_key=\"YOUR API KEY\", premium=False,\n", + " output_size='full', clean=True,\n", + " export_path=\".\", export=True\n", + ")\n", + "AV" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Create Watchlist and set it's 'ds' to AlphaVantage" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "watch = Watchlist([\"SPY\", \"IWM\"], ds=AV)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Info about the Watchlist. Note, the default Strategy is \"All\"" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Watch(name='Watchlist: SPY, IWM', tickers[2]='SPY, IWM', tf='D', strategy[0]='All')" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "watch" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Help about Watchlist" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on class Watchlist in module watchlist:\n", + "\n", + "class Watchlist(builtins.object)\n", + " | Watchlist(tickers: list, tf: str = None, name: str = None, strategy: pandas_ta.core.Strategy = None, ds: object = None, **kwargs)\n", + " | \n", + " | Watchlist Class (** This is subject to change! **)\n", + " | ============================================================================\n", + " | A simple Class to load/download financial market data and automatically\n", + " | apply Technical Analysis indicators with a Pandas TA Strategy. Default\n", + " | Strategy: pandas_ta.AllStrategy.\n", + " | \n", + " | Requirements:\n", + " | - Pandas TA (pip install pandas_ta)\n", + " | - AlphaVantage (pip install alphaVantage-api) for the Default Data Source.\n", + " | To use another Data Source, update the load() method after AV.\n", + " | \n", + " | Required Arguments:\n", + " | - tickers: A list of strings containing tickers. Example: ['SPY', 'AAPL']\n", + " | ============================================================================\n", + " | \n", + " | Methods defined here:\n", + " | \n", + " | __init__(self, tickers: list, tf: str = None, name: str = None, strategy: pandas_ta.core.Strategy = None, ds: object = None, **kwargs)\n", + " | Initialize self. See help(type(self)) for accurate signature.\n", + " | \n", + " | __repr__(self) -> str\n", + " | Return repr(self).\n", + " | \n", + " | indicators(self, *args, **kwargs) -> \n", + " | Returns the list of indicators that are available with Pandas Ta.\n", + " | \n", + " | load(self, ticker: str = None, tf: str = None, index: str = 'date', drop: list = ['dividend', 'split_coefficient'], file_path: str = '.', **kwargs) -> pandas.core.frame.DataFrame\n", + " | Loads or Downloads (if a local csv does not exist) the data from the\n", + " | Data Source. When successful, it returns a Data Frame for the requested\n", + " | ticker. If no tickers are given, it loads all the tickers.\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Data descriptors defined here:\n", + " | \n", + " | __dict__\n", + " | dictionary for instance variables (if defined)\n", + " | \n", + " | __weakref__\n", + " | list of weak references to the object (if defined)\n", + " | \n", + " | data\n", + " | When not None, it contains a dictionary of DataFrames keyed by ticker.\n", + " | \n", + " | name\n", + " | The name of the Watchlist. Default: \"Watchlist: {Watchlist.tickers}\".\n", + " | \n", + " | strategy\n", + " | Pandas TA Strategy Class. Default: pandas_ta.AllStrategy\n", + " | \n", + " | tf\n", + " | Alias for timeframe. Default: 'D'\n", + " | \n", + " | tickers\n", + " | tickers\n", + " | \n", + " | If a string, it it converted to a list. Example: 'AAPL' -> ['AAPL']\n", + " | * Does not accept, comma seperated strings.\n", + " | If a list, checks if it is a list of strings.\n", + " | \n", + " | verbose\n", + " | Toggle the verbose property. Default: False\n", + "\n" + ] + } + ], + "source": [ + "help(Watchlist)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Default Strategy is \"All\"" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[!] Loading All: SPY, IWM\n", + "\n", + "[+] Downloading['D']: SPY\n", + "strat.kwargs: {'ta': None, 'verbose': True, 'timed': False}\n", + "[+] Strategy \"All\"\n", + "[i] Indicators with the following arguments: {'append': True}\n", + "[i] Excluded[10]: above, above_value, below, below_value, cross, cross_value, long_run, short_run, trend_return, vp\n", + "[i] Total indicators: 101\n", + "[i] Columns added: 152\n", + "\n", + "[+] Downloading['D']: IWM\n", + "strat.kwargs: {'ta': None, 'verbose': True, 'timed': False}\n", + "[+] Strategy \"All\"\n", + "[i] Indicators with the following arguments: {'append': True}\n", + "[i] Excluded[10]: above, above_value, below, below_value, cross, cross_value, long_run, short_run, trend_return, vp\n", + "[i] Total indicators: 101\n", + "[i] Columns added: 152\n" + ] + } + ], + "source": [ + "# No arguments loads all the tickers and applies the Strategy to each ticker.\n", + "# The result can be accessed with Watchlist's 'data' property which returns a dictionary keyed by ticker and DataFrames as values \n", + "watch.load(verbose=True, timed=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'SPY': open high low close volume ABER_ZG_5_15 \\\n", + " date \n", + " 1999-11-01 136.5000 137.0000 135.5625 135.5625 4006500.0 NaN \n", + " 1999-11-02 135.9687 137.2500 134.5937 134.5937 6516900.0 NaN \n", + " 1999-11-03 136.0000 136.3750 135.1250 135.5000 7222300.0 NaN \n", + " 1999-11-04 136.7500 137.3593 135.7656 136.5312 7907500.0 NaN \n", + " 1999-11-05 138.6250 139.1093 136.7812 137.8750 7431500.0 136.332267 \n", + " ... ... ... ... ... ... ... \n", + " 2020-07-14 313.3000 319.7600 312.0000 318.9200 92791800.0 315.862000 \n", + " 2020-07-15 322.4100 323.0400 319.2700 321.8500 86921500.0 317.127333 \n", + " 2020-07-16 319.7900 321.2800 319.0900 320.7900 54433400.0 318.394000 \n", + " 2020-07-17 321.8800 322.5700 319.7400 321.7200 64421800.0 319.447333 \n", + " 2020-07-20 321.4300 325.1300 320.6200 324.2700 51616200.0 320.670000 \n", + " \n", + " ABER_SG_5_15 ABER_XG_5_15 ABER_ATR_5_15 ACCBL_20 ... \\\n", + " date ... \n", + " 1999-11-01 NaN NaN NaN NaN ... \n", + " 1999-11-02 NaN NaN NaN NaN ... \n", + " 1999-11-03 NaN NaN NaN NaN ... \n", + " 1999-11-04 NaN NaN NaN NaN ... \n", + " 1999-11-05 NaN NaN NaN NaN ... \n", + " ... ... ... ... ... ... \n", + " 2020-07-14 322.137987 309.586013 6.275987 297.831277 ... \n", + " 2020-07-15 323.259587 310.995079 6.132254 298.823299 ... \n", + " 2020-07-16 324.301437 312.486563 5.907437 299.367555 ... \n", + " 2020-07-17 325.149608 313.745059 5.702275 299.875050 ... \n", + " 2020-07-20 326.292790 315.047210 5.622790 300.906776 ... \n", + " \n", + " VAR_30 VTXP_14 VTXM_14 VWAP VWMA_10 WCP \\\n", + " date \n", + " 1999-11-01 NaN NaN NaN 136.041667 NaN 135.921875 \n", + " 1999-11-02 NaN NaN NaN 135.693303 NaN 135.257775 \n", + " 1999-11-03 NaN NaN NaN 135.682462 NaN 135.625000 \n", + " 1999-11-04 NaN NaN NaN 135.950504 NaN 136.546825 \n", + " 1999-11-05 NaN NaN NaN 136.393305 NaN 137.910125 \n", + " ... ... ... ... ... ... ... \n", + " 2020-07-14 34.050046 0.942619 0.851435 153.185166 314.416051 317.400000 \n", + " 2020-07-15 36.858968 1.134936 0.764889 153.210321 315.765307 321.502500 \n", + " 2020-07-16 39.296390 1.142186 0.731933 153.225976 316.788363 320.487500 \n", + " 2020-07-17 42.042623 1.200811 0.690749 153.244606 317.735725 321.437500 \n", + " 2020-07-20 45.052926 1.223611 0.623952 153.259707 318.469407 323.572500 \n", + " \n", + " WILLR_14 WMA_10 ZL_EMA_10 Z_30 \n", + " date \n", + " 1999-11-01 NaN NaN NaN NaN \n", + " 1999-11-02 NaN NaN NaN NaN \n", + " 1999-11-03 NaN NaN NaN NaN \n", + " 1999-11-04 NaN NaN NaN NaN \n", + " 1999-11-05 NaN NaN NaN NaN \n", + " ... ... ... ... ... \n", + " 2020-07-14 -15.937763 315.716545 317.221127 1.224000 \n", + " 2020-07-15 -4.935711 317.073818 319.420922 1.583443 \n", + " 2020-07-16 -9.332227 317.993091 320.251663 1.318671 \n", + " 2020-07-17 -5.474907 318.894727 321.769543 1.365046 \n", + " 2020-07-20 -4.035664 320.087455 323.196899 1.674079 \n", + " \n", + " [5212 rows x 157 columns],\n", + " 'IWM': open high low close volume ABER_ZG_5_15 \\\n", + " date \n", + " 2000-05-26 91.06 91.44 90.63 91.44 37400.0 NaN \n", + " 2000-05-30 92.75 94.81 92.75 94.81 28800.0 NaN \n", + " 2000-05-31 95.13 96.38 95.13 95.75 18000.0 NaN \n", + " 2000-06-01 97.11 97.31 97.11 97.31 3500.0 NaN \n", + " 2000-06-02 101.70 102.40 101.70 102.40 14700.0 96.091333 \n", + " ... ... ... ... ... ... ... \n", + " 2020-07-14 139.43 141.98 138.64 141.83 27919300.0 140.631333 \n", + " 2020-07-15 145.42 147.78 144.79 147.03 40619500.0 141.702000 \n", + " 2020-07-16 146.11 146.67 144.88 146.16 30386200.0 142.972000 \n", + " 2020-07-17 146.68 147.59 145.51 146.59 20632700.0 144.200000 \n", + " 2020-07-20 146.12 146.85 145.15 145.91 18335300.0 145.157333 \n", + " \n", + " ABER_SG_5_15 ABER_XG_5_15 ABER_ATR_5_15 ACCBL_20 ... \\\n", + " date ... \n", + " 2000-05-26 NaN NaN NaN NaN ... \n", + " 2000-05-30 NaN NaN NaN NaN ... \n", + " 2000-05-31 NaN NaN NaN NaN ... \n", + " 2000-06-01 NaN NaN NaN NaN ... \n", + " 2000-06-02 NaN NaN NaN NaN ... \n", + " ... ... ... ... ... ... \n", + " 2020-07-14 144.933519 136.329148 4.302186 131.931151 ... \n", + " 2020-07-15 146.114040 137.289960 4.412040 132.378072 ... \n", + " 2020-07-16 147.233237 138.710763 4.261237 132.740467 ... \n", + " 2020-07-17 148.315822 140.084178 4.115822 133.164616 ... \n", + " 2020-07-20 149.112100 141.202567 3.954767 133.720436 ... \n", + " \n", + " VAR_30 VTXP_14 VTXM_14 VWAP VWMA_10 WCP \\\n", + " date \n", + " 2000-05-26 NaN NaN NaN 91.170000 NaN 91.2375 \n", + " 2000-05-30 NaN NaN NaN 92.454834 NaN 94.2950 \n", + " 2000-05-31 NaN NaN NaN 93.159976 NaN 95.7525 \n", + " 2000-06-01 NaN NaN NaN 93.322938 NaN 97.2600 \n", + " 2000-06-02 NaN NaN NaN 94.592497 NaN 102.2250 \n", + " ... ... ... ... ... ... ... \n", + " 2020-07-14 14.580918 0.912088 0.993442 88.330119 141.551704 141.0700 \n", + " 2020-07-15 15.260700 1.069504 0.907148 88.343103 141.954972 146.6575 \n", + " 2020-07-16 15.532794 1.067613 0.898394 88.352707 142.400151 145.9675 \n", + " 2020-07-17 15.940380 1.090399 0.874371 88.359302 142.830341 146.5700 \n", + " 2020-07-20 14.336087 1.085970 0.934221 88.365101 143.084383 145.9550 \n", + " \n", + " WILLR_14 WMA_10 ZL_EMA_10 Z_30 \n", + " date \n", + " 2000-05-26 NaN NaN NaN NaN \n", + " 2000-05-30 NaN NaN NaN NaN \n", + " 2000-05-31 NaN NaN NaN NaN \n", + " 2000-06-01 NaN NaN NaN NaN \n", + " 2000-06-02 NaN NaN NaN NaN \n", + " ... ... ... ... ... \n", + " 2020-07-14 -41.126461 141.123273 140.481012 -0.140980 \n", + " 2020-07-15 -6.527415 142.121636 143.119010 1.145187 \n", + " 2020-07-16 -14.348981 142.891818 144.553735 0.902863 \n", + " 2020-07-17 -11.142322 143.661273 146.209420 0.982248 \n", + " 2020-07-20 -17.741935 144.230909 146.896798 0.893748 \n", + " \n", + " [5068 rows x 157 columns]}" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "watch.data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### " + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
openhighlowclosevolumeABER_ZG_5_15ABER_SG_5_15ABER_XG_5_15ABER_ATR_5_15ACCBL_20...VAR_30VTXP_14VTXM_14VWAPVWMA_10WCPWILLR_14WMA_10ZL_EMA_10Z_30
date
1999-11-01136.5000137.0000135.5625135.56254006500.0NaNNaNNaNNaNNaN...NaNNaNNaN136.041667NaN135.921875NaNNaNNaNNaN
1999-11-02135.9687137.2500134.5937134.59376516900.0NaNNaNNaNNaNNaN...NaNNaNNaN135.693303NaN135.257775NaNNaNNaNNaN
1999-11-03136.0000136.3750135.1250135.50007222300.0NaNNaNNaNNaNNaN...NaNNaNNaN135.682462NaN135.625000NaNNaNNaNNaN
1999-11-04136.7500137.3593135.7656136.53127907500.0NaNNaNNaNNaNNaN...NaNNaNNaN135.950504NaN136.546825NaNNaNNaNNaN
1999-11-05138.6250139.1093136.7812137.87507431500.0136.332267NaNNaNNaNNaN...NaNNaNNaN136.393305NaN137.910125NaNNaNNaNNaN
..................................................................
2020-07-14313.3000319.7600312.0000318.920092791800.0315.862000322.137987309.5860136.275987297.831277...34.0500460.9426190.851435153.185166314.416051317.400000-15.937763315.716545317.2211271.224000
2020-07-15322.4100323.0400319.2700321.850086921500.0317.127333323.259587310.9950796.132254298.823299...36.8589681.1349360.764889153.210321315.765307321.502500-4.935711317.073818319.4209221.583443
2020-07-16319.7900321.2800319.0900320.790054433400.0318.394000324.301437312.4865635.907437299.367555...39.2963901.1421860.731933153.225976316.788363320.487500-9.332227317.993091320.2516631.318671
2020-07-17321.8800322.5700319.7400321.720064421800.0319.447333325.149608313.7450595.702275299.875050...42.0426231.2008110.690749153.244606317.735725321.437500-5.474907318.894727321.7695431.365046
2020-07-20321.4300325.1300320.6200324.270051616200.0320.670000326.292790315.0472105.622790300.906776...45.0529261.2236110.623952153.259707318.469407323.572500-4.035664320.087455323.1968991.674079
\n", + "

5212 rows × 157 columns

\n", + "
" + ], + "text/plain": [ + " open high low close volume ABER_ZG_5_15 \\\n", + "date \n", + "1999-11-01 136.5000 137.0000 135.5625 135.5625 4006500.0 NaN \n", + "1999-11-02 135.9687 137.2500 134.5937 134.5937 6516900.0 NaN \n", + "1999-11-03 136.0000 136.3750 135.1250 135.5000 7222300.0 NaN \n", + "1999-11-04 136.7500 137.3593 135.7656 136.5312 7907500.0 NaN \n", + "1999-11-05 138.6250 139.1093 136.7812 137.8750 7431500.0 136.332267 \n", + "... ... ... ... ... ... ... \n", + "2020-07-14 313.3000 319.7600 312.0000 318.9200 92791800.0 315.862000 \n", + "2020-07-15 322.4100 323.0400 319.2700 321.8500 86921500.0 317.127333 \n", + "2020-07-16 319.7900 321.2800 319.0900 320.7900 54433400.0 318.394000 \n", + "2020-07-17 321.8800 322.5700 319.7400 321.7200 64421800.0 319.447333 \n", + "2020-07-20 321.4300 325.1300 320.6200 324.2700 51616200.0 320.670000 \n", + "\n", + " ABER_SG_5_15 ABER_XG_5_15 ABER_ATR_5_15 ACCBL_20 ... \\\n", + "date ... \n", + "1999-11-01 NaN NaN NaN NaN ... \n", + "1999-11-02 NaN NaN NaN NaN ... \n", + "1999-11-03 NaN NaN NaN NaN ... \n", + "1999-11-04 NaN NaN NaN NaN ... \n", + "1999-11-05 NaN NaN NaN NaN ... \n", + "... ... ... ... ... ... \n", + "2020-07-14 322.137987 309.586013 6.275987 297.831277 ... \n", + "2020-07-15 323.259587 310.995079 6.132254 298.823299 ... \n", + "2020-07-16 324.301437 312.486563 5.907437 299.367555 ... \n", + "2020-07-17 325.149608 313.745059 5.702275 299.875050 ... \n", + "2020-07-20 326.292790 315.047210 5.622790 300.906776 ... \n", + "\n", + " VAR_30 VTXP_14 VTXM_14 VWAP VWMA_10 WCP \\\n", + "date \n", + "1999-11-01 NaN NaN NaN 136.041667 NaN 135.921875 \n", + "1999-11-02 NaN NaN NaN 135.693303 NaN 135.257775 \n", + "1999-11-03 NaN NaN NaN 135.682462 NaN 135.625000 \n", + "1999-11-04 NaN NaN NaN 135.950504 NaN 136.546825 \n", + "1999-11-05 NaN NaN NaN 136.393305 NaN 137.910125 \n", + "... ... ... ... ... ... ... \n", + "2020-07-14 34.050046 0.942619 0.851435 153.185166 314.416051 317.400000 \n", + "2020-07-15 36.858968 1.134936 0.764889 153.210321 315.765307 321.502500 \n", + "2020-07-16 39.296390 1.142186 0.731933 153.225976 316.788363 320.487500 \n", + "2020-07-17 42.042623 1.200811 0.690749 153.244606 317.735725 321.437500 \n", + "2020-07-20 45.052926 1.223611 0.623952 153.259707 318.469407 323.572500 \n", + "\n", + " WILLR_14 WMA_10 ZL_EMA_10 Z_30 \n", + "date \n", + "1999-11-01 NaN NaN NaN NaN \n", + "1999-11-02 NaN NaN NaN NaN \n", + "1999-11-03 NaN NaN NaN NaN \n", + "1999-11-04 NaN NaN NaN NaN \n", + "1999-11-05 NaN NaN NaN NaN \n", + "... ... ... ... ... \n", + "2020-07-14 -15.937763 315.716545 317.221127 1.224000 \n", + "2020-07-15 -4.935711 317.073818 319.420922 1.583443 \n", + "2020-07-16 -9.332227 317.993091 320.251663 1.318671 \n", + "2020-07-17 -5.474907 318.894727 321.769543 1.365046 \n", + "2020-07-20 -4.035664 320.087455 323.196899 1.674079 \n", + "\n", + "[5212 rows x 157 columns]" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "watch.data['SPY']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Easy to swap Strategies and run them" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Running Simple Strategy A" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Strategy(name='A', ta=[{'kind': 'sma', 'length': 50}, {'kind': 'sma', 'length': 200}], description=None, created='07/21/2020, 10:42:07', last_run=None, run_time=None)" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load custom_a into Watchlist and verify\n", + "watch.strategy = custom_a\n", + "watch.strategy" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "[i] Loaded['D']: IWM_D.csv\n", + "strat.kwargs: {'ta': [{'kind': 'sma', 'length': 50}, {'kind': 'sma', 'length': 200}]}\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
openhighlowclosevolumeSMA_50SMA_200
date
2000-05-2691.0691.4490.6391.4437400.0NaNNaN
2000-05-3092.7594.8192.7594.8128800.0NaNNaN
2000-05-3195.1396.3895.1395.7518000.0NaNNaN
2000-06-0197.1197.3197.1197.313500.0NaNNaN
2000-06-02101.70102.40101.70102.4014700.0NaNNaN
........................
2020-07-14139.43141.98138.64141.8327919300.0138.1012145.99795
2020-07-15145.42147.78144.79147.0340619500.0138.5282145.97730
2020-07-16146.11146.67144.88146.1630386200.0138.9184145.95140
2020-07-17146.68147.59145.51146.5920632700.0139.3358145.94250
2020-07-20146.12146.85145.15145.9118335300.0139.7052145.93580
\n", + "

5068 rows × 7 columns

\n", + "
" + ], + "text/plain": [ + " open high low close volume SMA_50 SMA_200\n", + "date \n", + "2000-05-26 91.06 91.44 90.63 91.44 37400.0 NaN NaN\n", + "2000-05-30 92.75 94.81 92.75 94.81 28800.0 NaN NaN\n", + "2000-05-31 95.13 96.38 95.13 95.75 18000.0 NaN NaN\n", + "2000-06-01 97.11 97.31 97.11 97.31 3500.0 NaN NaN\n", + "2000-06-02 101.70 102.40 101.70 102.40 14700.0 NaN NaN\n", + "... ... ... ... ... ... ... ...\n", + "2020-07-14 139.43 141.98 138.64 141.83 27919300.0 138.1012 145.99795\n", + "2020-07-15 145.42 147.78 144.79 147.03 40619500.0 138.5282 145.97730\n", + "2020-07-16 146.11 146.67 144.88 146.16 30386200.0 138.9184 145.95140\n", + "2020-07-17 146.68 147.59 145.51 146.59 20632700.0 139.3358 145.94250\n", + "2020-07-20 146.12 146.85 145.15 145.91 18335300.0 139.7052 145.93580\n", + "\n", + "[5068 rows x 7 columns]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "watch.load('IWM')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Running Simple Strategy B" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Strategy(name='B', ta=[{'kind': 'ema', 'length': 8}, {'kind': 'ema', 'length': 21}, {'kind': 'log_return', 'cumulative': True}, {'kind': 'rsi'}, {'kind': 'supertrend'}], description=None, created='07/21/2020, 10:42:07', last_run=None, run_time=None)" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load custom_b into Watchlist and verify\n", + "watch.strategy = custom_b\n", + "watch.strategy" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "[i] Loaded['D']: SPY_D.csv\n", + "strat.kwargs: {'ta': [{'kind': 'ema', 'length': 8}, {'kind': 'ema', 'length': 21}, {'kind': 'log_return', 'cumulative': True}, {'kind': 'rsi'}, {'kind': 'supertrend'}]}\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
openhighlowclosevolumeEMA_8EMA_21CUMLOGRET_1RSI_14SUPERT_7_3.0SUPERTd_7_3.0SUPERTl_7_3.0SUPERTs_7_3.0
date
1999-11-01136.5000137.0000135.5625135.56254006500.0NaNNaNNaNNaN0.0000000NaNNaN
1999-11-02135.9687137.2500134.5937134.59376516900.0NaNNaN-0.0071720.000000NaN0NaNNaN
1999-11-03136.0000136.3750135.1250135.50007222300.0NaNNaN-0.0004616.263520NaN0NaNNaN
1999-11-04136.7500137.3593135.7656136.53127907500.0NaNNaN0.00712012.913259NaN0NaNNaN
1999-11-05138.6250139.1093136.7812137.87507431500.0NaNNaN0.01691520.761745NaN0NaNNaN
..........................................
2020-07-14313.3000319.7600312.0000318.920092791800.0315.153455311.7364180.85550759.173765321.864382-1NaN321.864382
2020-07-15322.4100323.0400319.2700321.850086921500.0316.641576312.6558350.86465361.428658321.864382-1NaN321.864382
2020-07-16319.7900321.2800319.0900320.790054433400.0317.563448313.3953040.86135460.141122321.864382-1NaN321.864382
2020-07-17321.8800322.5700319.7400321.720064421800.0318.487126314.1520950.86424960.911280321.864382-1NaN321.864382
2020-07-20321.4300325.1300320.6200324.270051616200.0319.772209315.0719050.87214463.010941307.8904391307.890439NaN
\n", + "

5212 rows × 13 columns

\n", + "
" + ], + "text/plain": [ + " open high low close volume EMA_8 \\\n", + "date \n", + "1999-11-01 136.5000 137.0000 135.5625 135.5625 4006500.0 NaN \n", + "1999-11-02 135.9687 137.2500 134.5937 134.5937 6516900.0 NaN \n", + "1999-11-03 136.0000 136.3750 135.1250 135.5000 7222300.0 NaN \n", + "1999-11-04 136.7500 137.3593 135.7656 136.5312 7907500.0 NaN \n", + "1999-11-05 138.6250 139.1093 136.7812 137.8750 7431500.0 NaN \n", + "... ... ... ... ... ... ... \n", + "2020-07-14 313.3000 319.7600 312.0000 318.9200 92791800.0 315.153455 \n", + "2020-07-15 322.4100 323.0400 319.2700 321.8500 86921500.0 316.641576 \n", + "2020-07-16 319.7900 321.2800 319.0900 320.7900 54433400.0 317.563448 \n", + "2020-07-17 321.8800 322.5700 319.7400 321.7200 64421800.0 318.487126 \n", + "2020-07-20 321.4300 325.1300 320.6200 324.2700 51616200.0 319.772209 \n", + "\n", + " EMA_21 CUMLOGRET_1 RSI_14 SUPERT_7_3.0 SUPERTd_7_3.0 \\\n", + "date \n", + "1999-11-01 NaN NaN NaN 0.000000 0 \n", + "1999-11-02 NaN -0.007172 0.000000 NaN 0 \n", + "1999-11-03 NaN -0.000461 6.263520 NaN 0 \n", + "1999-11-04 NaN 0.007120 12.913259 NaN 0 \n", + "1999-11-05 NaN 0.016915 20.761745 NaN 0 \n", + "... ... ... ... ... ... \n", + "2020-07-14 311.736418 0.855507 59.173765 321.864382 -1 \n", + "2020-07-15 312.655835 0.864653 61.428658 321.864382 -1 \n", + "2020-07-16 313.395304 0.861354 60.141122 321.864382 -1 \n", + "2020-07-17 314.152095 0.864249 60.911280 321.864382 -1 \n", + "2020-07-20 315.071905 0.872144 63.010941 307.890439 1 \n", + "\n", + " SUPERTl_7_3.0 SUPERTs_7_3.0 \n", + "date \n", + "1999-11-01 NaN NaN \n", + "1999-11-02 NaN NaN \n", + "1999-11-03 NaN NaN \n", + "1999-11-04 NaN NaN \n", + "1999-11-05 NaN NaN \n", + "... ... ... \n", + "2020-07-14 NaN 321.864382 \n", + "2020-07-15 NaN 321.864382 \n", + "2020-07-16 NaN 321.864382 \n", + "2020-07-17 NaN 321.864382 \n", + "2020-07-20 307.890439 NaN \n", + "\n", + "[5212 rows x 13 columns]" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "watch.load('SPY')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Running Bad Strategy. (Misspelled indicator)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Strategy(name='Runtime Failure', ta=[{'kind': 'percet_return'}], description=None, created='07/21/2020, 10:42:07', last_run=None, run_time=None)" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load custom_run_failure into Watchlist and verify\n", + "watch.strategy = custom_run_failure\n", + "watch.strategy" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "[i] Loaded['D']: IWM_D.csv\n", + "strat.kwargs: {'ta': [{'kind': 'percet_return'}]}\n", + "[X] Oops! 'AnalysisIndicators' object has no attribute 'percet_return'\n" + ] + } + ], + "source": [ + "try:\n", + " iwm = watch.load('IWM')\n", + "except AttributeError as error:\n", + " print(f\"[X] Oops! {error}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Indicator Composition/Chaining\n", + "- When you need an indicator to depend on the value of a prior indicator\n", + "- Utilitze _prefix_ or _suffix_ to help identify unique columns or avoid column name clashes." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Volume MAs and MA chains" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Strategy(name='Volume MAs and Price MA chain', ta=[{'kind': 'ema', 'close': 'volume', 'length': 10, 'prefix': 'VOLUME'}, {'kind': 'sma', 'close': 'volume', 'length': 20, 'prefix': 'VOLUME'}, {'kind': 'ema', 'length': 5}, {'kind': 'linreg', 'close': 'EMA_5', 'length': 8, 'prefix': 'EMA_5'}], description=None, created='07/21/2020, 10:42:07', last_run=None, run_time=None)" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Set EMA's and SMA's 'close' to 'volume' to create Volume MAs, prefix 'volume' MAs with 'VOLUME' so easy to identify the column\n", + "# Take a price EMA and apply LINREG from EMA's output\n", + "volmas_price_ma_chain = [\n", + " {\"kind\":\"ema\", \"close\": \"volume\", \"length\": 10, \"prefix\": \"VOLUME\"},\n", + " {\"kind\":\"sma\", \"close\": \"volume\", \"length\": 20, \"prefix\": \"VOLUME\"},\n", + " {\"kind\":\"ema\", \"length\": 5},\n", + " {\"kind\":\"linreg\", \"close\": \"EMA_5\", \"length\": 8, \"prefix\": \"EMA_5\"},\n", + "]\n", + "vp_ma_chain_ta = ta.Strategy(\"Volume MAs and Price MA chain\", volmas_price_ma_chain)\n", + "vp_ma_chain_ta" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Volume MAs and Price MA chain'" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Update the Watchlist\n", + "watch.strategy = vp_ma_chain_ta\n", + "watch.strategy.name" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "[i] Loaded['D']: SPY_D.csv\n", + "strat.kwargs: {'ta': [{'kind': 'ema', 'close': 'volume', 'length': 10, 'prefix': 'VOLUME'}, {'kind': 'sma', 'close': 'volume', 'length': 20, 'prefix': 'VOLUME'}, {'kind': 'ema', 'length': 5}, {'kind': 'linreg', 'close': 'EMA_5', 'length': 8, 'prefix': 'EMA_5'}]}\n", + "[i] Set 'df.ta.mp = True' to enable multiprocessing. This computer has 4 cores. Default: False\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
openhighlowclosevolumeVOLUME_EMA_10VOLUME_SMA_20EMA_5EMA_5_LR_8
date
1999-11-01136.5000137.0000135.5625135.56254006500.0NaNNaNNaNNaN
1999-11-02135.9687137.2500134.5937134.59376516900.0NaNNaNNaNNaN
1999-11-03136.0000136.3750135.1250135.50007222300.0NaNNaNNaNNaN
1999-11-04136.7500137.3593135.7656136.53127907500.0NaNNaNNaNNaN
1999-11-05138.6250139.1093136.7812137.87507431500.0NaNNaN136.012480NaN
..............................
2020-07-14313.3000319.7600312.0000318.920092791800.08.361704e+0789676222.80316.336980315.670761
2020-07-15322.4100323.0400319.2700321.850086921500.08.421785e+0787169880.45318.174653316.635348
2020-07-16319.7900321.2800319.0900320.790054433400.07.880249e+0785750820.85319.046435317.729875
2020-07-17321.8800322.5700319.7400321.720064421800.07.618782e+0784954118.65319.937624318.784441
2020-07-20321.4300325.1300320.6200324.270051616200.07.172025e+0780774361.40321.381749320.052445
\n", + "

5212 rows × 9 columns

\n", + "
" + ], + "text/plain": [ + " open high low close volume VOLUME_EMA_10 \\\n", + "date \n", + "1999-11-01 136.5000 137.0000 135.5625 135.5625 4006500.0 NaN \n", + "1999-11-02 135.9687 137.2500 134.5937 134.5937 6516900.0 NaN \n", + "1999-11-03 136.0000 136.3750 135.1250 135.5000 7222300.0 NaN \n", + "1999-11-04 136.7500 137.3593 135.7656 136.5312 7907500.0 NaN \n", + "1999-11-05 138.6250 139.1093 136.7812 137.8750 7431500.0 NaN \n", + "... ... ... ... ... ... ... \n", + "2020-07-14 313.3000 319.7600 312.0000 318.9200 92791800.0 8.361704e+07 \n", + "2020-07-15 322.4100 323.0400 319.2700 321.8500 86921500.0 8.421785e+07 \n", + "2020-07-16 319.7900 321.2800 319.0900 320.7900 54433400.0 7.880249e+07 \n", + "2020-07-17 321.8800 322.5700 319.7400 321.7200 64421800.0 7.618782e+07 \n", + "2020-07-20 321.4300 325.1300 320.6200 324.2700 51616200.0 7.172025e+07 \n", + "\n", + " VOLUME_SMA_20 EMA_5 EMA_5_LR_8 \n", + "date \n", + "1999-11-01 NaN NaN NaN \n", + "1999-11-02 NaN NaN NaN \n", + "1999-11-03 NaN NaN NaN \n", + "1999-11-04 NaN NaN NaN \n", + "1999-11-05 NaN 136.012480 NaN \n", + "... ... ... ... \n", + "2020-07-14 89676222.80 316.336980 315.670761 \n", + "2020-07-15 87169880.45 318.174653 316.635348 \n", + "2020-07-16 85750820.85 319.046435 317.729875 \n", + "2020-07-17 84954118.65 319.937624 318.784441 \n", + "2020-07-20 80774361.40 321.381749 320.052445 \n", + "\n", + "[5212 rows x 9 columns]" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "spy = watch.load('SPY')\n", + "spy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### MACD BBANDS" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Strategy(name='MACD BBands', ta=[{'kind': 'macd'}, {'kind': 'bbands', 'close': 'MACD_12_26_9', 'length': 20, 'prefix': 'MACD'}], description='BBANDS_20 applied to MACD', created='07/21/2020, 10:42:07', last_run=None, run_time=None)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# MACD is the initial indicator that BBANDS depends on.\n", + "# Set BBANDS's 'close' to MACD's main signal, in this case 'MACD_12_26_9' and add a prefix (or suffix) so it's easier to identify\n", + "macd_bands_ta = [\n", + " {\"kind\":\"macd\"},\n", + " {\"kind\":\"bbands\", \"close\": \"MACD_12_26_9\", \"length\": 20, \"prefix\": \"MACD\"}\n", + "]\n", + "macd_bands_ta = ta.Strategy(\"MACD BBands\", macd_bands_ta, f\"BBANDS_{macd_bands_ta[1]['length']} applied to MACD\")\n", + "macd_bands_ta" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'MACD BBands'" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Update the Watchlist\n", + "watch.strategy = macd_bands_ta\n", + "watch.strategy.name" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "[i] Loaded['D']: SPY_D.csv\n", + "strat.kwargs: {'ta': [{'kind': 'macd'}, {'kind': 'bbands', 'close': 'MACD_12_26_9', 'length': 20, 'prefix': 'MACD'}]}\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
openhighlowclosevolumeMACD_12_26_9MACDh_12_26_9MACDs_12_26_9MACD_BBL_20_2.0MACD_BBM_20_2.0MACD_BBU_20_2.0
date
1999-11-01136.5000137.0000135.5625135.56254006500.0NaNNaNNaNNaNNaNNaN
1999-11-02135.9687137.2500134.5937134.59376516900.0NaNNaNNaNNaNNaNNaN
1999-11-03136.0000136.3750135.1250135.50007222300.0NaNNaNNaNNaNNaNNaN
1999-11-04136.7500137.3593135.7656136.53127907500.0NaNNaNNaNNaNNaNNaN
1999-11-05138.6250139.1093136.7812137.87507431500.0NaNNaNNaNNaNNaNNaN
....................................
2020-07-14313.3000319.7600312.0000318.920092791800.03.4274210.3410143.0864070.8392133.4240326.008850
2020-07-15322.4100323.0400319.2700321.850086921500.03.8062340.5758623.2303720.9974013.3202925.643184
2020-07-16319.7900321.2800319.0900320.790054433400.03.9750910.5957753.3793161.1555063.2375935.319681
2020-07-17321.8800322.5700319.7400321.720064421800.04.1362740.6055663.5307081.2979953.1755515.053107
2020-07-20321.4300325.1300320.6200324.270051616200.04.4188380.7105043.7083341.3515603.1530964.954633
\n", + "

5212 rows × 11 columns

\n", + "
" + ], + "text/plain": [ + " open high low close volume MACD_12_26_9 \\\n", + "date \n", + "1999-11-01 136.5000 137.0000 135.5625 135.5625 4006500.0 NaN \n", + "1999-11-02 135.9687 137.2500 134.5937 134.5937 6516900.0 NaN \n", + "1999-11-03 136.0000 136.3750 135.1250 135.5000 7222300.0 NaN \n", + "1999-11-04 136.7500 137.3593 135.7656 136.5312 7907500.0 NaN \n", + "1999-11-05 138.6250 139.1093 136.7812 137.8750 7431500.0 NaN \n", + "... ... ... ... ... ... ... \n", + "2020-07-14 313.3000 319.7600 312.0000 318.9200 92791800.0 3.427421 \n", + "2020-07-15 322.4100 323.0400 319.2700 321.8500 86921500.0 3.806234 \n", + "2020-07-16 319.7900 321.2800 319.0900 320.7900 54433400.0 3.975091 \n", + "2020-07-17 321.8800 322.5700 319.7400 321.7200 64421800.0 4.136274 \n", + "2020-07-20 321.4300 325.1300 320.6200 324.2700 51616200.0 4.418838 \n", + "\n", + " MACDh_12_26_9 MACDs_12_26_9 MACD_BBL_20_2.0 MACD_BBM_20_2.0 \\\n", + "date \n", + "1999-11-01 NaN NaN NaN NaN \n", + "1999-11-02 NaN NaN NaN NaN \n", + "1999-11-03 NaN NaN NaN NaN \n", + "1999-11-04 NaN NaN NaN NaN \n", + "1999-11-05 NaN NaN NaN NaN \n", + "... ... ... ... ... \n", + "2020-07-14 0.341014 3.086407 0.839213 3.424032 \n", + "2020-07-15 0.575862 3.230372 0.997401 3.320292 \n", + "2020-07-16 0.595775 3.379316 1.155506 3.237593 \n", + "2020-07-17 0.605566 3.530708 1.297995 3.175551 \n", + "2020-07-20 0.710504 3.708334 1.351560 3.153096 \n", + "\n", + " MACD_BBU_20_2.0 \n", + "date \n", + "1999-11-01 NaN \n", + "1999-11-02 NaN \n", + "1999-11-03 NaN \n", + "1999-11-04 NaN \n", + "1999-11-05 NaN \n", + "... ... \n", + "2020-07-14 6.008850 \n", + "2020-07-15 5.643184 \n", + "2020-07-16 5.319681 \n", + "2020-07-17 5.053107 \n", + "2020-07-20 4.954633 \n", + "\n", + "[5212 rows x 11 columns]" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "spy = watch.load('SPY')\n", + "spy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Comprehensive Strategy" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### MACD and RSI Momentum with BBANDS and SMAs and Cumulative Log Returns" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Strategy(name='Momo, Bands and SMAs and Cumulative Log Returns', ta=[{'kind': 'sma', 'length': 50}, {'kind': 'sma', 'length': 200}, {'kind': 'bbands', 'length': 20}, {'kind': 'macd'}, {'kind': 'rsi'}, {'kind': 'log_return', 'cumulative': True}, {'kind': 'sma', 'close': 'CUMLOGRET_1', 'length': 5, 'suffix': 'CUMLOGRET'}], description='MACD and RSI Momo with BBANDS and SMAs 50 & 200 and Cumulative Log Returns', created='07/21/2020, 10:42:07', last_run=None, run_time=None)" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "momo_bands_sma_ta = [\n", + " {\"kind\":\"sma\", \"length\": 50},\n", + " {\"kind\":\"sma\", \"length\": 200},\n", + " {\"kind\":\"bbands\", \"length\": 20},\n", + " {\"kind\":\"macd\"},\n", + " {\"kind\":\"rsi\"},\n", + " {\"kind\":\"log_return\", \"cumulative\": True},\n", + " {\"kind\":\"sma\", \"close\": \"CUMLOGRET_1\", \"length\": 5, \"suffix\": \"CUMLOGRET\"},\n", + "]\n", + "momo_bands_sma_strategy = ta.Strategy(\n", + " \"Momo, Bands and SMAs and Cumulative Log Returns\", # name\n", + " momo_bands_sma_ta, # ta\n", + " \"MACD and RSI Momo with BBANDS and SMAs 50 & 200 and Cumulative Log Returns\" # description\n", + ")\n", + "momo_bands_sma_strategy" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Momo, Bands and SMAs and Cumulative Log Returns'" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Update the Watchlist\n", + "watch.strategy = momo_bands_sma_strategy\n", + "watch.strategy.name" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "[i] Loaded['D']: SPY_D.csv\n", + "strat.kwargs: {'ta': [{'kind': 'sma', 'length': 50}, {'kind': 'sma', 'length': 200}, {'kind': 'bbands', 'length': 20}, {'kind': 'macd'}, {'kind': 'rsi'}, {'kind': 'log_return', 'cumulative': True}, {'kind': 'sma', 'close': 'CUMLOGRET_1', 'length': 5, 'suffix': 'CUMLOGRET'}], 'timed': True}\n", + "[i] Runtime: 39.0952 ms (0.0391 s)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
openhighlowclosevolumeSMA_50SMA_200BBL_20_2.0BBM_20_2.0BBU_20_2.0MACD_12_26_9MACDh_12_26_9MACDs_12_26_9RSI_14CUMLOGRET_1SMA_5_CUMLOGRET03070
date
1999-11-01136.5000137.0000135.5625135.56254006500.0NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN03070
1999-11-02135.9687137.2500134.5937134.59376516900.0NaNNaNNaNNaNNaNNaNNaNNaN0.000000-0.007172NaN03070
1999-11-03136.0000136.3750135.1250135.50007222300.0NaNNaNNaNNaNNaNNaNNaNNaN6.263520-0.000461NaN03070
1999-11-04136.7500137.3593135.7656136.53127907500.0NaNNaNNaNNaNNaNNaNNaNNaN12.9132590.007120NaN03070
1999-11-05138.6250139.1093136.7812137.87507431500.0NaNNaNNaNNaNNaNNaNNaNNaN20.7617450.016915NaN03070
1999-11-08137.0000138.3750136.7500138.00004649200.0NaNNaNNaNNaNNaNNaNNaNNaN21.4671260.0178210.00684503070
1999-11-09138.5000138.6875136.2812136.70314533700.0NaNNaNNaNNaNNaNNaNNaNNaN19.5340820.0083790.00995503070
1999-11-10136.2500138.3906136.0781137.71876405600.0NaNNaNNaNNaNNaNNaNNaNNaN25.1864150.0157800.01320303070
1999-11-11138.1875138.5000137.4687138.50004794100.0NaNNaNNaNNaNNaNNaNNaN0.029.2810030.0214380.01606603070
1999-11-12139.2500139.9843137.1250139.750011802900.0NaNNaNNaNNaNNaNNaNNaN0.035.3466370.0304220.01876803070
1999-11-15139.8437140.2500139.4062140.07812187500.0NaNNaNNaNNaNNaNNaNNaN0.036.8694250.0327670.02175703070
1999-11-16140.5625143.0000140.0937141.25007544800.0NaNNaNNaNNaNNaNNaNNaN0.042.0892390.0410990.02830103070
1999-11-17142.2500142.9375141.3125141.62509459000.0NaNNaNNaNNaNNaNNaNNaN0.043.6856270.0437500.03389503070
1999-11-18142.4375143.0000141.6250142.62504491000.0NaNNaNNaNNaNNaNNaNNaN0.047.7971730.0507860.03976503070
1999-11-19142.4062142.9687142.0000142.50004832100.0NaNNaNNaNNaNNaNNaNNaN0.047.3343290.0499090.04366203070
1999-11-22142.4375143.0000141.5000142.46874155400.0NaNNaNNaNNaNNaNNaNNaN0.047.2116750.0496900.04704703070
1999-11-23142.8437142.8437140.3750141.21875918000.0NaNNaNNaNNaNNaNNaNNaN0.042.4995470.0408770.04700203070
1999-11-24140.7500142.4375140.0000141.96874459700.0NaNNaNNaNNaNNaNNaNNaN0.045.9664800.0461740.04748703070
1999-11-26142.4687142.8750141.2500141.43751693900.0NaNNaNNaNNaNNaNNaNNaN0.043.9553240.0424250.04581503070
1999-11-29140.8750141.9218140.4375140.93757348600.0NaNNaN134.086598139.34217144.597742NaNNaN0.042.0977810.0388840.04361003070
\n", + "
" + ], + "text/plain": [ + " open high low close volume SMA_50 \\\n", + "date \n", + "1999-11-01 136.5000 137.0000 135.5625 135.5625 4006500.0 NaN \n", + "1999-11-02 135.9687 137.2500 134.5937 134.5937 6516900.0 NaN \n", + "1999-11-03 136.0000 136.3750 135.1250 135.5000 7222300.0 NaN \n", + "1999-11-04 136.7500 137.3593 135.7656 136.5312 7907500.0 NaN \n", + "1999-11-05 138.6250 139.1093 136.7812 137.8750 7431500.0 NaN \n", + "1999-11-08 137.0000 138.3750 136.7500 138.0000 4649200.0 NaN \n", + "1999-11-09 138.5000 138.6875 136.2812 136.7031 4533700.0 NaN \n", + "1999-11-10 136.2500 138.3906 136.0781 137.7187 6405600.0 NaN \n", + "1999-11-11 138.1875 138.5000 137.4687 138.5000 4794100.0 NaN \n", + "1999-11-12 139.2500 139.9843 137.1250 139.7500 11802900.0 NaN \n", + "1999-11-15 139.8437 140.2500 139.4062 140.0781 2187500.0 NaN \n", + "1999-11-16 140.5625 143.0000 140.0937 141.2500 7544800.0 NaN \n", + "1999-11-17 142.2500 142.9375 141.3125 141.6250 9459000.0 NaN \n", + "1999-11-18 142.4375 143.0000 141.6250 142.6250 4491000.0 NaN \n", + "1999-11-19 142.4062 142.9687 142.0000 142.5000 4832100.0 NaN \n", + "1999-11-22 142.4375 143.0000 141.5000 142.4687 4155400.0 NaN \n", + "1999-11-23 142.8437 142.8437 140.3750 141.2187 5918000.0 NaN \n", + "1999-11-24 140.7500 142.4375 140.0000 141.9687 4459700.0 NaN \n", + "1999-11-26 142.4687 142.8750 141.2500 141.4375 1693900.0 NaN \n", + "1999-11-29 140.8750 141.9218 140.4375 140.9375 7348600.0 NaN \n", + "\n", + " SMA_200 BBL_20_2.0 BBM_20_2.0 BBU_20_2.0 MACD_12_26_9 \\\n", + "date \n", + "1999-11-01 NaN NaN NaN NaN NaN \n", + "1999-11-02 NaN NaN NaN NaN NaN \n", + "1999-11-03 NaN NaN NaN NaN NaN \n", + "1999-11-04 NaN NaN NaN NaN NaN \n", + "1999-11-05 NaN NaN NaN NaN NaN \n", + "1999-11-08 NaN NaN NaN NaN NaN \n", + "1999-11-09 NaN NaN NaN NaN NaN \n", + "1999-11-10 NaN NaN NaN NaN NaN \n", + "1999-11-11 NaN NaN NaN NaN NaN \n", + "1999-11-12 NaN NaN NaN NaN NaN \n", + "1999-11-15 NaN NaN NaN NaN NaN \n", + "1999-11-16 NaN NaN NaN NaN NaN \n", + "1999-11-17 NaN NaN NaN NaN NaN \n", + "1999-11-18 NaN NaN NaN NaN NaN \n", + "1999-11-19 NaN NaN NaN NaN NaN \n", + "1999-11-22 NaN NaN NaN NaN NaN \n", + "1999-11-23 NaN NaN NaN NaN NaN \n", + "1999-11-24 NaN NaN NaN NaN NaN \n", + "1999-11-26 NaN NaN NaN NaN NaN \n", + "1999-11-29 NaN 134.086598 139.34217 144.597742 NaN \n", + "\n", + " MACDh_12_26_9 MACDs_12_26_9 RSI_14 CUMLOGRET_1 \\\n", + "date \n", + "1999-11-01 NaN NaN NaN NaN \n", + "1999-11-02 NaN NaN 0.000000 -0.007172 \n", + "1999-11-03 NaN NaN 6.263520 -0.000461 \n", + "1999-11-04 NaN NaN 12.913259 0.007120 \n", + "1999-11-05 NaN NaN 20.761745 0.016915 \n", + "1999-11-08 NaN NaN 21.467126 0.017821 \n", + "1999-11-09 NaN NaN 19.534082 0.008379 \n", + "1999-11-10 NaN NaN 25.186415 0.015780 \n", + "1999-11-11 NaN 0.0 29.281003 0.021438 \n", + "1999-11-12 NaN 0.0 35.346637 0.030422 \n", + "1999-11-15 NaN 0.0 36.869425 0.032767 \n", + "1999-11-16 NaN 0.0 42.089239 0.041099 \n", + "1999-11-17 NaN 0.0 43.685627 0.043750 \n", + "1999-11-18 NaN 0.0 47.797173 0.050786 \n", + "1999-11-19 NaN 0.0 47.334329 0.049909 \n", + "1999-11-22 NaN 0.0 47.211675 0.049690 \n", + "1999-11-23 NaN 0.0 42.499547 0.040877 \n", + "1999-11-24 NaN 0.0 45.966480 0.046174 \n", + "1999-11-26 NaN 0.0 43.955324 0.042425 \n", + "1999-11-29 NaN 0.0 42.097781 0.038884 \n", + "\n", + " SMA_5_CUMLOGRET 0 30 70 \n", + "date \n", + "1999-11-01 NaN 0 30 70 \n", + "1999-11-02 NaN 0 30 70 \n", + "1999-11-03 NaN 0 30 70 \n", + "1999-11-04 NaN 0 30 70 \n", + "1999-11-05 NaN 0 30 70 \n", + "1999-11-08 0.006845 0 30 70 \n", + "1999-11-09 0.009955 0 30 70 \n", + "1999-11-10 0.013203 0 30 70 \n", + "1999-11-11 0.016066 0 30 70 \n", + "1999-11-12 0.018768 0 30 70 \n", + "1999-11-15 0.021757 0 30 70 \n", + "1999-11-16 0.028301 0 30 70 \n", + "1999-11-17 0.033895 0 30 70 \n", + "1999-11-18 0.039765 0 30 70 \n", + "1999-11-19 0.043662 0 30 70 \n", + "1999-11-22 0.047047 0 30 70 \n", + "1999-11-23 0.047002 0 30 70 \n", + "1999-11-24 0.047487 0 30 70 \n", + "1999-11-26 0.045815 0 30 70 \n", + "1999-11-29 0.043610 0 30 70 " + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "spy = watch.load('SPY', timed=True)\n", + "# Apply constants to the DataFrame for indicators\n", + "spy.ta.constants(True, 0, 0, 1) # 0\n", + "spy.ta.constants(True, 30, 30, 1) # 30\n", + "spy.ta.constants(True, 70, 70, 1) # 70\n", + "spy.head(20)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/watchlist.py b/examples/watchlist.py new file mode 100644 index 0000000..df8cc3c --- /dev/null +++ b/examples/watchlist.py @@ -0,0 +1,202 @@ +# -*- coding: utf-8 -*- +from functools import lru_cache +from pathlib import Path +from random import random + +import pandas as pd + +from alphaVantageAPI.alphavantage import AlphaVantage # pip install alphaVantage-api +import pandas_ta as ta + + +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. Default + Strategy: pandas_ta.AllStrategy. + + Requirements: + - Pandas TA (pip install pandas_ta) + - AlphaVantage (pip install alphaVantage-api) for the Default Data Source. + To use another Data Source, update the load() method after AV. + + Required Arguments: + - tickers: A list of strings containing tickers. Example: ['SPY', 'AAPL'] + ============================================================================ + """ + def __init__( + self, + tickers: list, + tf: str = None, + name: str = None, + strategy: ta.Strategy = None, + ds: object = None, + **kwargs + ): + self.tickers = tickers + self.tf = tf + self.verbose = kwargs.pop("verbose", False) + self.name = name + self.data = None + self.kwargs = kwargs + + self.ds = ds if ds is not None else None + self.strategy = strategy + + + def _drop_columns(self, df: pd.DataFrame, cols: list = ['Unnamed: 0', 'date', 'split_coefficient', 'dividend']): + """Helper methods to drop columns silently.""" + df_columns = list(df.columns) + if any(_ in df_columns for _ in cols): + if self.verbose: + print(f"[i] Possible columns dropped: {', '.join(cols)}") + df = df.drop(cols, axis=1, errors='ignore') + return df + + def _load_all(self, **kwargs) -> dict: + """Updates the Watchlist's data property with a dictionary of DataFrames + keyed by ticker.""" + if self.tickers is not None and isinstance(self.tickers, list) and len(self.tickers): + self.data = {ticker: self.load(ticker, **kwargs) for ticker in self.tickers} + return self.data + + def load( + self, + ticker: str = None, + tf: str = None, + index: str = 'date', + drop: list = ['dividend', 'split_coefficient'], + file_path: str = ".", + **kwargs + ) -> pd.DataFrame: + """Loads or Downloads (if a local csv does not exist) the data from the + Data Source. When successful, it returns a Data Frame for the requested + ticker. If no tickers are given, it loads all the tickers.""" + + tf = self.tf if tf is None else tf.upper() + if ticker is not None and isinstance(ticker, str): + ticker = str(ticker).upper() + else: + print(f"[!] Loading All: {', '.join(self.tickers)}") + self._load_all(**kwargs) + return + + filename_ = f"{ticker}_{tf}.csv" + current_file = Path(file_path) / filename_ + + # Load local or from Data Source + if current_file.exists(): + df = pd.read_csv(filename_, index_col=index) + if not df.ta.datetime_ordered: + df = df.set_index(pd.DatetimeIndex(df.index)) + print(f"\n[i] Loaded['{tf}']: {filename_}") + else: + if self.ds is not None and isinstance(self.ds, AlphaVantage): + df = self.ds.data(tf, ticker) + if not df.ta.datetime_ordered: + df = df.set_index(pd.DatetimeIndex(df[index])) + print(f"\n[+] Downloading['{tf}']: {ticker}") + + df = self._drop_columns(df) # Remove select columns + + if kwargs.pop("analyze", True): + df.ta.strategy(name=self.strategy.name, ta=self.strategy.ta, **kwargs) + + df.ticker = ticker # Attach ticker to the DataFrame + return df + + @property + def data(self) -> dict: + """When not None, it contains a dictionary of DataFrames keyed by ticker.""" + return self._data + + @data.setter + def data(self, value: dict) -> None: + # Later check dict has string keys and DataFrame values + if value is not None and isinstance(value, dict): + if self.verbose: print(f"[+] New data") + self._data = value + else: + self._data = None + + @property + def name(self) -> str: + """The name of the Watchlist. Default: "Watchlist: {Watchlist.tickers}".""" + return self._name + + @name.setter + def name(self, value: str) -> None: + if isinstance(value, str): + self._name = str(value) + else: + self._name = f"Watchlist: {', '.join(self.tickers)}" + + @property + def strategy(self) -> ta.Strategy: + """Pandas TA Strategy Class. Default: pandas_ta.AllStrategy""" + return self._strategy + + @strategy.setter + def strategy(self, value: ta.Strategy) -> None: + if value is not None and isinstance(value, ta.Strategy): + self._strategy = value + else: + self._strategy = ta.AllStrategy + + @property + def tf(self) -> str: + """Alias for timeframe. Default: 'D'""" + return self._tf + + @tf.setter + def tf(self, value: str) -> None: + if isinstance(value, str): + value = str(value) + self._tf = value + else: + self._tf = "D" + + @property + def tickers(self) -> list: + """tickers + + If a string, it it converted to a list. Example: 'AAPL' -> ['AAPL'] + * Does not accept, comma seperated strings. + If a list, checks if it is a list of strings. + """ + return self._tickers + + @tickers.setter + def tickers(self, value: (list, str)) -> None: + if value is None: + print(f"[X] {value} is not a valie Watchlist ticker.") + return + elif isinstance(value, list) and [isinstance(_, str) for _ in value]: + self._tickers = list(map(str.upper, value)) + elif isinstance(value, str): + self._tickers = [value.upper()] + self.name = self._tickers + + @property + def verbose(self) -> bool: + """Toggle the verbose property. Default: False""" + return self._verbose + + @verbose.setter + def verbose(self, value: bool) -> None: + if isinstance(value, bool): + self._verbose = bool(value) + else: + self._verbose = False + + def indicators(self, *args, **kwargs) -> any: + """Returns the list of indicators that are available with Pandas Ta.""" + pd.DataFrame().ta.indicators(*args, **kwargs) + + def __repr__(self) -> str: + s = f"Watch(name='{self.name}', tickers[{len(self.tickers)}]='{', '.join(self.tickers)}', tf='{self.tf}', strategy[{self.strategy.total_ta()}]='{self.strategy.name}'" + if self.data is not None: + s += f", data[{len(self.data.keys())}])" + return s + return s + ")" \ No newline at end of file diff --git a/pandas_ta/core.py b/pandas_ta/core.py index f544cba..cd25d04 100644 --- a/pandas_ta/core.py +++ b/pandas_ta/core.py @@ -1,8 +1,11 @@ # -*- coding: utf-8 -*- +from dataclasses import dataclass, field +from datetime import datetime from functools import wraps from multiprocessing import cpu_count, Pool from random import random from time import perf_counter +from typing import List import pandas as pd from pandas.core.base import PandasObject @@ -17,7 +20,7 @@ from pandas_ta.volatility import * from pandas_ta.volume import * from pandas_ta.utils import * -version = ".".join(("0", "1", "75b")) +version = ".".join(("0", "1", "76b")) def mp_worker(args): df, method, kwargs = args @@ -40,11 +43,91 @@ def finalize(method): return _wrapper +@dataclass +class Strategy: + """Strategy (Data)Class + A way to name and group your favorite indicators + + 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 + created (str): At datetime string of when it was created. Default: Automatically generated. *Subject to change* + + Example TA: + ta = [ + {"kind": "sma", "length": 200}, + {"kind": "sma", "close": "volume", "length": 50}, + {"kind": "bbands", "length": 20}, + {"kind": "rsi"}, + {"kind": "macd", "fast": 8, "slow": 21}, + {"kind": "sma", "close": "volume", "length": 20, "prefix": "VOLUME"}, + ] + """ + name: str# = None # Required. + ta: List = field(default_factory=list) # Required. + description: str = None # Helpful. More descriptive version or notes or w/e. + created: str = datetime.now().strftime("%m/%d/%Y, %H:%M:%S") # Optional. May change type later to datetime + last_run: str = None # Auto filled + run_time: str = None # Auto filled + + def __post_init__(self): + has_name = True + is_ta = False + required_args = ["[X] Strategy 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.ta is None: + self.ta = None + elif self.ta is not None and ta_is_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 + + def total_ta(self): + return len(self.ta) if self.ta is not None else 0 + +# All Default Strategy +AllStrategy = Strategy( + name="All", + description="All the indicators with their default settings. Pandas TA default.", + ta=None +) + +# Default (Example) Strategy. +CommonStrategy = Strategy( + name="Common Price and Volume SMAs", + description="Common Price SMAs: 10, 20, 50, 200 and Volume SMA: 20.", + ta=[ + {"kind": "sma", "length": 10}, + {"kind": "sma", "length": 20}, + {"kind": "sma", "length": 50}, + {"kind": "sma", "length": 200}, + {"kind": "sma", "close": "volume", "length": 20, "prefix": "VOL"} + ] +) + + class BasePandasObject(PandasObject): """Simple PandasObject Extension - Ensures the DataFrame is not empty and has columns. It would be a - sad Panda otherwise. + Ensures the DataFrame is not empty and has columns. + It would be a sad Panda otherwise. Args: df (pd.DataFrame): Extends Pandas DataFrame @@ -140,30 +223,37 @@ class AnalysisIndicators(BasePandasObject): _adjusted = None _mp = False - def __call__(self, kind=None, alias=None, timed=False, verbose=False, **kwargs): - try: - if isinstance(kind, str): - kind = kind.lower() - fn = getattr(self, kind) + def __call__( + self, + kind: str= None, + alias: str = None, + timed = False, + verbose = False, + **kwargs + ): + try: + if isinstance(kind, str): + kind = kind.lower() + fn = getattr(self, kind) - if timed: stime = perf_counter() + if timed: stime = perf_counter() - # Run the indicator - result = fn(**kwargs) # = getattr(self, kind)(**kwargs) + # Run the indicator + result = fn(**kwargs) # = getattr(self, kind)(**kwargs) - # Add an alias if passed - if alias: result.alias = f"{alias}" + # Add an alias if passed + if alias: result.alias = f"{alias}" - if timed: - result.timed = final_time(stime) - print(f"[+] {kind}:{alias + ':' if alias is not None else ''} {result.timed}") + if timed: + result.timed = final_time(stime) + alias_str = alias + ':' if alias is not None else '' + print(f"[+] {kind}:{alias_str} {result.timed}") - return result - else: - self.help() - - except: pass + return result + else: + self.help() + except: pass @property def adjusted(self) -> str: @@ -257,11 +347,11 @@ class AnalysisIndicators(BasePandasObject): match = [i for i, x in enumerate(matches) if x] # If found, awesome. Return it or return the 'series'. cols = ', '.join(list(df.columns)) - NOT_FOUND = f" [X] Ooops!!!: It's {series not in df.columns}, the series '{series}' not in {cols}" + NOT_FOUND = f"[X] Ooops!!!: It's {series not in df.columns}, the series '{series}' was not found in {cols}" return df.iloc[:,match[0]] if len(match) else print(NOT_FOUND) - def constants(self, append, lower_bound=-100, upper_bound=100, every=1): + def constants(self, append, lower_bound=-100, upper_bound=100, every=10): """Constants Useful for creating indicator levels or if you need some constant value @@ -332,58 +422,7 @@ class AnalysisIndicators(BasePandasObject): s = f"{header}\nTotal Indicators: {total_indicators}\n" print(f"{s}Abbreviations:\n {', '.join(ta_indicators)}") if total_indicators > 0 else print(s) - - # ALL Features - def _all(self, **kwargs): - """Appends by default all non-excluded indicators to the DataFrame. Used by ta.strategy(**kwargs)""" - cpus = cpu_count() - cores = int(kwargs.pop("cores", cpus)) - timed = kwargs.pop("timed", False) - verbose = kwargs.pop("verbose", False) - user_excluded = kwargs.pop("exclude", []) - append = kwargs.setdefault("append", True) - - excluded = ["above", "above_value", "below", "below_value", - "cross", "cross_value", "long_run", "short_run", "trend_return", "vp"] - excluded += user_excluded - - current_columns = len(self._df.columns) - indicators = self.indicators(as_list=True, exclude=excluded) - - print('[+] Strategy "All"') - if verbose: - print(f'[i] Indicators with the following arguments: {kwargs}') - print(f"[i] excluded[{len(excluded)}]: {', '.join(excluded)}") - - if timed: stime = perf_counter() - - if not self.mp: - # Display multiprocessing tip 10% of the time. - if random() < 0.1: - print(f"[i] Set 'df.ta.mp = True' to enable multiprocessing. This computer has {cpus} cores. Default: False") - - methods = [getattr(self, kind) for kind in indicators] - [f(**kwargs) for f in methods] - - else: - print(f"[i] multiprocessing: {cores} of {cpu_count()} cores") - pool = Pool(cores) - result = pool.imap_unordered( - mp_worker, ((self._df, ind, kwargs) for ind in indicators), cores - ) - pool.close() - pool.join() - - # Apply prefixes/suffixes and append to the DataFrame - for r in result: - self._add_prefix_suffix(r, **kwargs) - self._append(r, **kwargs) - - print(f"[i] total indicators: {len(indicators)}, columns added: {len(self._df.columns) - current_columns}") - print(f"[i] runtime: {final_time(stime)}\n") if timed else None - - - def strategy(self, **kwargs): + def strategy(self, *args, **kwargs): """Strategy Method An experimental method that by default runs all applicable indicators. @@ -393,17 +432,85 @@ class AnalysisIndicators(BasePandasObject): Args: name (str, optional): Default: 'all' exclude (list, optional): Default: []. List of indicator names to exclude. - verbose (bool): Default: False kwargs: (optional) Default: {}. Any indicator argument you want to modify. For example, length=20 or offset=-1 or high=df['High'] ... - """ - name = kwargs.pop("name", "all") - if name is None or name == "" or not isinstance(name, str): # Extra check - name = "all" - self._all(**kwargs) if name == "all" else None + cpus = cpu_count() + name = kwargs.pop("name", None) + if name is None or name.lower() == "all": + name = "All" + print(f"strat.kwargs: {kwargs}") + # removing before sending the rest of kwargs to the indicators + ta = kwargs.pop("ta", None) + mp = kwargs.pop("mp", False) + cores = int(kwargs.pop("cores", cpus)) + timed = kwargs.pop("timed", False) + verbose = kwargs.pop("verbose", False) + user_excluded = kwargs.pop("exclude", []) + kwargs["append"] = True + + is_all = True if name is None or name.lower() == "all" else False + has_ta = True if ta is not None else False + initial_column_count = len(self._df.columns) + + excluded = [] + excluded += user_excluded # Exclude user excluded ta if listed + + print(f'[+] Strategy "{name}"') if verbose else None + if is_all: + # Exclude utilities special functions + excluded += ["above", "above_value", "below", "below_value", "cross", "cross_value", "long_run", "short_run", "trend_return", "vp"] + ta = self.indicators(as_list=True, exclude=excluded) + else: + for kwds in ta: + kwds["append"] = True + + if verbose: + print(f'[i] Indicators with the following arguments: {kwargs}') + if len(excluded) > 0: + print(f"[i] Excluded[{len(excluded)}]: {', '.join(excluded)}") + + # Enable multiprocessing if user sets: mp=True + if mp: self.mp = not self.mp + + if self.mp: + # TODO: Fix for Custom Strategies + print(f"[i] Multiprocessing: {cores} of {cpu_count()} cores") + pool = Pool(cores) + + if timed: stime = perf_counter() + result = pool.imap_unordered( + mp_worker, ((self._df, ind, kwargs) for ind in ta), cores + ) + pool.close() + pool.join() + + # Apply prefixes/suffixes and appends indicator result to the DataFrame + for r in result: + self._add_prefix_suffix(r, **kwargs) + self._append(r, **kwargs) + if timed: ftime = final_time(stime) + + else: + # Display multiprocessing tip 10% of the time. + if random() < 0.1: + print(f"[i] Set 'df.ta.mp = True' to enable multiprocessing. This computer has {cpus} cores. Default: False") + + if timed: stime = perf_counter() + if is_all: + indicators = [getattr(self, kind) for kind in ta] + [f(**kwargs) for f in indicators] + else: + [getattr(self, kwds["kind"])(**kwds) for kwds in ta] + + if timed: ftime = final_time(stime) + + if verbose: + print(f"[i] Total indicators: {len(ta)}") + print(f"[i] Columns added: {len(self._df.columns) - initial_column_count}") + print(f"[i] Runtime: {ftime}") if timed else None # Candles diff --git a/pandas_ta/overlap/rma.py b/pandas_ta/overlap/rma.py index ddbc917..5ed998a 100644 --- a/pandas_ta/overlap/rma.py +++ b/pandas_ta/overlap/rma.py @@ -6,12 +6,11 @@ def rma(close, length=None, offset=None, **kwargs): # Validate Arguments close = verify_series(close) length = int(length) if length and length > 0 else 10 - min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length offset = get_offset(offset) alpha = (1.0 / length) if length > 0 else 0.5 # Calculate Result - rma = close.ewm(alpha=alpha, min_periods=min_periods).mean() + rma = close.ewm(alpha=alpha).mean() # Offset if offset != 0: diff --git a/pandas_ta/utils.py b/pandas_ta/utils.py index 77be0e0..256002c 100644 --- a/pandas_ta/utils.py +++ b/pandas_ta/utils.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import math +from pathlib import Path from time import perf_counter import numpy as np @@ -9,7 +10,7 @@ from functools import reduce from operator import mul from sys import float_info as sflt -TRADING_DAYS_PER_YEAR = 250 +TRADING_DAYS_PER_YEAR = 251 TRADING_HOURS_PER_DAY = 6.5 MINUTES_PER_HOUR = 60 diff --git a/pandas_ta/volatility/bbands.py b/pandas_ta/volatility/bbands.py index a9d01c8..83b1005 100644 --- a/pandas_ta/volatility/bbands.py +++ b/pandas_ta/volatility/bbands.py @@ -44,15 +44,15 @@ def bbands(close, length=None, std=None, mamode=None, offset=None, **kwargs): upper.fillna(method=kwargs['fill_method'], inplace=True) # Name and Categorize it - lower.name = f"BBL_{length}" - mid.name = f"BBM_{length}" - upper.name = f"BBU_{length}" + lower.name = f"BBL_{length}_{std}" + mid.name = f"BBM_{length}_{std}" + upper.name = f"BBU_{length}_{std}" mid.category = upper.category = lower.category = 'volatility' # Prepare DataFrame to return data = {lower.name: lower, mid.name: mid, upper.name: upper} bbandsdf = DataFrame(data) - bbandsdf.name = f"BBANDS_{length}" + bbandsdf.name = f"BBANDS_{length}_{std}" bbandsdf.category = 'volatility' return bbandsdf diff --git a/tests/config.py b/tests/config.py index 3c4534f..dde7f77 100644 --- a/tests/config.py +++ b/tests/config.py @@ -6,7 +6,7 @@ VERBOSE = True ALERT = f"[!]" INFO = f"[i]" -CORRELATION = 'corr' #'sem' +CORRELATION = "corr" #"sem" CORRELATION_THRESHOLD = 0.99 # Less than 0.99 is undesirable sample_data = read_csv( diff --git a/tests/context.py b/tests/context.py index a237851..11f5534 100644 --- a/tests/context.py +++ b/tests/context.py @@ -1,5 +1,5 @@ import os import sys -sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) import pandas_ta \ No newline at end of file diff --git a/tests/test_indicator_candle.py b/tests/test_indicator_candle.py index 1297164..f0a58dc 100644 --- a/tests/test_indicator_candle.py +++ b/tests/test_indicator_candle.py @@ -14,11 +14,11 @@ class TestCandle(TestCase): def setUpClass(cls): cls.data = sample_data cls.data.columns = cls.data.columns.str.lower() - cls.open = cls.data['open'] - cls.high = cls.data['high'] - cls.low = cls.data['low'] - cls.close = cls.data['close'] - if 'volume' in cls.data.columns: cls.volume = cls.data['volume'] + cls.open = cls.data["open"] + cls.high = cls.data["high"] + cls.low = cls.data["low"] + cls.close = cls.data["close"] + if "volume" in cls.data.columns: cls.volume = cls.data["volume"] @classmethod def tearDownClass(cls): @@ -26,7 +26,7 @@ class TestCandle(TestCase): del cls.high del cls.low del cls.close - if hasattr(cls, 'volume'): del cls.volume + if hasattr(cls, "volume"): del cls.volume del cls.data diff --git a/tests/test_indicator_candle_ext.py b/tests/test_indicator_candle_ext.py index 561a768..ebb3035 100644 --- a/tests/test_indicator_candle_ext.py +++ b/tests/test_indicator_candle_ext.py @@ -26,9 +26,9 @@ class TestCandleExtension(TestCase): def test_ha_ext(self): self.data.ta.ha(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-4:]), ['HA_open', 'HA_high', 'HA_low', 'HA_close']) + self.assertEqual(list(self.data.columns[-4:]), ["HA_open", "HA_high", "HA_low", "HA_close"]) def test_cdl_doji_ext(self): self.data.ta.cdl_doji(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'CDL_DOJI_10_0.1') \ No newline at end of file + self.assertEqual(self.data.columns[-1], "CDL_DOJI_10_0.1") \ No newline at end of file diff --git a/tests/test_indicator_momentum.py b/tests/test_indicator_momentum.py index 6a9ad00..32ab89a 100644 --- a/tests/test_indicator_momentum.py +++ b/tests/test_indicator_momentum.py @@ -14,11 +14,11 @@ class TestMomentum(TestCase): def setUpClass(cls): cls.data = sample_data cls.data.columns = cls.data.columns.str.lower() - cls.open = cls.data['open'] - cls.high = cls.data['high'] - cls.low = cls.data['low'] - cls.close = cls.data['close'] - if 'volume' in cls.data.columns: cls.volume = cls.data['volume'] + cls.open = cls.data["open"] + cls.high = cls.data["high"] + cls.low = cls.data["low"] + cls.close = cls.data["close"] + if "volume" in cls.data.columns: cls.volume = cls.data["volume"] @classmethod def tearDownClass(cls): @@ -26,7 +26,7 @@ class TestMomentum(TestCase): del cls.high del cls.low del cls.close - if hasattr(cls, 'volume'): del cls.volume + if hasattr(cls, "volume"): del cls.volume del cls.data @@ -62,12 +62,12 @@ class TestMomentum(TestCase): def test_ao(self): result = pandas_ta.ao(self.high, self.low) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'AO_5_34') + self.assertEqual(result.name, "AO_5_34") def test_apo(self): result = pandas_ta.apo(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'APO_12_26') + self.assertEqual(result.name, "APO_12_26") try: expected = tal.APO(self.close) @@ -82,12 +82,12 @@ class TestMomentum(TestCase): def test_bias(self): result = pandas_ta.bias(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'BIAS_SMA_26') + self.assertEqual(result.name, "BIAS_SMA_26") def test_bop(self): result = pandas_ta.bop(self.open, self.high, self.low, self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'BOP') + self.assertEqual(result.name, "BOP") try: expected = tal.BOP(self.open, self.high, self.low, self.close) @@ -102,12 +102,12 @@ class TestMomentum(TestCase): def test_brar(self): result = pandas_ta.brar(self.open, self.high, self.low, self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'BRAR_26') + self.assertEqual(result.name, "BRAR_26") def test_cci(self): result = pandas_ta.cci(self.high, self.low, self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'CCI_14_0.015') + self.assertEqual(result.name, "CCI_14_0.015") try: expected = tal.CCI(self.high, self.low, self.close) @@ -122,12 +122,12 @@ class TestMomentum(TestCase): def test_cg(self): result = pandas_ta.cg(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'CG_10') + self.assertEqual(result.name, "CG_10") def test_cmo(self): result = pandas_ta.cmo(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'CMO_14') + self.assertEqual(result.name, "CMO_14") try: expected = tal.CMO(self.close) @@ -142,45 +142,45 @@ class TestMomentum(TestCase): def test_coppock(self): result = pandas_ta.coppock(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'COPC_11_14_10') + self.assertEqual(result.name, "COPC_11_14_10") def test_fisher(self): result = pandas_ta.fisher(self.high, self.low) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'FISHERT_5') + self.assertEqual(result.name, "FISHERT_5") def test_inertia(self): result = pandas_ta.inertia(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'INERTIA_20_14') + self.assertEqual(result.name, "INERTIA_20_14") result = pandas_ta.inertia(self.close, self.high, self.low, refined=True) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'INERTIAr_20_14') + self.assertEqual(result.name, "INERTIAr_20_14") result = pandas_ta.inertia(self.close, self.high, self.low, thirds=True) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'INERTIAt_20_14') + self.assertEqual(result.name, "INERTIAt_20_14") def test_kdj(self): result = pandas_ta.kdj(self.high, self.low, self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'KDJ_9_3') + self.assertEqual(result.name, "KDJ_9_3") def test_kst(self): result = pandas_ta.kst(self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'KST_10_15_20_30_10_10_10_15_9') + self.assertEqual(result.name, "KST_10_15_20_30_10_10_10_15_9") def test_macd(self): result = pandas_ta.macd(self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'MACD_12_26_9') + self.assertEqual(result.name, "MACD_12_26_9") try: expected = tal.MACD(self.close) - expecteddf = DataFrame({'MACD_12_26_9': expected[0], 'MACDh_12_26_9': expected[2], 'MACDs_12_26_9': expected[1]}) + expecteddf = DataFrame({"MACD_12_26_9": expected[0], "MACDh_12_26_9": expected[2], "MACDs_12_26_9": expected[1]}) pdt.assert_frame_equal(result, expecteddf) except AssertionError as ae: try: @@ -204,7 +204,7 @@ class TestMomentum(TestCase): def test_mom(self): result = pandas_ta.mom(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'MOM_10') + self.assertEqual(result.name, "MOM_10") try: expected = tal.MOM(self.close) @@ -219,32 +219,32 @@ class TestMomentum(TestCase): def test_ppo(self): result = pandas_ta.ppo(self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'PPO_12_26_9') + self.assertEqual(result.name, "PPO_12_26_9") try: expected = tal.PPO(self.close) - pdt.assert_series_equal(result['PPO_12_26_9'], expected, check_names=False) + pdt.assert_series_equal(result["PPO_12_26_9"], expected, check_names=False) except AssertionError as ae: try: - corr = pandas_ta.utils.df_error_analysis(result['PPO_12_26_9'], expected, col=CORRELATION) + corr = pandas_ta.utils.df_error_analysis(result["PPO_12_26_9"], expected, col=CORRELATION) self.assertGreater(corr, CORRELATION_THRESHOLD) except Exception as ex: - error_analysis(result['PPO_12_26_9'], CORRELATION, ex) + error_analysis(result["PPO_12_26_9"], CORRELATION, ex) def test_psl(self): result = pandas_ta.psl(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'PSL_12') + self.assertEqual(result.name, "PSL_12") def test_pvo(self): result = pandas_ta.pvo(self.volume) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'PVO_12_26_9') + self.assertEqual(result.name, "PVO_12_26_9") def test_roc(self): result = pandas_ta.roc(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'ROC_10') + self.assertEqual(result.name, "ROC_10") try: expected = tal.ROC(self.close) @@ -259,7 +259,7 @@ class TestMomentum(TestCase): def test_rsi(self): result = pandas_ta.rsi(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'RSI_14') + self.assertEqual(result.name, "RSI_14") try: expected = tal.RSI(self.close) @@ -274,37 +274,37 @@ class TestMomentum(TestCase): def test_rvgi(self): result = pandas_ta.rvgi(self.open, self.high, self.low, self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'RVGI_14_4') + self.assertEqual(result.name, "RVGI_14_4") def test_slope(self): result = pandas_ta.slope(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'SLOPE_1') + self.assertEqual(result.name, "SLOPE_1") def test_slope_as_angle(self): result = pandas_ta.slope(self.close, as_angle=True) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'ANGLEr_1') + self.assertEqual(result.name, "ANGLEr_1") def test_slope_as_angle_to_degrees(self): result = pandas_ta.slope(self.close, as_angle=True, to_degrees=True) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'ANGLEd_1') + self.assertEqual(result.name, "ANGLEd_1") def test_stoch(self): result = pandas_ta.stoch(self.high, self.low, self.close, fast_k=14, slow_k=14, slow_d=14) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'STOCH_14_14_14') + self.assertEqual(result.name, "STOCH_14_14_14") self.assertEqual(len(result.columns), 4) result = pandas_ta.stoch(self.high, self.low, self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'STOCH_14_5_3') + self.assertEqual(result.name, "STOCH_14_5_3") try: tal_stochf = tal.STOCHF(self.high, self.low, self.close) tal_stoch = tal.STOCH(self.high, self.low, self.close) - tal_stochdf = DataFrame({'STOCHF_14': tal_stochf[0], 'STOCHF_3': tal_stochf[1], 'STOCH_5': tal_stoch[0], 'STOCH_3': tal_stoch[1]}) + tal_stochdf = DataFrame({"STOCHF_14": tal_stochf[0], "STOCHF_3": tal_stochf[1], "STOCH_5": tal_stoch[0], "STOCH_3": tal_stoch[1]}) pdt.assert_frame_equal(result, tal_stochdf) except AssertionError as ae: try: @@ -334,17 +334,17 @@ class TestMomentum(TestCase): def test_trix(self): result = pandas_ta.trix(self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'TRIX_30_9') + self.assertEqual(result.name, "TRIX_30_9") def test_tsi(self): result = pandas_ta.tsi(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'TSI_13_25') + self.assertEqual(result.name, "TSI_13_25") def test_uo(self): result = pandas_ta.uo(self.high, self.low, self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'UO_7_14_28') + self.assertEqual(result.name, "UO_7_14_28") try: expected = tal.ULTOSC(self.high, self.low, self.close) @@ -359,7 +359,7 @@ class TestMomentum(TestCase): def test_willr(self): result = pandas_ta.willr(self.high, self.low, self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'WILLR_14') + self.assertEqual(result.name, "WILLR_14") try: expected = tal.WILLR(self.high, self.low, self.close) diff --git a/tests/test_indicator_momentum_ext.py b/tests/test_indicator_momentum_ext.py index deabd83..87ec2db 100644 --- a/tests/test_indicator_momentum_ext.py +++ b/tests/test_indicator_momentum_ext.py @@ -26,152 +26,152 @@ class TestMomentumExtension(TestCase): def test_ao_ext(self): self.data.ta.ao(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'AO_5_34') + self.assertEqual(self.data.columns[-1], "AO_5_34") def test_apo_ext(self): self.data.ta.apo(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'APO_12_26') + self.assertEqual(self.data.columns[-1], "APO_12_26") def test_bias_ext(self): self.data.ta.bias(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'BIAS_SMA_26') + self.assertEqual(self.data.columns[-1], "BIAS_SMA_26") def test_bop_ext(self): self.data.ta.bop(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'BOP') + self.assertEqual(self.data.columns[-1], "BOP") def test_brar_ext(self): self.data.ta.brar(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-2:]), ['AR_26', 'BR_26']) + self.assertEqual(list(self.data.columns[-2:]), ["AR_26", "BR_26"]) def test_cci_ext(self): self.data.ta.cci(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'CCI_14_0.015') + self.assertEqual(self.data.columns[-1], "CCI_14_0.015") def test_cg_ext(self): self.data.ta.cg(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'CG_10') + self.assertEqual(self.data.columns[-1], "CG_10") def test_cmo_ext(self): self.data.ta.cmo(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'CMO_14') + self.assertEqual(self.data.columns[-1], "CMO_14") def test_coppock_ext(self): self.data.ta.coppock(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'COPC_11_14_10') + self.assertEqual(self.data.columns[-1], "COPC_11_14_10") def test_fisher_ext(self): self.data.ta.fisher(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'FISHERT_5') + self.assertEqual(self.data.columns[-1], "FISHERT_5") def test_inertia_ext(self): self.data.ta.inertia(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'INERTIA_20_14') + self.assertEqual(self.data.columns[-1], "INERTIA_20_14") def test_inertia_refined_ext(self): self.data.ta.inertia(refined=True, append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'INERTIAr_20_14') + self.assertEqual(self.data.columns[-1], "INERTIAr_20_14") def test_inertia_thirds_ext(self): self.data.ta.inertia(thirds=True, append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'INERTIAt_20_14') + self.assertEqual(self.data.columns[-1], "INERTIAt_20_14") def test_kdj_ext(self): self.data.ta.kdj(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ['K_9_3', 'D_9_3', 'J_9_3']) + self.assertEqual(list(self.data.columns[-3:]), ["K_9_3", "D_9_3", "J_9_3"]) def test_kst_ext(self): self.data.ta.kst(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-2:]), ['KST_10_15_20_30_10_10_10_15', 'KSTs_9']) + self.assertEqual(list(self.data.columns[-2:]), ["KST_10_15_20_30_10_10_10_15", "KSTs_9"]) def test_macd_ext(self): self.data.ta.macd(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ['MACD_12_26_9', 'MACDh_12_26_9', 'MACDs_12_26_9']) + self.assertEqual(list(self.data.columns[-3:]), ["MACD_12_26_9", "MACDh_12_26_9", "MACDs_12_26_9"]) def test_mom_ext(self): self.data.ta.mom(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'MOM_10') + self.assertEqual(self.data.columns[-1], "MOM_10") def test_ppo_ext(self): self.data.ta.ppo(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ['PPO_12_26_9', 'PPOh_12_26_9', 'PPOs_12_26_9']) + self.assertEqual(list(self.data.columns[-3:]), ["PPO_12_26_9", "PPOh_12_26_9", "PPOs_12_26_9"]) def test_psl_ext(self): self.data.ta.psl(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'PSL_12') + self.assertEqual(self.data.columns[-1], "PSL_12") def test_pvo_ext(self): self.data.ta.pvo(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ['PVO_12_26_9', 'PVOh_12_26_9', 'PVOs_12_26_9']) + self.assertEqual(list(self.data.columns[-3:]), ["PVO_12_26_9", "PVOh_12_26_9", "PVOs_12_26_9"]) def test_roc_ext(self): self.data.ta.roc(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'ROC_10') + self.assertEqual(self.data.columns[-1], "ROC_10") def test_rsi_ext(self): self.data.ta.rsi(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'RSI_14') + self.assertEqual(self.data.columns[-1], "RSI_14") def test_rvgi_ext(self): self.data.ta.rvgi(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-2:]), ['RVGI_14_4', 'RVGIs_14_4']) + self.assertEqual(list(self.data.columns[-2:]), ["RVGI_14_4", "RVGIs_14_4"]) def test_slope_ext(self): self.data.ta.slope(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'SLOPE_1') + self.assertEqual(self.data.columns[-1], "SLOPE_1") self.data.ta.slope(append=True, as_angle=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'ANGLEr_1') + self.assertEqual(self.data.columns[-1], "ANGLEr_1") self.data.ta.slope(append=True, as_angle=True, to_degrees=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'ANGLEd_1') + self.assertEqual(self.data.columns[-1], "ANGLEd_1") def test_stoch_ext(self): self.data.ta.stoch(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-4:]), ['STOCHFk_14', 'STOCHFd_3', 'STOCHk_5', 'STOCHd_3']) + self.assertEqual(list(self.data.columns[-4:]), ["STOCHFk_14", "STOCHFd_3", "STOCHk_5", "STOCHd_3"]) def test_trix_ext(self): self.data.ta.trix(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-2:]), ['TRIX_30_9', 'TRIXs_30_9']) + self.assertEqual(list(self.data.columns[-2:]), ["TRIX_30_9", "TRIXs_30_9"]) def test_tsi_ext(self): self.data.ta.tsi(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'TSI_13_25') + self.assertEqual(self.data.columns[-1], "TSI_13_25") def test_uo_ext(self): self.data.ta.uo(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'UO_7_14_28') + self.assertEqual(self.data.columns[-1], "UO_7_14_28") def test_willr_ext(self): self.data.ta.willr(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'WILLR_14') + self.assertEqual(self.data.columns[-1], "WILLR_14") diff --git a/tests/test_indicator_overlap.py b/tests/test_indicator_overlap.py index 401c816..902b3d0 100644 --- a/tests/test_indicator_overlap.py +++ b/tests/test_indicator_overlap.py @@ -15,11 +15,11 @@ class TestOverlap(TestCase): def setUpClass(cls): cls.data = sample_data cls.data.columns = cls.data.columns.str.lower() - cls.open = cls.data['open'] - cls.high = cls.data['high'] - cls.low = cls.data['low'] - cls.close = cls.data['close'] - if 'volume' in cls.data.columns: cls.volume = cls.data['volume'] + cls.open = cls.data["open"] + cls.high = cls.data["high"] + cls.low = cls.data["low"] + cls.close = cls.data["close"] + if "volume" in cls.data.columns: cls.volume = cls.data["volume"] @classmethod def tearDownClass(cls): @@ -27,7 +27,7 @@ class TestOverlap(TestCase): del cls.high del cls.low del cls.close - if hasattr(cls, 'volume'): del cls.volume + if hasattr(cls, "volume"): del cls.volume del cls.data @@ -39,7 +39,7 @@ class TestOverlap(TestCase): def test_dema(self): result = pandas_ta.dema(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'DEMA_10') + self.assertEqual(result.name, "DEMA_10") try: expected = tal.DEMA(self.close, 10) @@ -54,7 +54,7 @@ class TestOverlap(TestCase): def test_ema(self): result = pandas_ta.ema(self.close, presma=False) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'EMA_10') + self.assertEqual(result.name, "EMA_10") try: expected = tal.EMA(self.close, 10) @@ -69,17 +69,17 @@ class TestOverlap(TestCase): def test_fwma(self): result = pandas_ta.fwma(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'FWMA_10') + self.assertEqual(result.name, "FWMA_10") def test_hl2(self): result = pandas_ta.hl2(self.high, self.low) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'HL2') + self.assertEqual(result.name, "HL2") def test_hlc3(self): result = pandas_ta.hlc3(self.high, self.low, self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'HLC3') + self.assertEqual(result.name, "HLC3") try: expected = tal.TYPPRICE(self.high, self.low, self.close) @@ -94,24 +94,24 @@ class TestOverlap(TestCase): def test_hma(self): result = pandas_ta.hma(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'HMA_10') + self.assertEqual(result.name, "HMA_10") def test_kama(self): result = pandas_ta.kama(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'KAMA_10_2_30') + self.assertEqual(result.name, "KAMA_10_2_30") def test_ichimoku(self): ichimoku, span = pandas_ta.ichimoku(self.high, self.low, self.close) self.assertIsInstance(ichimoku, DataFrame) self.assertIsInstance(span, DataFrame) - self.assertEqual(ichimoku.name, 'ICHIMOKU_9_26_52') - self.assertEqual(span.name, 'ICHISPAN_9_26') + self.assertEqual(ichimoku.name, "ICHIMOKU_9_26_52") + self.assertEqual(span.name, "ICHISPAN_9_26") def test_linreg(self): result = pandas_ta.linreg(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'LR_14') + self.assertEqual(result.name, "LR_14") try: expected = tal.LINEARREG(self.close) @@ -126,7 +126,7 @@ class TestOverlap(TestCase): def test_linreg_angle(self): result = pandas_ta.linreg(self.close, angle=True) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'LRa_14') + self.assertEqual(result.name, "LRa_14") try: expected = tal.LINEARREG_ANGLE(self.close) @@ -141,7 +141,7 @@ class TestOverlap(TestCase): def test_linreg_intercept(self): result = pandas_ta.linreg(self.close, intercept=True) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'LRb_14') + self.assertEqual(result.name, "LRb_14") try: expected = tal.LINEARREG_INTERCEPT(self.close) @@ -156,12 +156,12 @@ class TestOverlap(TestCase): def test_linreg_r(self): result = pandas_ta.linreg(self.close, r=True) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'LRr_14') + self.assertEqual(result.name, "LRr_14") def test_linreg_slope(self): result = pandas_ta.linreg(self.close, slope=True) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'LRm_14') + self.assertEqual(result.name, "LRm_14") try: expected = tal.LINEARREG_SLOPE(self.close) @@ -176,7 +176,7 @@ class TestOverlap(TestCase): def test_midpoint(self): result = pandas_ta.midpoint(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'MIDPOINT_2') + self.assertEqual(result.name, "MIDPOINT_2") try: expected = tal.MIDPOINT(self.close, 2) @@ -191,7 +191,7 @@ class TestOverlap(TestCase): def test_midprice(self): result = pandas_ta.midprice(self.high, self.low) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'MIDPRICE_2') + self.assertEqual(result.name, "MIDPRICE_2") try: expected = tal.MIDPRICE(self.high, self.low, 2) @@ -206,27 +206,27 @@ class TestOverlap(TestCase): def test_ohlc4(self): result = pandas_ta.ohlc4(self.open, self.high, self.low, self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'OHLC4') + self.assertEqual(result.name, "OHLC4") def test_pwma(self): result = pandas_ta.pwma(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'PWMA_10') + self.assertEqual(result.name, "PWMA_10") def test_rma(self): result = pandas_ta.rma(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'RMA_10') + self.assertEqual(result.name, "RMA_10") def test_sinwma(self): result = pandas_ta.sinwma(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'SINWMA_14') + self.assertEqual(result.name, "SINWMA_14") def test_sma(self): result = pandas_ta.sma(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'SMA_10') + self.assertEqual(result.name, "SMA_10") try: expected = tal.SMA(self.close, 10) @@ -241,17 +241,17 @@ class TestOverlap(TestCase): def test_swma(self): result = pandas_ta.swma(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'SWMA_10') + self.assertEqual(result.name, "SWMA_10") def test_supertrend(self): result = pandas_ta.supertrend(self.high, self.low, self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'SUPERT_7_3.0') + self.assertEqual(result.name, "SUPERT_7_3.0") def test_t3(self): result = pandas_ta.t3(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'T3_10_0.7') + self.assertEqual(result.name, "T3_10_0.7") try: expected = tal.T3(self.close, 10) @@ -266,7 +266,7 @@ class TestOverlap(TestCase): def test_tema(self): result = pandas_ta.tema(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'TEMA_10') + self.assertEqual(result.name, "TEMA_10") try: expected = tal.TEMA(self.close, 10) @@ -281,7 +281,7 @@ class TestOverlap(TestCase): def test_trima(self): result = pandas_ta.trima(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'TRIMA_10') + self.assertEqual(result.name, "TRIMA_10") try: expected = tal.TRIMA(self.close, 10) @@ -296,17 +296,17 @@ class TestOverlap(TestCase): def test_vwap(self): result = pandas_ta.vwap(self.high, self.low, self.close, self.volume) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'VWAP') + self.assertEqual(result.name, "VWAP") def test_vwma(self): result = pandas_ta.vwma(self.close, self.volume) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'VWMA_10') + self.assertEqual(result.name, "VWMA_10") def test_wcp(self): result = pandas_ta.wcp(self.high, self.low, self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'WCP') + self.assertEqual(result.name, "WCP") try: expected = tal.WCLPRICE(self.high, self.low, self.close) @@ -321,7 +321,7 @@ class TestOverlap(TestCase): def test_wma(self): result = pandas_ta.wma(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'WMA_10') + self.assertEqual(result.name, "WMA_10") try: expected = tal.WMA(self.close, 10) @@ -336,4 +336,4 @@ class TestOverlap(TestCase): def test_zlma(self): result = pandas_ta.zlma(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'ZL_EMA_10') + self.assertEqual(result.name, "ZL_EMA_10") diff --git a/tests/test_indicator_overlap_ext.py b/tests/test_indicator_overlap_ext.py index 47ee84e..9ce1967 100644 --- a/tests/test_indicator_overlap_ext.py +++ b/tests/test_indicator_overlap_ext.py @@ -26,87 +26,87 @@ class TestOverlapExtension(TestCase): def test_dema_ext(self): self.data.ta.dema(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'DEMA_10') + self.assertEqual(self.data.columns[-1], "DEMA_10") def test_ema_ext(self): self.data.ta.ema(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'EMA_10') + self.assertEqual(self.data.columns[-1], "EMA_10") def test_fwma_ext(self): self.data.ta.fwma(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'FWMA_10') + self.assertEqual(self.data.columns[-1], "FWMA_10") def test_hl2_ext(self): self.data.ta.hl2(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'HL2') + self.assertEqual(self.data.columns[-1], "HL2") def test_hlc3_ext(self): self.data.ta.hlc3(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'HLC3') + self.assertEqual(self.data.columns[-1], "HLC3") def test_hma_ext(self): self.data.ta.hma(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'HMA_10') + self.assertEqual(self.data.columns[-1], "HMA_10") def test_kama_ext(self): self.data.ta.kama(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'KAMA_10_2_30') + self.assertEqual(self.data.columns[-1], "KAMA_10_2_30") def test_ichimoku_ext(self): self.data.ta.ichimoku(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-5:]), ['ISA_9', 'ISB_26', 'ITS_9', 'IKS_26', 'ICS_26']) + self.assertEqual(list(self.data.columns[-5:]), ["ISA_9", "ISB_26", "ITS_9", "IKS_26", "ICS_26"]) def test_linreg_ext(self): self.data.ta.linreg(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'LR_14') + self.assertEqual(self.data.columns[-1], "LR_14") def test_midpoint_ext(self): self.data.ta.midpoint(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'MIDPOINT_2') + self.assertEqual(self.data.columns[-1], "MIDPOINT_2") def test_midprice_ext(self): self.data.ta.midprice(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'MIDPRICE_2') + self.assertEqual(self.data.columns[-1], "MIDPRICE_2") def test_ohlc4_ext(self): self.data.ta.ohlc4(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'OHLC4') + self.assertEqual(self.data.columns[-1], "OHLC4") def test_pwma_ext(self): self.data.ta.pwma(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'PWMA_10') + self.assertEqual(self.data.columns[-1], "PWMA_10") def test_rma_ext(self): self.data.ta.rma(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'RMA_10') + self.assertEqual(self.data.columns[-1], "RMA_10") def test_sinwma_ext(self): self.data.ta.sinwma(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'SINWMA_14') + self.assertEqual(self.data.columns[-1], "SINWMA_14") def test_sma_ext(self): self.data.ta.sma(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'SMA_10') + self.assertEqual(self.data.columns[-1], "SMA_10") def test_swma_ext(self): self.data.ta.swma(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'SWMA_10') + self.assertEqual(self.data.columns[-1], "SWMA_10") def test_supertrend_ext(self): self.data.ta.supertrend(append=True) @@ -116,39 +116,39 @@ class TestOverlapExtension(TestCase): def test_t3_ext(self): self.data.ta.t3(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'T3_10_0.7') + self.assertEqual(self.data.columns[-1], "T3_10_0.7") def test_tema_ext(self): self.data.ta.tema(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'TEMA_10') + self.assertEqual(self.data.columns[-1], "TEMA_10") def test_trima_ext(self): self.data.ta.trima(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'TRIMA_10') + self.assertEqual(self.data.columns[-1], "TRIMA_10") def test_vwap_ext(self): self.data.ta.vwap(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'VWAP') + self.assertEqual(self.data.columns[-1], "VWAP") def test_vwma_ext(self): self.data.ta.vwma(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'VWMA_10') + self.assertEqual(self.data.columns[-1], "VWMA_10") def test_wcp_ext(self): self.data.ta.wcp(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'WCP') + self.assertEqual(self.data.columns[-1], "WCP") def test_wma_ext(self): self.data.ta.wma(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'WMA_10') + self.assertEqual(self.data.columns[-1], "WMA_10") def test_zlma_ext(self): self.data.ta.zlma(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'ZL_EMA_10') \ No newline at end of file + self.assertEqual(self.data.columns[-1], "ZL_EMA_10") \ No newline at end of file diff --git a/tests/test_indicator_performance.py b/tests/test_indicator_performance.py index 0fea777..d25e6ca 100644 --- a/tests/test_indicator_performance.py +++ b/tests/test_indicator_performance.py @@ -10,7 +10,7 @@ class TestPerformace(TestCase): @classmethod def setUpClass(cls): cls.data = sample_data - cls.close = cls.data['close'] + cls.close = cls.data["close"] cls.islong = cls.close > pandas_ta.sma(cls.close, length=50) @classmethod @@ -27,42 +27,42 @@ class TestPerformace(TestCase): def test_log_return(self): result = pandas_ta.log_return(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'LOGRET_1') + self.assertEqual(result.name, "LOGRET_1") def test_cum_log_return(self): result = pandas_ta.log_return(self.close, cumulative=True) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'CUMLOGRET_1') + self.assertEqual(result.name, "CUMLOGRET_1") def test_percent_return(self): result = pandas_ta.percent_return(self.close, cumulative=False) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'PCTRET_1') + self.assertEqual(result.name, "PCTRET_1") def test_cum_percent_return(self): result = pandas_ta.percent_return(self.close, cumulative=True) - self.assertEqual(result.name, 'CUMPCTRET_1') + self.assertEqual(result.name, "CUMPCTRET_1") def test_log_trend_return(self): result = pandas_ta.trend_return(self.close, self.islong, log=True, cumulative=False) - self.assertEqual(result.name, 'LTR') + self.assertEqual(result.name, "LTR") def test_cum_log_trend_return(self): result = pandas_ta.trend_return(self.close, self.islong, log=True, cumulative=True) - self.assertEqual(result.name, 'CLTR') + self.assertEqual(result.name, "CLTR") def test_variable_cum_log_trend_return(self): result = pandas_ta.trend_return(self.close, self.islong, log=True, cumulative=True, variable=True) - self.assertEqual(result.name, 'CLTR') + self.assertEqual(result.name, "CLTR") def test_pct_trend_return(self): result = pandas_ta.trend_return(self.close, self.islong, log=False, cumulative=False) - self.assertEqual(result.name, 'PTR') + self.assertEqual(result.name, "PTR") def test_cum_pct_trend_return(self): result = pandas_ta.trend_return(self.close, self.islong, log=False, cumulative=True) - self.assertEqual(result.name, 'CPTR') + self.assertEqual(result.name, "CPTR") def test_variable_pct_log_trend_return(self): result = pandas_ta.trend_return(self.close, self.islong, log=False, cumulative=True, variable=True) - self.assertEqual(result.name, 'CPTR') \ No newline at end of file + self.assertEqual(result.name, "CPTR") \ No newline at end of file diff --git a/tests/test_indicator_performance_ext.py b/tests/test_indicator_performance_ext.py index 8ea3e59..af3a0e3 100644 --- a/tests/test_indicator_performance_ext.py +++ b/tests/test_indicator_performance_ext.py @@ -10,7 +10,7 @@ class TestPerformaceExtension(TestCase): @classmethod def setUpClass(cls): cls.data = sample_data - cls.islong = cls.data['close'] > pandas_ta.sma(cls.data['close'], length=50) + cls.islong = cls.data["close"] > pandas_ta.sma(cls.data["close"], length=50) @classmethod def tearDownClass(cls): @@ -25,39 +25,39 @@ class TestPerformaceExtension(TestCase): def test_log_return_ext(self): self.data.ta.log_return(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'LOGRET_1') + self.assertEqual(self.data.columns[-1], "LOGRET_1") def test_cum_log_return_ext(self): self.data.ta.log_return(append=True, cumulative=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'CUMLOGRET_1') + self.assertEqual(self.data.columns[-1], "CUMLOGRET_1") def test_percent_return_ext(self): self.data.ta.percent_return(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'PCTRET_1') + self.assertEqual(self.data.columns[-1], "PCTRET_1") def test_cum_percent_return_ext(self): self.data.ta.percent_return(append=True, cumulative=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'CUMPCTRET_1') + self.assertEqual(self.data.columns[-1], "CUMPCTRET_1") def test_log_trend_return_ext(self): self.data.ta.trend_return(trend=self.islong, log=True, cumulative=False, append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'LTR') + self.assertEqual(self.data.columns[-1], "LTR") def test_cum_log_trend_return_ext(self): self.data.ta.trend_return(trend=self.islong, log=True, cumulative=True, append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'CLTR') + self.assertEqual(self.data.columns[-1], "CLTR") def test_pct_trend_return_ext(self): self.data.ta.trend_return(trend=self.islong, log=False, cumulative=False, append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'PTR') + self.assertEqual(self.data.columns[-1], "PTR") def test_cum_pct_trend_return_ext(self): self.data.ta.trend_return(trend=self.islong, log=False, cumulative=True, append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'CPTR') \ No newline at end of file + self.assertEqual(self.data.columns[-1], "CPTR") \ No newline at end of file diff --git a/tests/test_indicator_statistics.py b/tests/test_indicator_statistics.py index 57f2adc..5e58dce 100644 --- a/tests/test_indicator_statistics.py +++ b/tests/test_indicator_statistics.py @@ -14,11 +14,11 @@ class TestStatistics(TestCase): def setUpClass(cls): cls.data = sample_data cls.data.columns = cls.data.columns.str.lower() - cls.open = cls.data['open'] - cls.high = cls.data['high'] - cls.low = cls.data['low'] - cls.close = cls.data['close'] - if 'volume' in cls.data.columns: cls.volume = cls.data['volume'] + cls.open = cls.data["open"] + cls.high = cls.data["high"] + cls.low = cls.data["low"] + cls.close = cls.data["close"] + if "volume" in cls.data.columns: cls.volume = cls.data["volume"] @classmethod def tearDownClass(cls): @@ -26,7 +26,7 @@ class TestStatistics(TestCase): del cls.high del cls.low del cls.close - if hasattr(cls, 'volume'): del cls.volume + if hasattr(cls, "volume"): del cls.volume del cls.data @@ -36,37 +36,37 @@ class TestStatistics(TestCase): def test_entropy(self): result = pandas_ta.entropy(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'ENTP_10') + self.assertEqual(result.name, "ENTP_10") def test_kurtosis(self): result = pandas_ta.kurtosis(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'KURT_30') + self.assertEqual(result.name, "KURT_30") def test_mad(self): result = pandas_ta.mad(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'MAD_30') + self.assertEqual(result.name, "MAD_30") def test_median(self): result = pandas_ta.median(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'MEDIAN_30') + self.assertEqual(result.name, "MEDIAN_30") def test_quantile(self): result = pandas_ta.quantile(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'QTL_30_0.5') + self.assertEqual(result.name, "QTL_30_0.5") def test_skew(self): result = pandas_ta.skew(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'SKEW_30') + self.assertEqual(result.name, "SKEW_30") def test_stdev(self): result = pandas_ta.stdev(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'STDEV_30') + self.assertEqual(result.name, "STDEV_30") try: expected = tal.STDDEV(self.close, 30) @@ -81,7 +81,7 @@ class TestStatistics(TestCase): def test_variance(self): result = pandas_ta.variance(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'VAR_30') + self.assertEqual(result.name, "VAR_30") try: expected = tal.VAR(self.close, 30) @@ -96,4 +96,4 @@ class TestStatistics(TestCase): def test_zscore(self): result = pandas_ta.zscore(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'Z_30') \ No newline at end of file + self.assertEqual(result.name, "Z_30") \ No newline at end of file diff --git a/tests/test_indicator_statistics_ext.py b/tests/test_indicator_statistics_ext.py index 4a3727c..33b33ab 100644 --- a/tests/test_indicator_statistics_ext.py +++ b/tests/test_indicator_statistics_ext.py @@ -26,39 +26,39 @@ class TestStatisticsExtension(TestCase): def test_entropy_ext(self): self.data.ta.entropy(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'ENTP_10') + self.assertEqual(self.data.columns[-1], "ENTP_10") def test_kurtosis_ext(self): self.data.ta.kurtosis(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'KURT_30') + self.assertEqual(self.data.columns[-1], "KURT_30") def test_mad_ext(self): self.data.ta.mad(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'MAD_30') + self.assertEqual(self.data.columns[-1], "MAD_30") def test_median_ext(self): self.data.ta.median(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'MEDIAN_30') + self.assertEqual(self.data.columns[-1], "MEDIAN_30") def test_quantile_ext(self): self.data.ta.quantile(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'QTL_30_0.5') + self.assertEqual(self.data.columns[-1], "QTL_30_0.5") def test_skew_ext(self): self.data.ta.skew(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'SKEW_30') + self.assertEqual(self.data.columns[-1], "SKEW_30") def test_stdev_ext(self): self.data.ta.stdev(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'STDEV_30') + self.assertEqual(self.data.columns[-1], "STDEV_30") def test_variance_ext(self): self.data.ta.variance(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'VAR_30') + self.assertEqual(self.data.columns[-1], "VAR_30") diff --git a/tests/test_indicator_trend.py b/tests/test_indicator_trend.py index 90d04ff..f6a7152 100644 --- a/tests/test_indicator_trend.py +++ b/tests/test_indicator_trend.py @@ -14,11 +14,11 @@ class TestTrend(TestCase): def setUpClass(cls): cls.data = sample_data cls.data.columns = cls.data.columns.str.lower() - cls.open = cls.data['open'] - cls.high = cls.data['high'] - cls.low = cls.data['low'] - cls.close = cls.data['close'] - if 'volume' in cls.data.columns: cls.volume = cls.data['volume'] + cls.open = cls.data["open"] + cls.high = cls.data["high"] + cls.low = cls.data["low"] + cls.close = cls.data["close"] + if "volume" in cls.data.columns: cls.volume = cls.data["volume"] @classmethod def tearDownClass(cls): @@ -26,7 +26,7 @@ class TestTrend(TestCase): del cls.high del cls.low del cls.close - if hasattr(cls, 'volume'): del cls.volume + if hasattr(cls, "volume"): del cls.volume del cls.data @@ -37,7 +37,7 @@ class TestTrend(TestCase): def test_adx(self): result = pandas_ta.adx(self.high, self.low, self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'ADX_14') + self.assertEqual(result.name, "ADX_14") try: expected = tal.ADX(self.high, self.low, self.close) @@ -52,16 +52,16 @@ class TestTrend(TestCase): def test_amat(self): result = pandas_ta.amat(self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'AMAT_EMA_8_21_2') + self.assertEqual(result.name, "AMAT_EMA_8_21_2") def test_aroon(self): result = pandas_ta.aroon(self.high, self.low) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'AROON_14') + self.assertEqual(result.name, "AROON_14") try: expected = tal.AROON(self.high, self.low) - expecteddf = DataFrame({'AROOND_14': expected[0], 'AROONU_14': expected[1]}) + expecteddf = DataFrame({"AROOND_14": expected[0], "AROONU_14": expected[1]}) pdt.assert_frame_equal(result, expecteddf) except AssertionError as ae: try: @@ -92,44 +92,44 @@ class TestTrend(TestCase): def test_chop(self): result = pandas_ta.chop(self.high, self.low, self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'CHOP_14_1_100') + self.assertEqual(result.name, "CHOP_14_1_100") def test_cksp(self): result = pandas_ta.cksp(self.high, self.low, self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'CKSP_10_1_9') + self.assertEqual(result.name, "CKSP_10_1_9") def test_decreasing(self): result = pandas_ta.decreasing(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'DEC_1') + self.assertEqual(result.name, "DEC_1") def test_dpo(self): result = pandas_ta.dpo(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'DPO_20') + self.assertEqual(result.name, "DPO_20") def test_increasing(self): result = pandas_ta.increasing(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'INC_1') + self.assertEqual(result.name, "INC_1") def test_linear_decay(self): result = pandas_ta.linear_decay(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'LDECAY_5') + self.assertEqual(result.name, "LDECAY_5") def test_long_run(self): result = pandas_ta.long_run(self.close, self.open) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'LR_2') + self.assertEqual(result.name, "LR_2") def test_psar(self): result = pandas_ta.psar(self.high, self.low) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'PSAR_0.02_0.2') + self.assertEqual(result.name, "PSAR_0.02_0.2") - # Combine Long and Short SAR's into one SAR value + # Combine Long and Short SAR"s into one SAR value psar = result[result.columns[:2]].fillna(0) psar = psar[psar.columns[0]] + psar[psar.columns[1]] psar.name = result.name @@ -147,14 +147,14 @@ class TestTrend(TestCase): def test_qstick(self): result = pandas_ta.qstick(self.open, self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'QS_10') + self.assertEqual(result.name, "QS_10") def test_short_run(self): result = pandas_ta.short_run(self.close, self.open) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'SR_2') + self.assertEqual(result.name, "SR_2") def test_vortex(self): result = pandas_ta.vortex(self.high, self.low, self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'VTX_14') \ No newline at end of file + self.assertEqual(result.name, "VTX_14") \ No newline at end of file diff --git a/tests/test_indicator_trend_ext.py b/tests/test_indicator_trend_ext.py index 5f06337..a3df15f 100644 --- a/tests/test_indicator_trend_ext.py +++ b/tests/test_indicator_trend_ext.py @@ -26,79 +26,79 @@ class TestTrendExtension(TestCase): def test_adx_ext(self): self.data.ta.adx(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ['ADX_14', 'DMP_14', 'DMN_14']) + self.assertEqual(list(self.data.columns[-3:]), ["ADX_14", "DMP_14", "DMN_14"]) def test_amat_ext(self): self.data.ta.amat(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-2:]), ['AMAT_LR_2', 'AMAT_SR_2']) + self.assertEqual(list(self.data.columns[-2:]), ["AMAT_LR_2", "AMAT_SR_2"]) def test_aroon_ext(self): self.data.ta.aroon(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ['AROOND_14', 'AROONU_14', 'AROONOSC_14']) + self.assertEqual(list(self.data.columns[-3:]), ["AROOND_14", "AROONU_14", "AROONOSC_14"]) def test_chop_ext(self): self.data.ta.chop(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'CHOP_14_1_100') + self.assertEqual(self.data.columns[-1], "CHOP_14_1_100") def test_cksp_ext(self): self.data.ta.cksp(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'CKSPs_10_1_9') + self.assertEqual(self.data.columns[-1], "CKSPs_10_1_9") def test_decreasing_ext(self): self.data.ta.decreasing(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'DEC_1') + self.assertEqual(self.data.columns[-1], "DEC_1") def test_dpo_ext(self): self.data.ta.dpo(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'DPO_20') + self.assertEqual(self.data.columns[-1], "DPO_20") def test_increasing_ext(self): self.data.ta.increasing(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'INC_1') + self.assertEqual(self.data.columns[-1], "INC_1") def test_linear_decay_ext(self): self.data.ta.linear_decay(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'LDECAY_5') + self.assertEqual(self.data.columns[-1], "LDECAY_5") def test_long_run_ext(self): # Nothing passed, return self self.assertEqual(self.data.ta.long_run(append=True).shape, self.data.shape) - fast = self.data.ta.ema('close', 8) - slow = self.data.ta.ema('close', 21) + fast = self.data.ta.ema("close", 8) + slow = self.data.ta.ema("close", 21) self.data.ta.long_run(fast, slow, append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'LR_2') + self.assertEqual(self.data.columns[-1], "LR_2") def test_psar_ext(self): self.data.ta.psar(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-4:]), ['PSARl_0.02_0.2', 'PSARs_0.02_0.2', 'PSARaf_0.02_0.2', 'PSARr_0.02_0.2']) + self.assertEqual(list(self.data.columns[-4:]), ["PSARl_0.02_0.2", "PSARs_0.02_0.2", "PSARaf_0.02_0.2", "PSARr_0.02_0.2"]) def test_qstick_ext(self): self.data.ta.qstick(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'QS_10') + self.assertEqual(self.data.columns[-1], "QS_10") def test_short_run_ext(self): # Nothing passed, return self self.assertEqual(self.data.ta.short_run(append=True).shape, self.data.shape) - fast = self.data.ta.ema('close', 8) - slow = self.data.ta.ema('close', 21) + fast = self.data.ta.ema("close", 8) + slow = self.data.ta.ema("close", 21) self.data.ta.short_run(fast, slow, append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'SR_2') + self.assertEqual(self.data.columns[-1], "SR_2") def test_vortext_ext(self): self.data.ta.vortex(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-2:]), ['VTXP_14', 'VTXM_14']) + self.assertEqual(list(self.data.columns[-2:]), ["VTXP_14", "VTXM_14"]) diff --git a/tests/test_indicator_volatility.py b/tests/test_indicator_volatility.py index 15585cb..a06296f 100644 --- a/tests/test_indicator_volatility.py +++ b/tests/test_indicator_volatility.py @@ -14,11 +14,11 @@ class TestVolatility(TestCase): def setUpClass(cls): cls.data = sample_data cls.data.columns = cls.data.columns.str.lower() - cls.open = cls.data['open'] - cls.high = cls.data['high'] - cls.low = cls.data['low'] - cls.close = cls.data['close'] - if 'volume' in cls.data.columns: cls.volume = cls.data['volume'] + cls.open = cls.data["open"] + cls.high = cls.data["high"] + cls.low = cls.data["low"] + cls.close = cls.data["close"] + if "volume" in cls.data.columns: cls.volume = cls.data["volume"] @classmethod def tearDownClass(cls): @@ -26,7 +26,7 @@ class TestVolatility(TestCase): del cls.high del cls.low del cls.close - if hasattr(cls, 'volume'): del cls.volume + if hasattr(cls, "volume"): del cls.volume del cls.data @@ -37,17 +37,17 @@ class TestVolatility(TestCase): def test_aberration(self): result = pandas_ta.aberration(self.high, self.low, self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'ABER_5_15') + self.assertEqual(result.name, "ABER_5_15") def test_accbands(self): result = pandas_ta.accbands(self.high, self.low, self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'ACCBANDS_20') + self.assertEqual(result.name, "ACCBANDS_20") def test_atr(self): result = pandas_ta.atr(self.high, self.low, self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'ATR_14') + self.assertEqual(result.name, "ATR_14") try: expected = tal.ATR(self.high, self.low, self.close) @@ -62,11 +62,11 @@ class TestVolatility(TestCase): def test_bbands(self): result = pandas_ta.bbands(self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'BBANDS_5') + self.assertEqual(result.name, "BBANDS_5_2.0") try: expected = tal.BBANDS(self.close) - expecteddf = DataFrame({'BBL_5': expected[0], 'BBM_5': expected[1], 'BBU_5': expected[2]}) + expecteddf = DataFrame({"BBL_5_2.0": expected[0], "BBM_5_2.0": expected[1], "BBU_5_2.0": expected[2]}) pdt.assert_frame_equal(result, expecteddf) except AssertionError as ae: try: @@ -90,27 +90,27 @@ class TestVolatility(TestCase): def test_donchian(self): result = pandas_ta.donchian(self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'DC_10_20') + self.assertEqual(result.name, "DC_10_20") result = pandas_ta.donchian(self.close, lower_length=20, upper_length=5) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'DC_20_5') + self.assertEqual(result.name, "DC_20_5") def test_kc(self): result = pandas_ta.kc(self.high, self.low, self.close) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'KC_20') + self.assertEqual(result.name, "KC_20") def test_massi(self): result = pandas_ta.massi(self.high, self.low) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'MASSI_9_25') + self.assertEqual(result.name, "MASSI_9_25") def test_natr(self): result = pandas_ta.natr(self.high, self.low, self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'NATR_14') + self.assertEqual(result.name, "NATR_14") try: expected = tal.NATR(self.high, self.low, self.close) @@ -125,25 +125,25 @@ class TestVolatility(TestCase): def test_pdist(self): result = pandas_ta.pdist(self.open, self.high, self.low, self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'PDIST') + self.assertEqual(result.name, "PDIST") def test_rvi(self): result = pandas_ta.rvi(self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'RVI_14') + self.assertEqual(result.name, "RVI_14") result = pandas_ta.rvi(self.close, self.high, self.low, refined=True) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'RVIr_14') + self.assertEqual(result.name, "RVIr_14") result = pandas_ta.rvi(self.close, self.high, self.low, thirds=True) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'RVIt_14') + self.assertEqual(result.name, "RVIt_14") def test_true_range(self): result = pandas_ta.true_range(self.high, self.low, self.close) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'TRUERANGE_1') + self.assertEqual(result.name, "TRUERANGE_1") try: expected = tal.TRANGE(self.high, self.low, self.close) diff --git a/tests/test_indicator_volatility_ext.py b/tests/test_indicator_volatility_ext.py index 14d743f..78d5a94 100644 --- a/tests/test_indicator_volatility_ext.py +++ b/tests/test_indicator_volatility_ext.py @@ -23,64 +23,64 @@ class TestVolatilityExtension(TestCase): def test_aberration_ext(self): self.data.ta.aberration(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-4:]), ['ABER_ZG_5_15', 'ABER_SG_5_15', 'ABER_XG_5_15', 'ABER_ATR_5_15']) + self.assertEqual(list(self.data.columns[-4:]), ["ABER_ZG_5_15", "ABER_SG_5_15", "ABER_XG_5_15", "ABER_ATR_5_15"]) def test_accbands_ext(self): self.data.ta.accbands(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ['ACCBL_20', 'ACCBM_20', 'ACCBU_20']) + self.assertEqual(list(self.data.columns[-3:]), ["ACCBL_20", "ACCBM_20", "ACCBU_20"]) def test_atr_ext(self): self.data.ta.atr(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'ATR_14') + self.assertEqual(self.data.columns[-1], "ATR_14") def test_bbands_ext(self): self.data.ta.bbands(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ['BBL_5', 'BBM_5', 'BBU_5']) + self.assertEqual(list(self.data.columns[-3:]), ["BBL_5_2.0", "BBM_5_2.0", "BBU_5_2.0"]) def test_donchian_ext(self): self.data.ta.donchian(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ['DCL_10_20', 'DCM_10_20', 'DCU_10_20']) + self.assertEqual(list(self.data.columns[-3:]), ["DCL_10_20", "DCM_10_20", "DCU_10_20"]) def test_kc_ext(self): self.data.ta.kc(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ['KCL_20', 'KCB_20', 'KCU_20']) + self.assertEqual(list(self.data.columns[-3:]), ["KCL_20", "KCB_20", "KCU_20"]) def test_massi_ext(self): self.data.ta.massi(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'MASSI_9_25') + self.assertEqual(self.data.columns[-1], "MASSI_9_25") def test_natr_ext(self): self.data.ta.natr(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'NATR_14') + self.assertEqual(self.data.columns[-1], "NATR_14") def test_pdist_ext(self): self.data.ta.pdist(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'PDIST') + self.assertEqual(self.data.columns[-1], "PDIST") def test_rvi_ext(self): self.data.ta.rvi(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'RVI_14') + self.assertEqual(self.data.columns[-1], "RVI_14") def test_rvi_refined_ext(self): self.data.ta.rvi(refined=True, append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'RVIr_14') + self.assertEqual(self.data.columns[-1], "RVIr_14") def test_rvi_thirds_ext(self): self.data.ta.rvi(thirds=True, append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'RVIt_14') + self.assertEqual(self.data.columns[-1], "RVIt_14") def test_true_range_ext(self): self.data.ta.true_range(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'TRUERANGE_1') \ No newline at end of file + self.assertEqual(self.data.columns[-1], "TRUERANGE_1") \ No newline at end of file diff --git a/tests/test_indicator_volume.py b/tests/test_indicator_volume.py index 9e30d6d..7dc1b47 100644 --- a/tests/test_indicator_volume.py +++ b/tests/test_indicator_volume.py @@ -14,11 +14,11 @@ class TestVolume(TestCase): def setUpClass(cls): cls.data = sample_data cls.data.columns = cls.data.columns.str.lower() - cls.open = cls.data['open'] - cls.high = cls.data['high'] - cls.low = cls.data['low'] - cls.close = cls.data['close'] - if 'volume' in cls.data.columns: cls.volume_ = cls.data['volume'] + cls.open = cls.data["open"] + cls.high = cls.data["high"] + cls.low = cls.data["low"] + cls.close = cls.data["close"] + if "volume" in cls.data.columns: cls.volume_ = cls.data["volume"] @classmethod def tearDownClass(cls): @@ -26,7 +26,7 @@ class TestVolume(TestCase): del cls.high del cls.low del cls.close - if hasattr(cls, 'volume'): del cls.volume_ + if hasattr(cls, "volume"): del cls.volume_ del cls.data @@ -37,7 +37,7 @@ class TestVolume(TestCase): def test_ad(self): result = pandas_ta.ad(self.high, self.low, self.close, self.volume_) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'AD') + self.assertEqual(result.name, "AD") try: expected = tal.AD(self.high, self.low, self.close, self.volume_) @@ -52,12 +52,12 @@ class TestVolume(TestCase): def test_ad_open(self): result = pandas_ta.ad(self.high, self.low, self.close, self.volume_, self.open) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'ADo') + self.assertEqual(result.name, "ADo") def test_adosc(self): result = pandas_ta.adosc(self.high, self.low, self.close, self.volume_) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'ADOSC_3_10') + self.assertEqual(result.name, "ADOSC_3_10") try: expected = tal.ADOSC(self.high, self.low, self.close, self.volume_) @@ -72,27 +72,27 @@ class TestVolume(TestCase): def test_aobv(self): result = pandas_ta.aobv(self.close, self.volume_) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'AOBV_EMA_2_4_2_2_2') + self.assertEqual(result.name, "AOBV_EMA_2_4_2_2_2") def test_cmf(self): result = pandas_ta.cmf(self.high, self.low, self.close, self.volume_) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'CMF_20') + self.assertEqual(result.name, "CMF_20") def test_efi(self): result = pandas_ta.efi(self.close, self.volume_) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'EFI_13') + self.assertEqual(result.name, "EFI_13") def test_eom(self): result = pandas_ta.eom(self.high, self.low, self.close, self.volume_) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'EOM_14_100000000') + self.assertEqual(result.name, "EOM_14_100000000") def test_mfi(self): result = pandas_ta.mfi(self.high, self.low, self.close, self.volume_) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'MFI_14') + self.assertEqual(result.name, "MFI_14") try: expected = tal.MFI(self.high, self.low, self.close, self.volume_) @@ -107,12 +107,12 @@ class TestVolume(TestCase): def test_nvi(self): result = pandas_ta.nvi(self.close, self.volume_) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'NVI_1') + self.assertEqual(result.name, "NVI_1") def test_obv(self): result = pandas_ta.obv(self.close, self.volume_) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'OBV') + self.assertEqual(result.name, "OBV") try: expected = tal.OBV(self.close, self.volume_) @@ -127,19 +127,19 @@ class TestVolume(TestCase): def test_pvi(self): result = pandas_ta.pvi(self.close, self.volume_) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'PVI_1') + self.assertEqual(result.name, "PVI_1") def test_pvol(self): result = pandas_ta.pvol(self.close, self.volume_) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'PVOL') + self.assertEqual(result.name, "PVOL") def test_pvt(self): result = pandas_ta.pvt(self.close, self.volume_) self.assertIsInstance(result, Series) - self.assertEqual(result.name, 'PVT') + self.assertEqual(result.name, "PVT") def test_vp(self): result = pandas_ta.vp(self.close, self.volume_) self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'VP_10') + self.assertEqual(result.name, "VP_10") diff --git a/tests/test_indicator_volume_ext.py b/tests/test_indicator_volume_ext.py index da7efac..2f4e99d 100644 --- a/tests/test_indicator_volume_ext.py +++ b/tests/test_indicator_volume_ext.py @@ -10,7 +10,7 @@ class TestVolumeExtension(TestCase): @classmethod def setUpClass(cls): cls.data = sample_data - cls.open = cls.data['open'] + cls.open = cls.data["open"] @classmethod def tearDownClass(cls): @@ -25,49 +25,49 @@ class TestVolumeExtension(TestCase): def test_ad_ext(self): self.data.ta.ad(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'AD') + self.assertEqual(self.data.columns[-1], "AD") def test_ad_open_ext(self): self.data.ta.ad(open_=self.open, append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'ADo') + self.assertEqual(self.data.columns[-1], "ADo") def test_adosc_ext(self): self.data.ta.adosc(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'ADOSC_3_10') + self.assertEqual(self.data.columns[-1], "ADOSC_3_10") def test_aobv_ext(self): self.data.ta.aobv(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-7:]), ['OBV', 'OBV_min_2', 'OBV_max_2', 'OBV_EMA_2', 'OBV_EMA_4', 'AOBV_LR_2', 'AOBV_SR_2']) - # Remove 'OBV' so it does not interfere with test_obv_ext() - self.data.drop('OBV', axis=1, inplace=True) + self.assertEqual(list(self.data.columns[-7:]), ["OBV", "OBV_min_2", "OBV_max_2", "OBV_EMA_2", "OBV_EMA_4", "AOBV_LR_2", "AOBV_SR_2"]) + # Remove "OBV" so it does not interfere with test_obv_ext() + self.data.drop("OBV", axis=1, inplace=True) def test_cmf_ext(self): self.data.ta.cmf(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'CMF_20') + self.assertEqual(self.data.columns[-1], "CMF_20") def test_efi_ext(self): self.data.ta.efi(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'EFI_13') + self.assertEqual(self.data.columns[-1], "EFI_13") def test_eom_ext(self): self.data.ta.eom(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'EOM_14_100000000') + self.assertEqual(self.data.columns[-1], "EOM_14_100000000") def test_mfi_ext(self): self.data.ta.mfi(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'MFI_14') + self.assertEqual(self.data.columns[-1], "MFI_14") def test_nvi_ext(self): self.data.ta.nvi(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'NVI_1') + self.assertEqual(self.data.columns[-1], "NVI_1") # print(f"\nNVI: {self.data.columns[-1]}") # print(f"NVI: {self.data.columns}") @@ -76,24 +76,24 @@ class TestVolumeExtension(TestCase): self.assertIsInstance(self.data, DataFrame) # print(f"\nOBV: {self.data.columns[-1]}") # print(f"OBV: {self.data.columns}") - self.assertEqual(self.data.columns[-1], 'OBV') + self.assertEqual(self.data.columns[-1], "OBV") def test_pvi_ext(self): self.data.ta.pvi(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'PVI_1') + self.assertEqual(self.data.columns[-1], "PVI_1") def test_pvol_ext(self): self.data.ta.pvol(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'PVOL') + self.assertEqual(self.data.columns[-1], "PVOL") def test_pvt_ext(self): self.data.ta.pvt(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(self.data.columns[-1], 'PVT') + self.assertEqual(self.data.columns[-1], "PVT") def test_vp_ext(self): result = self.data.ta.vp() self.assertIsInstance(result, DataFrame) - self.assertEqual(result.name, 'VP_10') \ No newline at end of file + self.assertEqual(result.name, "VP_10") \ No newline at end of file