twopirllc / pandas-ta

Technical Analysis Indicators - Pandas TA is an easy to use Python 3 Pandas Extension with 150+ Indicators
https://twopirllc.github.io/pandas-ta/
MIT License
5.46k stars 1.06k forks source link

Support Resistance #217

Open coinrazzi opened 3 years ago

coinrazzi commented 3 years ago

Which version are you running? The lastest version is on Github. Pip is for major releases. Pandas Ta version : 0.2.42b0

I am using an indicator for automatic drawing support and resistance levels. It's working well. Can you add to pandas ta? I am pasting pine script code.

---------------------------------------------------------------------------------------------------
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
//@version=4
study("Support/Resistance", shorttitle="S/R", overlay=true, scale = scale.right, linktoseries = true)

line_width = input(4, type = input.integer, title="SR Level line Width")
level_min_lengh = input(4, type = input.integer, title="Set minimum number of bars from level start to qualify a level")
y  = input("Orange", "Line Color", options=["Red", "Lime", "Orange", "Teal", "Yellow", "White", "Black"])
line_extend = input(false, type = input.bool, title = "Extend Level line Right") ? extend.right : extend.none
sr_tf = input("", type = input.resolution, title="SR Timeframe (Beta)")

//color function
colour(z) => z=="Red"?color.red:z=="Lime"?color.lime:z=="Orange"?color.orange:z=="Teal"?
 color.teal:z=="Yellow"?color.yellow:z=="Black"?color.black:color.white

//Legacy RSI calc
rsi_src = close, len = 9
up1 = rma(max(change(rsi_src), 0), len)
down1 = rma(-min(change(rsi_src), 0), len)
legacy_rsi = down1 == 0 ? 100 : up1 == 0 ? 0 : 100 - (100 / (1 + up1 / down1))

//CMO based on HMA
length = 1
src1 = hma(open, 5)[1] // legacy hma(5) calculation gives a resul with one candel shift, thus use hma()[1]
src2 = hma(close, 12)
momm1 = change(src1) // Difference between current value and previous, x - x[y] (sources series - length integer
momm2 = change(src2)
f1(m, n) => m >= n ? m : 0.0
f2(m, n) => m >= n ? 0.0 : -m
m1 = f1(momm1, momm2)
m2 = f2(momm1, momm2)
sm1 = sum(m1, length)
sm2 = sum(m2, length)
percent(nom, div) => 100 * nom / div
cmo_new = percent(sm1-sm2, sm1+sm2)

//Legacy Close Pivots calcs.
len5 = 2
h = highest(len5)
h1 = dev(h, len5) ? na : h
hpivot = fixnan(h1)
l = lowest(len5)
l1 = dev(l, len5) ? na : l
lpivot = fixnan(l1)

//Calc Values

rsi_new = rsi(close,9)
lpivot_new =  lpivot  // use legacy pivots calculation as integrated pivotlow/pivothigh functions give very different result
hpivot_new =  hpivot

sup = rsi_new < 25 and cmo_new > 50  and lpivot_new
res = rsi_new > 75 and cmo_new < -50  and hpivot_new
calcXup() =>
    var xup = 0.0
    xup :=  sup ? low : xup[1]
calcXdown() =>
    var xdown = 0.0
    xdown := res ? high : xdown[1]

//Lines drawing variables
tf1 = security(syminfo.tickerid, sr_tf, calcXup(), lookahead=barmerge.lookahead_on)
tf2  = security(syminfo.tickerid, sr_tf, calcXdown(), lookahead=barmerge.lookahead_on)

//SR Line plotting
var tf1_line = line.new(0, 0, 0, 0)
var tf1_bi_start = 0
var tf1_bi_end = 0
tf1_bi_start := change(tf1) ? bar_index : tf1_bi_start[1]
tf1_bi_end := change(tf1) ? tf1_bi_start : bar_index
if change(tf1)
    if (line.get_x2(tf1_line) - line.get_x1(tf1_line)) < level_min_lengh
        line.delete(tf1_line)
    tf1_line := line.new(tf1_bi_start, tf1, tf1_bi_end, tf1, color = colour(y), width = line_width, extend = line_extend)
line.set_x2(tf1_line, tf1_bi_end)

var tf2_line = line.new(0, 0, 0, 0)
var tf2_bi_start = 0
var tf2_bi_end = 0
tf2_bi_start := change(tf2) ? bar_index : tf2_bi_start[1]
tf2_bi_end := change(tf2) ? tf2_bi_start : bar_index
if change(tf2)
    if (line.get_x2(tf2_line) - line.get_x1(tf2_line)) < level_min_lengh
        line.delete(tf2_line)
    tf2_line := line.new(tf2_bi_start, tf2, tf2_bi_end, tf2, color =  colour(y), width = line_width, extend = line_extend)
line.set_x2(tf2_line, tf2_bi_end)

alertcondition(change(tf1) != 0 or change(tf2) != 0 , message = "New S/R line" )
twopirllc commented 3 years ago

Hello @coinrazzi,

Seems interesting. Can you provide the Author or link to the indicator so that I can find it via TradingView? I would like to add Pivots to Pandas TA and hopefully someone has knowledge or experience to get this or other basic Pivot systems implemented. There are some complexities involved to get these to work.

Unfortunately, I do not have a time frame for this addition as the library is currently overwhelmed with Issues and current enhancements. If you want to make some head way, please fork, add the indicator with a new directory called pivots and submit a PR. I can take it from there.

Kind Regards, KJ

coinrazzi commented 3 years ago

This is script and profile link. https://www.tradingview.com/script/JAW0oW7R-Support-Resistance-V2-Indicator/

Ok. I will try to python code this script.

twopirllc commented 3 years ago

@coinrazzi,

Awesome! Let me know if you need help getting started. I would look at some other PRs on which files need editing to integrate it into Pandas TA.

Thanks, KJ

coinrazzi commented 3 years ago

I fund another python script about support resistance on github. I am using in my project now. it's well too. Maybe you want to check. This is Class Code (using numpy)

import numpy as np

class Sup_Res_Finder():
    def isSupport(self, df,i):
        support = df['Low'][i] < df['Low'][i-1]  and df['Low'][i] < df['Low'][i+1] \
        and df['Low'][i+1] < df['Low'][i+2] and df['Low'][i-1] < df['Low'][i-2]

        return support

    def isResistance(self, df,i):
        resistance = df['High'][i] > df['High'][i-1]  and df['High'][i] > df['High'][i+1] \
        and df['High'][i+1] > df['High'][i+2] and df['High'][i-1] > df['High'][i-2] 

        return resistance

    def find_levels(self, df):
        levels = []
        s =  np.mean(df['High'] - df['Low'])

        for i in range(2, df.shape[0]-2):
            if self.isSupport(df,i):
                l = df['Low'][i]

                if np.sum([abs(l-x) < s  for x in levels]) == 0:
                    levels.append((i,l))

            elif self.isResistance(df,i):
                l = df['High'][i]

                if np.sum([abs(l-x) < s  for x in levels]) == 0:
                    levels.append((i,l))

        return levels

This is github link.

twopirllc commented 3 years ago

Hello @coinrazzi,

Thanks but there is no link 😬 .

KJ

coinrazzi commented 3 years ago

Sorry this is link. https://github.com/Reed-Schimmel/support_resistance_finder

Kubilay

talhazan commented 3 years ago

Seems very interesting, what can be the equivilent of fixnan() from pine?

pine has brought a lot of positive processing to dataframes and indicators world, contributing to pandas-ta will provide great benefits to the community using it.

@twopirllc Kevin, I have some indicators to contribute but I might need some guidance to have the code written in the pandas-ta fashion and merge it properly.

Would you consider a benchmark of pandas-ta TA's vs TVs TA's ?

twopirllc commented 3 years ago

Hello @talhazan,

Seems very interesting, what can be the equivilent of fixnan() from pine?

Pinescript fixnan sounds like a Pandas Forward Fill: df.fillna("ffill", inplace=True).

pine has brought a lot of positive processing to dataframes and indicators world, contributing to pandas-ta will provide great benefits to the community using it.

Yeah TradingView has. Good for prototyping indicators before porting them to other languages. Plan to implement more when possible.

Kevin, I have some indicators to contribute but I might need some guidance to have the code written in the pandas-ta fashion and merge it properly.

Great! I've ported some of mine here as well. To add indicators or utility methods, please follow Issue #264 Is there a proper way to extend pandas-ta with Custom Indicator?.

Make PRs when your ready. One indicator per PR. 😎

Would you consider a benchmark of pandas-ta TA's vs TVs TA's ?

Sure if you know an automatic way to do it without having to build a a New Chart Layout with said indicator(s), manually downloading their data with processed indicators.

At a minimum, if there are common indicators between TA Lib and Pandas TA, I correlation test those. I have to do the manual process above to run correlation tests with TradingView indicators.

Kind Regards, KJ

Samiisd commented 10 months ago

A second iteration on the version shared by the author of #551, thanks for the code!

The version below is ~10x times faster on my machine.

def supres2(open, high, low, close, offset=None, suffix=None, **kwargs):
    # Validate and prepare data
    length = len(close)
    open, high, low, close = map(lambda s: v_series(s, length), [open, high, low, close])
    offset = v_offset(offset)

    # Compute basic indicators
    src1 = ta.hma(open, length=5, offset=1)
    src2 = ta.hma(close, length=12)
    momm1, momm2 = src1.diff(), src2.diff()
    rsi_new = ta.rsi(close, length=9)
    sh, sl = high.rolling(2).max().rolling(2).mean(), low.rolling(2).min().rolling(2).mean()

    # Calculate momentum and CMO
    m1 = momm1 >= momm2
    m2 = -1 * momm1.where(momm1 < momm2, 0)
    sm1, sm2 = m1.rolling(1).sum(), m2.rolling(1).sum()
    CMO = 100 * (sm1 - sm2) / (sm1 + sm2).replace(0, float('nan'))

    # Calculate support and resistance
    h1, l1 = (high - sh).abs().expanding().mean(), (low - sl).abs().expanding().mean()
    hpivot, lpivot = h1 != 0, l1 != 0
    hpivot_new, lpivot_new = high.where(hpivot), low.where(lpivot)

    RESISTANCE = hpivot_new.where((rsi_new > 75) & (CMO < -50))
    SUPPORT = lpivot_new.where((rsi_new < 25) & (CMO > 50))

    # Prepare DataFrame to return
    props = f"_{length}" if suffix == "length" else ""
    df = pd.DataFrame({f"SUPPORT{props}": SUPPORT, f"RESISTANCE{props}": RESISTANCE}, index=close.index)
    df.name = f"SUPPORT{props}"
    df.category = "overlap"
    df.ffill(inplace=True)

    # Apply offset if needed
    return df.shift(offset)

image

vpmartin commented 5 months ago

Hey @twopirllc,

Was this somehow merged and released? The related PR is noted as merged, but this issue is kept open.

Also, I can't find any reference to supres in the docs or in pandas_ta, using 0.3.14b.

Many thanks.

twopirllc commented 5 months ago

Hello @vpmartin,

This issue is still open because it has not been completed, see my latest comment in #551.

Kind Regards, KJ