Skip to content

Commit

Permalink
Merge branch 'pr/777' into development
Browse files Browse the repository at this point in the history
  • Loading branch information
twopirllc committed Jun 15, 2024
2 parents 64fb424 + 32bed0a commit c1a161b
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 81 deletions.
44 changes: 25 additions & 19 deletions pandas_ta/momentum/stc.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,37 +152,43 @@ def stc(

def schaff_tc(close, xmacd, tclength, factor):
# ACTUAL Calculation part, which is shared between operation modes
# 1St : Stochastic of MACD
lowest_xmacd = xmacd.rolling(tclength).min() # min value in interval tclen
lowest_xmacd = xmacd.rolling(tclength).min()
xmacd_range = non_zero_range(xmacd.rolling(tclength).max(), lowest_xmacd)
m = len(xmacd)

# %Fast K of MACD
stoch1, pf = list(xmacd), list(xmacd)
stoch1[0], pf[0] = 0.0, 0.0
# Initialize lists
stoch1, pf = [0] * m, [0] * m
stoch2, pff = [0] * m, [0] * m

for i in range(1, m):
if lowest_xmacd.iloc[i] > 0:
stoch1[i] = 100 * ((xmacd.iloc[i] - lowest_xmacd.iloc[i]) / xmacd_range.iloc[i])
# %Fast K of MACD
if lowest_xmacd[i] > 0:
stoch1[i] = 100 * ((xmacd[i] - lowest_xmacd[i]) / xmacd_range[i])
else:
stoch1[i] = stoch1[i - 1]
# Smoothed Calculation for % Fast D of MACD
pf[i] = round(pf[i - 1] + (factor * (stoch1[i] - pf[i - 1])), 8)

pf = Series(pf, index=close.index)
# find min and max so far
if i < tclength:
# If there are not enough elements for a full tclength window, use what is available
lowest_pf = min(pf[:i+1])
highest_pf = max(pf[:i+1])
else:
lowest_pf = min(pf[i-tclength+1:i+1])
highest_pf = max(pf[i-tclength+1:i+1])

# 2nd : Stochastic of smoothed Percent Fast D, 'PF', above
lowest_pf = pf.rolling(tclength).min()
pf_range = non_zero_range(pf.rolling(tclength).max(), lowest_pf)
# Ensure non-zero range
pf_range = highest_pf - lowest_pf if highest_pf - lowest_pf > 0 else 1

# % of Fast K of PF
stoch2, pff = list(xmacd), list(xmacd)
stoch2[0], pff[0] = 0, 0
for i in range(1, m):
if pf_range.iloc[i] > 0:
stoch2[i] = 100 * ((pf.iloc[i] - lowest_pf.iloc[i]) / pf_range.iloc[i])
# % of Fast K of PF
if pf_range > 0:
stoch2[i] = 100 * ((pf[i] - lowest_pf) / pf_range)
else:
stoch2[i] = stoch2[i - 1]
# Smoothed Calculation for % Fast D of PF
pff[i] = round(pff[i - 1] + (factor * (stoch2[i] - pff[i - 1])), 8)

return pff, pf
pf_series = Series(pf, index=close.index)
pff_series = Series(pff, index=close.index)

return pff_series, pf_series
113 changes: 51 additions & 62 deletions pandas_ta/trend/psar.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# -*- coding: utf-8 -*-
from numpy import nan
from numpy import full, nan, zeros
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.utils import v_offset, v_pos_default, v_series, zero



def psar(
high: Series, low: Series, close: Series = None,
af0: IntFloat = None, af: IntFloat = None, max_af: IntFloat = None, tv=False,
Expand Down Expand Up @@ -51,80 +50,70 @@ def psar(
if high is None or low is None:
return

orig_high = high.copy()
orig_low = low.copy()
# Numpy arrays offer some performance improvements
high, low = high.values, low.values

paf = v_pos_default(af, 0.02) # paf is used to keep af from parameters
af0 = v_pos_default(af0, paf)
af = af0

max_af = v_pos_default(max_af, 0.2)
offset = v_offset(offset)

# Falling if the first NaN -DM is positive
falling = _falling(high.iloc[:2], low.iloc[:2])
if falling:
sar = high.iloc[0]
ep = low.iloc[0]
else:
sar = low.iloc[0]
ep = high.iloc[0]

# Set up
m = high.size
sar = zeros(m)
long = full(m, nan)
short = full(m, nan)
reversal = zeros(m, dtype=int)
_af = zeros(m)
_af[:2] = af0
falling = _falling(orig_high.iloc[:2], orig_low.iloc[:2])
ep = low[0] if falling else high[0]
if close is not None:
close = v_series(close)
sar = close.iloc[0]

long = Series(nan, index=high.index)
short = Series(nan, index=high.index)
reversal = Series(0, index=high.index)
_af = Series(0.0, index=high.index)
_af.iloc[0:2] = af0
sar[0] = close.iloc[0]
else:
sar[0] = high[0] if falling else low[0]

# Calculate
m = high.shape[0]
for row in range(2, m):
high_ = high.iat[row]
low_ = low.iat[row]

_sar = sar + af * (ep - sar)
for row in range(1, m):
sar[row] = sar[row-1] + af * (ep - sar[row-1])

if falling:
reverse = high_ > _sar

if low_ < ep:
ep = low_
af = min(af + paf, max_af)

_sar = max(high.iat[row - 1], high.iat[row - 2], _sar)
reverse = high[row] > sar[row]
if low[row] < ep:
ep = low[row]
af = min(af + af0, max_af)
sar[row] = max(high[row-1], sar[row])
else:
reverse = low_ < _sar

if high_ > ep:
ep = high_
af = min(af + paf, max_af)

_sar = min(low.iat[row - 1], low.iat[row - 2], _sar)
reverse = low[row] < sar[row]
if high[row] > ep:
ep = high[row]
af = min(af + af0, max_af)
sar[row] = min(low[row-1], sar[row])

if reverse:
if tv: # handle trading view version
if falling:
_sar = min(low.iat[row - 1], low.iat[row - 2], ep)
else:
_sar = max(high.iat[row - 1], high.iat[row - 2], ep)
else:
_sar = ep

sar[row] = ep
af = af0
falling = not falling # Must come before next line
ep = low_ if falling else high_

sar = _sar # Update SAR
falling = not falling
ep = low[row] if falling else high[row]

# Separate long/short sar based on falling
# Separate long/short SAR based on falling
if falling:
short.iat[row] = sar
short[row] = sar[row]
else:
long.iat[row] = sar
long[row] = sar[row]

_af[row] = af
reversal[row] = int(reverse)

_af.iat[row] = af
reversal.iat[row] = int(reverse)
_af = Series(_af, index=orig_high.index)
long = Series(long, index=orig_high.index)
short = Series(short, index=orig_high.index)
reversal = Series(reversal, index=orig_high.index)

# Offset
if offset != 0:
Expand All @@ -140,15 +129,15 @@ def psar(
short.fillna(kwargs["fillna"], inplace=True)
reversal.fillna(kwargs["fillna"], inplace=True)

_params = f"_{af0}_{max_af}"
_props = f"_{af0}_{max_af}"
data = {
f"PSARl{_params}": long,
f"PSARs{_params}": short,
f"PSARaf{_params}": _af,
f"PSARr{_params}": reversal
f"PSARl{_props}": long,
f"PSARs{_props}": short,
f"PSARaf{_props}": _af,
f"PSARr{_props}": reversal
}
df = DataFrame(data, index=high.index)
df.name = f"PSAR{_params}"
df = DataFrame(data, index=orig_high.index)
df.name = f"PSAR{_props}"
df.category = long.category = short.category = "trend"

return df
Expand Down

0 comments on commit c1a161b

Please sign in to comment.