ENH #254 BUG #271 thermo DEP trade_return ENH tsignals erf DOC updates

This commit is contained in:
Kevin Johnson
2021-04-22 10:19:48 -07:00
parent 37e3aeb7fc
commit 294a1c5a00
22 changed files with 1598 additions and 1534 deletions
+1 -2
View File
@@ -143,5 +143,4 @@ data/SPY_D_TV2.csv
data/SPY_D_TV3.csv
data/TV_5min.csv
data/tulip.csv
examples/*.csv
examples/Chande_Kroll_Stop.ipynb
examples/*.csv
+8 -15
View File
@@ -15,7 +15,7 @@ Pandas TA - A Technical Analysis Library in Python 3
![Example Chart](/images/TA_Chart.png)
_Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that leverages the Pandas library with more than 130 Indicators and Utility functions and more than 60 TA Lib Candlestick Patterns. Many commonly used indicators are included, such as: _Simple Moving Average_ (**sma**) _Moving Average Convergence Divergence_ (**macd**), _Hull Exponential Moving Average_ (**hma**), _Bollinger Bands_ (**bbands**), _On-Balance Volume_ (**obv**), _Aroon & Aroon Oscillator_ (**aroon**), _Squeeze_ (**squeeze**) and **_many more_**.
_Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that leverages the Pandas library with more than 130 Indicators and Utility functions and more than 60 TA Lib Candlestick Patterns. Many commonly used indicators are included, such as: _Candle Pattern_(**cdl_pattern**), _Simple Moving Average_ (**sma**) _Moving Average Convergence Divergence_ (**macd**), _Hull Exponential Moving Average_ (**hma**), _Bollinger Bands_ (**bbands**), _On-Balance Volume_ (**obv**), _Aroon & Aroon Oscillator_ (**aroon**), _Squeeze_ (**squeeze**) and **_many more_**.
**Note:** _TA Lib_ must be installed to use **all** the Candlestick Patterns. ```pip install TA-Lib```. If _TA Lib_ is not installed, then only the builtin Candlestick Patterns will be available.
@@ -43,9 +43,9 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever
* [Cycles](#cycles-1)
* [Momentum](#momentum-39)
* [Overlap](#overlap-31)
* [Performance](#performance-4)
* [Performance](#performance-3)
* [Statistics](#statistics-9)
* [Trend](#trend-15)
* [Trend](#trend-16)
* [Utility](#utility-5)
* [Volatility](#volatility-13)
* [Volume](#volume-14)
@@ -97,7 +97,7 @@ $ pip install pandas_ta
Latest Version
--------------
Best choice! Version: *0.2.73b*
Best choice! Version: *0.2.74b*
```sh
$ pip install -U git+https://github.com/twopirllc/pandas-ta
```
@@ -636,19 +636,12 @@ Patterns that are **not bold**, require TA-Lib to be installed: ```pip install T
```python
# Get all candle patterns (This is the default behaviour)
df = df.ta.cdl_pattern(name="all")
# Or
df.ta.cdl("all", append=True) # = df.ta.cdl_pattern("all", append=True)
# Get only one pattern
df = df.ta.cdl_pattern(name="doji")
# Or
df.ta.cdl("doji", append=True)
# Get some patterns
df = df.ta.cdl_pattern(name=["doji", "inside"])
# Or
df.ta.cdl(["doji", "inside"], append=True)
```
<br/>
@@ -754,14 +747,13 @@ df.ta.cdl(["doji", "inside"], append=True)
<br/>
### **Performance** (4)
### **Performance** (3)
Use parameter: cumulative=**True** for cumulative results.
* _Draw Down_: **drawdown**
* _Log Return_: **log_return**
* _Percent Return_: **percent_return**
* _Trend Return_: **trend_return**
| _Percent Return_ (Cumulative) with _Simple Moving Average_ (SMA) |
|:--------:|
@@ -785,7 +777,7 @@ Use parameter: cumulative=**True** for cumulative results.
| ![Example Z Score](/images/SPY_ZScore.png) |
<br/>
### **Trend** (15)
### **Trend** (16)
* _Average Directional Movement Index_: **adx**
* Also includes **dmp** and **dmn** in the resultant DataFrame.
@@ -803,6 +795,7 @@ Use parameter: cumulative=**True** for cumulative results.
* _Parabolic Stop and Reverse_: **psar**
* _Q Stick_: **qstick**
* _Short Run_: **short_run**
* _Trend Signals_: **tsignals**
* _TTM Trend_: **ttm_trend**
* _Vortex_: **vortex**
@@ -902,7 +895,7 @@ result = ta.cagr(df.close)
<br />
## **Breaking Indicators**
* _Trend Return_ (**trend_return**) when given a trend Series like ```close > sma(close, 50)``` it now returns by default log and cumulative log returns of the trend as well as the Trends, Trades, Trade Entries and Trade Exits of that trend. Now compatible with [**vectorbt**](https://github.com/polakowo/vectorbt) by setting ```asbool=True``` to get boolean Trade Entries and Exits. See: ```help(ta.trend_return)```
* _Trend Return_ (**trend_return**) has been removed and replaced with **tsignals**. When given a trend Series like ```close > sma(close, 50)``` it returns the Trend, Trade Entries and Trade Exits of that trend to make it compatible with [**vectorbt**](https://github.com/polakowo/vectorbt) by setting ```asbool=True``` to get boolean Trade Entries and Exits. See: ```help(ta.tsignals)```
<br/>
+536 -303
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+203 -313
View File
File diff suppressed because one or more lines are too long
+9 -8
View File
@@ -22,18 +22,19 @@ import pandas_ta as ta # pip install pandas_ta
def colors(colors: str = None, default: str = "GrRd"):
aliases = {
# Pairs
"GrRd": ["green", "red"],
"RdGr": ["red", "green"],
"BkGy": ["black", "gray"],
"BkSv": ["black", "silver"],
"BkPr": ["black", "purple"],
"BkBl": ["black", "blue"],
"GyBk": ["gray", "black"],
"GySv": ["gray", "silver"],
"GyPr": ["gray", "purple"],
"GyBl": ["gray", "blue"],
"SvGy": ["silver", "gray"],
"FcLi": ["fuchsia", "lime"],
"GrRd": ["green", "red"],
"GyBk": ["gray", "black"],
"GyBl": ["gray", "blue"],
"GyOr": ["gray", "orange"],
"GyPr": ["gray", "purple"],
"GySv": ["gray", "silver"],
"RdGr": ["red", "green"],
"SvGy": ["silver", "gray"],
# Triples
"BkGrRd": ["black", "green", "red"],
"BkBlPr": ["black", "blue", "purple"],
@@ -106,7 +107,7 @@ class Watchlist(object):
def _drop_columns(self, df: pd.DataFrame, cols: list = None) -> pd.DataFrame:
if cols is None or not isinstance(cols, list):
cols = ["Unnamed: 0", "date", "split_coefficient", "dividend"]
cols = ["Unnamed: 0", "date", "split", "split_coefficient", "dividend", "dividends"]
else: cols
"""Helper methods to drop columns silently."""
df_columns = list(df.columns)
+4 -4
View File
@@ -40,7 +40,7 @@ Imports = {
Category = {
# Candles
"candles": [
"cdl", "cdl_pattern", "cdl_z", "ha"
"cdl_pattern", "cdl_z", "ha"
],
# Cycles
"cycles": ["ebsw"],
@@ -60,7 +60,7 @@ Category = {
"vidya", "vwap", "vwma", "wcp", "wma", "zlma"
],
# Performance
"performance": ["log_return", "percent_return", "trend_return"],
"performance": ["log_return", "percent_return"],
# Statistics
"statistics": [
"entropy", "kurtosis", "mad", "median", "quantile", "skew", "stdev",
@@ -69,8 +69,8 @@ Category = {
# Trend
"trend": [
"adx", "amat", "aroon", "chop", "cksp", "decay", "decreasing", "dpo",
"increasing", "long_run", "psar", "qstick", "short_run", "ttm_trend",
"vortex"
"increasing", "long_run", "psar", "qstick", "short_run", "tsignals",
"ttm_trend", "vortex"
],
# Volatility
"volatility": [
+38 -22
View File
@@ -641,14 +641,13 @@ class AnalysisIndicators(BasePandasObject):
"above_value",
"below",
"below_value",
"cdl", # Alias for "cdl_pattern"
"cross",
"cross_value",
# "data", # reserved
"long_run",
"short_run",
"trend_return",
"td_seq", # Performance exclusion
"tsignals",
"vp",
]
@@ -708,6 +707,10 @@ class AnalysisIndicators(BasePandasObject):
if has_col_names:
use_multiprocessing = False
if Imports["tqdm"]:
# from tqdm import tqdm
from tqdm import tqdm
if use_multiprocessing:
_total_ta = len(ta)
pool = Pool(self.cores)
@@ -731,9 +734,15 @@ class AnalysisIndicators(BasePandasObject):
default_ta = [(ind, tuple(), kwargs) for ind in ta]
# All and Categorical multiprocessing pool.
if all_ordered:
results = pool.imap(self._mp_worker, default_ta, _chunksize) # Order over Speed
if Imports["tqdm"]:
results = tqdm(pool.imap(self._mp_worker, default_ta, _chunksize)) # Order over Speed
else:
results = pool.imap(self._mp_worker, default_ta, _chunksize) # Order over Speed
else:
results = pool.imap_unordered(self._mp_worker, default_ta, _chunksize) # Speed over Order
if Imports["tqdm"]:
results = tqdm(pool.imap_unordered(self._mp_worker, default_ta, _chunksize)) # Speed over Order
else:
results = pool.imap_unordered(self._mp_worker, default_ta, _chunksize) # Speed over Order
if results is None:
print(f"[X] ta.strategy('{name}') has no results.")
return
@@ -751,15 +760,25 @@ class AnalysisIndicators(BasePandasObject):
print(f"[i] No mulitproccessing (cores = 0).")
if mode["custom"]:
for ind in ta:
params = ind["params"] if "params" in ind and isinstance(ind["params"], tuple) else tuple()
getattr(self, ind["kind"])(*params, **{**ind, **kwargs})
if Imports["tqdm"] and verbose:
pbar = tqdm(ta, f"[i] Progress")
for ind in pbar:
params = ind["params"] if "params" in ind and isinstance(ind["params"], tuple) else tuple()
getattr(self, ind["kind"])(*params, **{**ind, **kwargs})
else:
for ind in ta:
params = ind["params"] if "params" in ind and isinstance(ind["params"], tuple) else tuple()
getattr(self, ind["kind"])(*params, **{**ind, **kwargs})
else:
for ind in ta:
getattr(self, ind)(*tuple(), **kwargs)
if Imports["tqdm"] and verbose:
pbar = tqdm(ta, f"[i] Progress")
for ind in pbar:
getattr(self, ind)(*tuple(), **kwargs)
else:
for ind in ta:
getattr(self, ind)(*tuple(), **kwargs)
# Apply prefixes/suffixes and appends indicator results to the
# DataFrame
# Apply prefixes/suffixes and appends indicator results to the DataFrame
[self._post_process(r, **kwargs) for r in results]
if verbose:
@@ -823,7 +842,7 @@ class AnalysisIndicators(BasePandasObject):
print(f"[X] DataFrame is empty: {df.shape}")
return
else:
if kwargs.pop("lc_input", False):
if kwargs.pop("lc_cols", False):
df.index.name = df.index.name.lower()
df.columns = df.columns.str.lower()
self._df = df
@@ -842,8 +861,6 @@ class AnalysisIndicators(BasePandasObject):
result = cdl_pattern(open_=open_, high=high, low=low, close=close, name=name, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
cdl = cdl_pattern # Alias for cdl_pattern
def cdl_z(self, full=None, offset=None, **kwargs):
open_ = self._get_column(kwargs.pop("open", "open"))
high = self._get_column(kwargs.pop("high", "high"))
@@ -1299,14 +1316,6 @@ class AnalysisIndicators(BasePandasObject):
result = percent_return(close=close, length=length, cumulative=cumulative, percent=percent, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def trend_return(self, trend=None, log=True, asbool=None, offset=None, trend_reset=None, **kwargs):
if trend is None:
return self._df
else:
close = self._get_column(kwargs.pop("close", "close"))
result = trend_return(close=close, trend=trend, log=log, asbool=asbool, offset=offset, trend_reset=trend_reset, **kwargs)
return self._post_process(result, **kwargs)
# Statistics
def entropy(self, length=None, base=None, offset=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
@@ -1440,6 +1449,13 @@ class AnalysisIndicators(BasePandasObject):
result = supertrend(high=high, low=low, close=close, period=period, multiplier=multiplier, mamode=mamode, drift=drift, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def tsignals(self, trend=None, asbool=None, trend_reset=None, trend_offset=None, offset=None, **kwargs):
if trend is None:
return self._df
else:
result = tsignals(trend, asbool=asbool, trend_offset=trend_offset, trend_reset=trend_reset, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def ttm_trend(self, length=None, offset=None, **kwargs):
high = self._get_column(kwargs.pop("high", "high"))
low = self._get_column(kwargs.pop("low", "low"))
-1
View File
@@ -2,4 +2,3 @@
from .drawdown import drawdown
from .log_return import log_return
from .percent_return import percent_return
from .trend_return import trend_return
+6 -4
View File
@@ -3,20 +3,22 @@ from numpy import log as nplog
from pandas_ta.utils import get_offset, verify_series
def log_return(close, length=None, cumulative=False, offset=None, **kwargs):
def log_return(close, length=None, cumulative=None, offset=None, **kwargs):
"""Indicator: Log Return"""
# Validate Arguments
length = int(length) if length and length > 0 else 1
cumulative = bool(cumulative) if cumulative is not None and cumulative else False
close = verify_series(close, length)
offset = get_offset(offset)
if close is None: return
# Calculate Result
log_return = nplog(close).diff(periods=length)
if cumulative:
log_return = log_return.cumsum()
# log_return = nplog(close).diff(length).cumsum()
log_return = nplog(close / close.iloc[0])
else:
log_return = nplog(close / close.shift(length)) # nplog(close).diff(length)
# Offset
if offset != 0:
+6 -4
View File
@@ -1,21 +1,23 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta.utils import get_offset, verify_series
def percent_return(close, length=None, cumulative=False, offset=None, **kwargs):
def percent_return(close, length=None, cumulative=None, offset=None, **kwargs):
"""Indicator: Percent Return"""
# Validate Arguments
length = int(length) if length and length > 0 else 1
cumulative = bool(cumulative) if cumulative is not None and cumulative else False
close = verify_series(close, length)
offset = get_offset(offset)
if close is None: return
# Calculate Result
pct_return = close.pct_change(length)
if cumulative:
pct_return = pct_return.cumsum()
pct_return = (close / close.iloc[0]) - 1
else:
pct_return = close.pct_change(length) # (close / close.shift(length)) - 1
# Offset
if offset != 0:
-126
View File
@@ -1,126 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame
from .log_return import log_return
from .percent_return import percent_return
from pandas_ta.utils import get_offset, verify_series, zero
def trend_return(close, trend, log=True, asbool=None, trend_reset=0, trade_offset=None, offset=None, **kwargs):
"""Indicator: Trend Return"""
# Validate Arguments
close = verify_series(close)
trend = verify_series(trend)
asbool = bool(asbool) if isinstance(asbool, bool) else False
log = bool(log) if isinstance(log, bool) else True
trend_reset = int(trend_reset) if trend_reset and isinstance(trend_reset, int) else 0
if trade_offset !=0:
trade_offset = int(trade_offset) if trade_offset and isinstance(trade_offset, int) else -1
offset = get_offset(offset)
# Calculate Result
returns = log_return(close) if log else percent_return(close)
_return_name = returns.name
trends = trend.astype(int)
active_returns = (trends * returns).apply(zero)
tsum = 0
m = trends.size
csum = []
for i in range(0, m):
if trends[i] == trend_reset:
tsum = 0
else:
tsum += active_returns[i]
csum.append(tsum)
trades = trends.diff().shift(trade_offset).fillna(0).astype(int)
entries = (trades > 0).astype(int)
exits = (trades < 0).abs().astype(int)
if asbool:
trends = trends.astype(bool)
entries = entries.astype(bool)
exits = exits.astype(bool)
data = {
f"TR_{_return_name}": active_returns,
f"TR_CUM{_return_name}": csum,
f"TR_Trends": trends,
f"TR_Trades": trades,
f"TR_Entries": entries,
f"TR_Exits": exits,
}
df = DataFrame(data, index=close.index)
# Offset
if offset != 0:
df = df.shift(offset)
# Handle fills
if "fillna" in kwargs:
df.fillna(kwargs["fillna"], inplace=True)
if "fill_method" in kwargs:
df.fillna(method=kwargs["fill_method"], inplace=True)
# Name & Category
df.name = f"TR{'l' if log else 'p'}"
df.category = "performance"
return df
trend_return.__doc__ = \
"""Trend Return
Calculates the Returns and Cumulative Returns of a Trend as defined by a
sequence of booleans called a 'trend'. One popular example in TA literature is
to be long when the 'close' > 'moving average'. For example, if the trend is
long when close is above sma(close, 50), then set trend= close > sma(close, 50).
Trend Return will calculate the returns and cumulative returns as well as the
Trends, Trades, Entries and Exits. By default, Trends, Entries and Exits return
integers. When 'asbool=True', Trends, Entries and Exits will return as boolean
which is helpful when combined with the vectorbt backtesting package.
Additionally, returns are log returns by default.
Examples:
ta.trend_return(close, trend= close > ta.sma(close, 50))
ta.trend_return(close, trend= ta.ema(close, 8) > ta.ema(close, 21))
Sources: Kevin Johnson
Calculation:
Default Inputs:
log=True, asbool=False, trend_reset=0
sum = 0
returns = log_return if log else percent_return # These are not cumulative
returns = (trend * returns).apply(zero)
for i, in range(0, trend.size):
if item == trend_reset:
sum = 0
else:
returns += returns.iloc[i]
trend_return.append(sum)
Args:
close (pd.Series): Series of 'close's
trend (pd.Series): Series of 'trend's. The trend can be either a boolean or
integer series of '0's and '1's
log (bool): Calculate logarithmic returns. Default: True
asbool (bool): If True, it converts the Trends, Entries and Exits columns to
booleans. When boolean, it is also useful for backtesting with
vectorbt's Portfolio.from_signal(close, entries, exits) Default: False
trend_reset (value): Value used to identify if a trend has ended. Default: 0
trade_offset (value): Value used shift the trade entries/exits. Default: -1
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
fill_method (value, optional): Type of fill method
Returns:
pd.DataFrame: Returns columns: Returns, Cumulative Returns,
Trends (trend: 1, no trend: 0), Trades (Enter: 1, Exit: -1, Otherwise: 0),
Entries (entry: 1, nothing: 0), Exits (exit: 1, nothing: 0)
"""
+1
View File
@@ -12,5 +12,6 @@ from .long_run import long_run
from .psar import psar
from .qstick import qstick
from .short_run import short_run
from .tsignals import tsignals
from .ttm_trend import ttm_trend
from .vortex import vortex
+95
View File
@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame
from pandas_ta.utils import get_offset, verify_series
def tsignals(trend, asbool=None, trend_reset=0, trade_offset=None, offset=None, **kwargs):
"""Indicator: Trend Signals"""
# Validate Arguments
trend = verify_series(trend)
asbool = bool(asbool) if isinstance(asbool, bool) else False
trend_reset = int(trend_reset) if trend_reset and isinstance(trend_reset, int) else 0
if trade_offset !=0:
trade_offset = int(trade_offset) if trade_offset and isinstance(trade_offset, int) else -1
offset = get_offset(offset)
# Calculate Result
trends = trend.astype(int)
trades = trends.diff().shift(trade_offset).fillna(0).astype(int)
entries = (trades > 0).astype(int)
exits = (trades < 0).abs().astype(int)
if asbool:
trends = trends.astype(bool)
entries = entries.astype(bool)
exits = exits.astype(bool)
data = {
f"TS_Trends": trends,
f"TS_Trades": trades,
f"TS_Entries": entries,
f"TS_Exits": exits,
}
df = DataFrame(data, index=trends.index)
# Offset
if offset != 0:
df = df.shift(offset)
# Handle fills
if "fillna" in kwargs:
df.fillna(kwargs["fillna"], inplace=True)
if "fill_method" in kwargs:
df.fillna(method=kwargs["fill_method"], inplace=True)
# Name & Category
df.name = f"TS"
df.category = "trend"
return df
tsignals.__doc__ = \
"""Trend Signals
Given a Trend, Trend Signals returns the Trend, Trades, Entries and Exits as
boolean integers. When 'asbool=True', it returns Trends, Entries and Exits as
boolean values which is helpful when combined with the vectorbt backtesting
package.
A Trend can be a simple as: 'close' > 'moving average' or something more complex
whose values are boolean or integers (0 or 1).
Examples:
ta.tsignals(close > ta.sma(close, 50), asbool=False)
ta.tsignals(ta.ema(close, 8) > ta.ema(close, 21), asbool=True)
Sources: Kevin Johnson
Calculation:
Default Inputs:
asbool=False, trend_reset=0, trade_offset=-1
trades = trends.diff().shift(trade_offset).fillna(0).astype(int)
entries = (trades > 0).astype(int)
exits = (trades < 0).abs().astype(int)
Args:
trend (pd.Series): Series of 'trend's. The trend can be either a boolean or
integer series of '0's and '1's
asbool (bool): If True, it converts the Trends, Entries and Exits columns to
booleans. When boolean, it is also useful for backtesting with
vectorbt's Portfolio.from_signal(close, entries, exits) Default: False
trend_reset (value): Value used to identify if a trend has ended. Default: 0
trade_offset (value): Value used shift the trade entries/exits. Default: -1
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
fill_method (value, optional): Type of fill method
Returns:
pd.DataFrame with columns:
Trends (trend: 1, no trend: 0), Trades (Enter: 1, Exit: -1, Otherwise: 0),
Entries (entry: 1, nothing: 0), Exits (exit: 1, nothing: 0)
"""
+23 -7
View File
@@ -134,6 +134,11 @@ def yf(ticker: str, **kwargs):
print(f"[X] Ticker '{ticker}' not found.")
return
filtered = {k: v for k, v in ticker_info.items() if v is not None}
# print(f"\n{type(ticker_info)}\n{ticker_info}\n{ticker_info.items()}")
ticker_info.clear()
ticker_info.update(filtered)
# Dividends and Splits
dividends, splits = yfd.splits, yfd.dividends
@@ -143,7 +148,11 @@ def yf(ticker: str, **kwargs):
snd_length = kwargs.pop("snd", 5)
print("\n==== Company Information " + div)
print(f"{ticker_info['longName']} ({ticker_info['shortName']}) [{ticker_info['symbol']}]")
ci_header = f"({ticker_info['shortName']}) [{ticker_info['symbol']}]"
if "longName" in ticker_info and len(ticker_info["longName"]):
print(f"{ticker_info['longName']}" + ci_header)
else:
print(ci_header)
if description:
print(f"{ticker_info['longBusinessSummary']}\n")
@@ -195,7 +204,7 @@ def yf(ticker: str, **kwargs):
if "shortPercentOfFloat" in ticker_info and ticker_info['shortPercentOfFloat'] is not None and "sharesShortPriorMonth" in ticker_info and ticker_info['sharesShortPriorMonth'] is not None:
print(f"Short % of Float | Short prior Month".ljust(39), f"{100 * ticker_info['shortPercentOfFloat']:.4f}% | {ticker_info['sharesShortPriorMonth']:,}".rjust(40))
if "heldPercentInstitutions" in ticker_info and ticker_info['heldPercentInstitutions'] is not None or "heldPercentInsiders" in ticker_info and ticker_info['heldPercentInsiders'] is not None:
print(f"Insiders % | Institution %".ljust(39), f"{100 * ticker_info['heldPercentInsiders']:.4f}% | {100 * ticker_info['heldPercentInstitutions']:.4f}".rjust(40))
print(f"Insiders % | Institution %".ljust(39), f"{100 * ticker_info['heldPercentInsiders']:.4f}% | {100 * ticker_info['heldPercentInstitutions']:.4f}%".rjust(40))
print()
if "bookValue" in ticker_info and ticker_info['bookValue'] is not None or "priceToBook" in ticker_info and ticker_info['priceToBook'] is not None or "pegRatio" in ticker_info and ticker_info['pegRatio'] is not None:
@@ -223,11 +232,16 @@ def yf(ticker: str, **kwargs):
print("\n==== Price Information " + div)
_o, _h, _l, _c, _v = ticker_info['open'], ticker_info['dayHigh'], ticker_info['dayLow'], ticker_info['regularMarketPrice'], ticker_info['regularMarketVolume']
print(f"Open High Low Close".ljust(39), f"{_o:.4f}, {_o:.4f}, {_l:.4f}, {_c:.4f}".rjust(40))
print(f"Open High Low | Close".ljust(39), f"{_o:.4f} {_o:.4f} {_l:.4f} | {_c:.4f}".rjust(40))
print(f"HL2 | HLC3 | OHLC4 | C - OHLC4".ljust(39), f"{0.5 * (_h + _l):.4f}, {(_h + _l + _c) / 3.:.4f}, {0.25 * (_o + _h + _l + _c):.4f}, {_c - 0.25 * (_o + _h + _l + _c):.4f}".rjust(40))
print(f"Change (%)".ljust(39), f"{_c - ticker_info['previousClose']:.4f} ({100 * ((_c / ticker_info['previousClose']) - 1):.4f}%)".rjust(40))
print(f"Volume | Avg Vol (10Day)".ljust(39), f"{ticker_info['volume']:,} | {ticker_info['averageVolume']:,} ({ticker_info['averageDailyVolume10Day']:,})".rjust(40))
print(f"Bid | Ask | Spread".ljust(39), f"{ticker_info['bid']} x {ticker_info['bidSize']} | {ticker_info['ask']} x {ticker_info['askSize']} | {ticker_info['ask'] - ticker_info['bid']:.4f}".rjust(40))
if "bid" in ticker_info and ticker_info['bid'] is not None \
and "bidSize" in ticker_info and ticker_info['bidSize'] is not None \
and "ask" in ticker_info and ticker_info['ask'] is not None \
and "askSize" in ticker_info and ticker_info['askSize'] is not None:
print(f"Bid | Ask | Spread".ljust(39), f"{ticker_info['bid']} x {ticker_info['bidSize']} | {ticker_info['ask']} x {ticker_info['askSize']} | {ticker_info['ask'] - ticker_info['bid']:.4f}".rjust(40))
print(f"Volume | Market | Avg Vol (10Day)".ljust(40))
print(f"{ticker_info['volume']:,} | {_v:,} | {ticker_info['averageVolume']:,} ({ticker_info['averageDailyVolume10Day']:,})".rjust(80))
print()
if "52WeekChange" in ticker_info and ticker_info['52WeekChange'] is not None:
@@ -245,9 +259,11 @@ def yf(ticker: str, **kwargs):
print(f"SMA 50".ljust(39), f"{ticker_info['fiftyDayAverage']:.4f}".rjust(40))
elif avg200:
print(f"SMA 200".ljust(39), f"{ticker_info['twoHundredDayAverage']:.4f}".rjust(40))
if "beta" in ticker_info and ticker_info['beta'] is not None:
if "beta" in ticker_info and ticker_info['beta'] is not None and "beta3Year" in ticker_info and ticker_info['beta3Year'] is not None:
print(f"Beta | 3Yr".ljust(39), f"{ticker_info['beta']} | {ticker_info['beta3Year']}".rjust(40))
if "threeYearAverageReturn" in ticker_info and ticker_info['threeYearAverageReturn'] is not None or "fiveYearAverageReturn" in ticker_info and ticker_info['fiveYearAverageReturn'] is not None:
elif "beta" in ticker_info and ticker_info['beta'] is not None:
print(f"Beta".ljust(39), f"{ticker_info['beta']}".rjust(40))
if "threeYearAverageReturn" in ticker_info and ticker_info['threeYearAverageReturn'] is not None and "fiveYearAverageReturn" in ticker_info and ticker_info['fiveYearAverageReturn'] is not None:
print(f"Avg. Return 3Yr | 5Yr".ljust(39), f"{100 * ticker_info['threeYearAverageReturn']:.4f}% | {100 * ticker_info['fiveYearAverageReturn']:.4f}%".rjust(40))
# Dividends and Splits
+23
View File
@@ -45,6 +45,29 @@ def combination(**kwargs: dict) -> int:
return numerator // denominator
def erf(x):
"""Error Function erf(x)
The algorithm comes from Handbook of Mathematical Functions, formula 7.1.26.
Source: https://stackoverflow.com/questions/457408/is-there-an-easily-available-implementation-of-erf-for-python
"""
# save the sign of x
sign = 1 if x >= 0 else -1
x = abs(x)
# constants
a1 = 0.254829592
a2 = -0.284496736
a3 = 1.421413741
a4 = -1.453152027
a5 = 1.061405429
p = 0.3275911
# A&S formula 7.1.26
t = 1.0 / (1.0 + p * x)
y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * math.exp(-x * x)
return sign * y # erf(-x) = -erf(x)
def fibonacci(n: int = 2, **kwargs: dict) -> npNdArray:
"""Fibonacci Sequence as a numpy array"""
n = int(npFabs(n)) if n >= 0 else 2
+1 -1
View File
@@ -61,7 +61,7 @@ def get_time(exchange: str = "NYSE", full:bool = True, to_string:bool = False) -
if full:
lt = localtime()
local_ = f"Local: {lt.tm_hour}:{lt.tm_min:02d}:{lt.tm_sec:02d} {lt.tm_zone}"
doy = f"Day {today.dayofyear}/365 ({100 * round(today.dayofyear/365, 2)}%)"
doy = f"Day {today.dayofyear}/365 ({100 * round(today.dayofyear/365, 2):.2f}%)"
exchange_ = f"{exchange}: {exchange_time}"
s = f"{date}, {exchange_}, {local_}, {doy}"
+2 -2
View File
@@ -42,8 +42,8 @@ def thermo(high, low, length=None, long=None, short=None, mamode=None, drift=Non
if offset != 0:
thermo = thermo.shift(offset)
thermo_ma = thermo_ma.shift(offset)
therthermo_longmo_ma = thermo_ma.shift(offset)
thermo_short = thermo_ma.shift(offset)
thermo_long = thermo_long.shift(offset)
thermo_short = thermo_short.shift(offset)
# Handle fills
if "fillna" in kwargs:
+1 -1
View File
@@ -18,7 +18,7 @@ setup(
"pandas_ta.volatility",
"pandas_ta.volume"
],
version=".".join(("0", "2", "73b")),
version=".".join(("0", "2", "74b")),
description=long_description,
long_description=long_description,
author="Kevin Johnson",
-12
View File
@@ -39,15 +39,3 @@ class TestPerformaceExtension(TestCase):
self.data.ta.percent_return(append=True, cumulative=True)
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], "CUMPCTRET_1")
def test_log_trend_return_ext(self):
tr = self.data.ta.trend_return(trend=self.islong, log=True, append=True)
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(list(self.data.columns[-6:]), list(tr.columns))
self.data.drop(columns=tr.columns, inplace=True, errors="ignore")
def test_pct_trend_return_ext(self):
tr = self.data.ta.trend_return(trend=self.islong, log=False, append=True)
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(list(self.data.columns[-6:]), list(tr.columns))
self.data.drop(columns=tr.columns, inplace=True, errors="ignore")
-16
View File
@@ -45,19 +45,3 @@ class TestPerformace(TestCase):
def test_cum_percent_return(self):
result = pandas_ta.percent_return(self.close, cumulative=True)
self.assertEqual(result.name, "CUMPCTRET_1")
def test_log_trend_return(self):
result = pandas_ta.trend_return(self.close, self.islong, log=True)
self.assertEqual(result.name, "TRl")
def test_cum_log_trend_return(self):
result = pandas_ta.trend_return(self.close, self.islong, log=True)
self.assertEqual(result.name, "TRl")
def test_pct_trend_return(self):
result = pandas_ta.trend_return(self.close, self.islong, log=False)
self.assertEqual(result.name, "TRp")
def test_cum_pct_trend_return(self):
result = pandas_ta.trend_return(self.close, self.islong, log=False)
self.assertEqual(result.name, "TRp")
+2 -2
View File
@@ -185,8 +185,8 @@ class TestStrategyMethods(TestCase):
"AMAT Log Returns", # description
)
self.data.ta.strategy(custom, verbose=verbose, timed=strategy_timed, ordered=True)
self.data.ta.trend_return(trend=self.data["AMATe_LR_2"], cumulative=True, append=True)
self.assertEqual(len(self.data.columns), 15)
self.data.ta.tsignals(trend=self.data["AMATe_LR_2"], append=True)
self.assertEqual(len(self.data.columns), 13)
# @skip
def test_momentum_category(self):