mirror of
https://github.com/wassname/pandas-ta.git
synced 2026-06-27 16:10:07 +08:00
+1
-2
@@ -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
|
||||
@@ -15,7 +15,7 @@ Pandas TA - A Technical Analysis Library in Python 3
|
||||

|
||||
|
||||
|
||||
_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.
|
||||
|  |
|
||||
<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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+203
-313
File diff suppressed because one or more lines are too long
@@ -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)
|
||||
|
||||
@@ -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
@@ -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"))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
"""
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
"""
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user