Support Resistance Algorithm - Technical analysis

后端 未结 11 504
悲哀的现实
悲哀的现实 2020-12-12 09:23

I have an intra-day chart and I am trying to figure out how to calculate support and resistance levels, anyone knows an algorithm for doing that, or a good starting point?

相关标签:
11条回答
  • 2020-12-12 09:25

    Here's a python function to find support / resistance levels

    This function takes a numpy array of last traded price and returns a list of support and resistance levels respectively. n is the number of entries to be scanned.

    def supres(ltp, n):
        """
        This function takes a numpy array of last traded price
        and returns a list of support and resistance levels 
        respectively. n is the number of entries to be scanned.
        """
        from scipy.signal import savgol_filter as smooth
    
        # converting n to a nearest even number
        if n % 2 != 0:
            n += 1
    
        n_ltp = ltp.shape[0]
    
        # smoothening the curve
        ltp_s = smooth(ltp, (n + 1), 3)
    
        # taking a simple derivative
        ltp_d = np.zeros(n_ltp)
        ltp_d[1:] = np.subtract(ltp_s[1:], ltp_s[:-1])
    
        resistance = []
        support = []
    
        for i in xrange(n_ltp - n):
            arr_sl = ltp_d[i:(i + n)]
            first = arr_sl[:(n / 2)]  # first half
            last = arr_sl[(n / 2):]  # second half
    
            r_1 = np.sum(first > 0)
            r_2 = np.sum(last < 0)
    
            s_1 = np.sum(first < 0)
            s_2 = np.sum(last > 0)
    
            # local maxima detection
            if (r_1 == (n / 2)) and (r_2 == (n / 2)):
                resistance.append(ltp[i + ((n / 2) - 1)])
    
            # local minima detection
            if (s_1 == (n / 2)) and (s_2 == (n / 2)):
                support.append(ltp[i + ((n / 2) - 1)])
    
        return support, resistance
    

    SRC

    0 讨论(0)
  • 2020-12-12 09:29

    Here is the PineScript code for S/Rs. It doesn't include all the logic Dr. Andrew or Nilendu discuss, but definitely a good start:

    https://www.tradingview.com/script/UUUyEoU2-S-R-Barry-extended-by-PeterO/

    //@version=3
    study(title="S/R Barry, extended by PeterO", overlay=true)
    FractalLen=input(10)
    isFractal(x) => highestbars(x,FractalLen*2+1)==-FractalLen
    
    sF=isFractal(-low), support=low, support:=sF ? low[FractalLen] : support[1]
    rF=isFractal(high), resistance=high, resistance:=rF ? high[FractalLen] : resistance[1]
    plot(series=support, color=sF?#00000000:blue, offset=-FractalLen)
    plot(series=resistance, color=rF?#00000000:red, offset=-FractalLen)
    
    supportprevious=low, supportprevious:=sF ? support[1] : supportprevious[1]
    resistanceprevious=low, resistanceprevious:=rF ? resistance[1] : resistanceprevious[1]
    plot(series=supportprevious, color=blue, style=circles, offset=-FractalLen)
    plot(series=resistanceprevious, color=red, style=circles, offset=-FractalLen)
    
    0 讨论(0)
  • 2020-12-12 09:32

    I have figured out another way of calculating Support/Resistance dynamically.

    Steps:

    1. Create a list of important price - The high and low of each candle in your range is important. Each of this prices is basically a probable SR(Support / Resistance).

    2. Give each price a score.

    3. Sort the prices by score and remove the ones close to each other(at a distance of x% from each other).

    4. Print the top N prices and having a mimimum score of Y. These are your Support Resistances. It worked very well for me in ~300 different stocks.

    The scoring technique

    A price is acting as a strong SR if there are many candles which comes close to this but cannot cross this. So, for each candle which are close to this price (within a distance of y% from the price), we will add +S1 to the score. For each candle which cuts through this price, we will add -S2(negative) to the score.

    This should give you a very basic idea of how to assign scores to this.

    Now you have to tweak it according to your requirements. Some tweak I made and which improved the performance a lot are as follows:

    1. Different score for different types of cut. If the body of a candle cuts through the price, then score change is -S3 but the wick of a candle cuts through the price, the score change is -S4. Here Abs(S3) > Abs(S4) because cut by body is more significant than cut by wick.

    2. If the candle which closes close the price but unable to cross is a high(higher than two candles on each side) or low(lower than 2 candles on each side), then add a higher score than other normal candles closing near this.

    3. If the candle closing near this is a high or low, and the price was in a downtrend or a uptrend (at least y% move) then add a higher score to this point.

    4. You can remove some prices from the initial list. I consider a price only if it is the highest or the lowest among N candles on both side of it.

    Here is a snippet of my code.

        private void findSupportResistance(List<Candle> candles, Long scripId) throws ExecutionException {
            // This is a cron job, so I skip for some time once a SR is found in a stock
            if(processedCandles.getIfPresent(scripId) == null || checkAlways) {
                //Combining small candles to get larger candles of required timeframe. ( I have 1 minute candles and here creating 1 Hr candles)
                List<Candle> cumulativeCandles = cumulativeCandleHelper.getCumulativeCandles(candles, CUMULATIVE_CANDLE_SIZE);
                //Tell whether each point is a high(higher than two candles on each side) or a low(lower than two candles on each side)
                List<Boolean> highLowValueList = this.highLow.findHighLow(cumulativeCandles);
                String name = scripIdCache.getScripName(scripId);
                Set<Double> impPoints = new HashSet<Double>();
                int pos = 0;
                for(Candle candle : cumulativeCandles){
                    //A candle is imp only if it is the highest / lowest among #CONSECUTIVE_CANDLE_TO_CHECK_MIN on each side
                    List<Candle> subList = cumulativeCandles.subList(Math.max(0, pos - CONSECUTIVE_CANDLE_TO_CHECK_MIN),
                            Math.min(cumulativeCandles.size(), pos + CONSECUTIVE_CANDLE_TO_CHECK_MIN));
                    if(subList.stream().min(Comparator.comparing(Candle::getLow)).get().getLow().equals(candle.getLow()) ||
                            subList.stream().min(Comparator.comparing(Candle::getHigh)).get().getHigh().equals(candle.getHigh())) {
                        impPoints.add(candle.getHigh());
                        impPoints.add(candle.getLow());
                    }
                    pos++;
                }
                Iterator<Double> iterator = impPoints.iterator();
                List<PointScore> score = new ArrayList<PointScore>();
                while (iterator.hasNext()){
                    Double currentValue = iterator.next();
                    //Get score of each point
                    score.add(getScore(cumulativeCandles, highLowValueList, currentValue));
                }
                score.sort((o1, o2) -> o2.getScore().compareTo(o1.getScore()));
                List<Double> used = new ArrayList<Double>();
                int total = 0;
                Double min = getMin(cumulativeCandles);
                Double max = getMax(cumulativeCandles);
                for(PointScore pointScore : score){
                    // Each point should have at least #MIN_SCORE_TO_PRINT point
                    if(pointScore.getScore() < MIN_SCORE_TO_PRINT){
                        break;
                    }
                    //The extremes always come as a Strong SR, so I remove some of them
                    // I also reject a price which is very close the one already used
                    if (!similar(pointScore.getPoint(), used) && !closeFromExtreme(pointScore.getPoint(), min, max)) {
                        logger.info("Strong SR for scrip {} at {} and score {}", name, pointScore.getPoint(), pointScore.getScore());
    //                    logger.info("Events at point are {}", pointScore.getPointEventList());
                        used.add(pointScore.getPoint());
                        total += 1;
                    }
                    if(total >= totalPointsToPrint){
                        break;
                    }
                }
            }
        }
    
        private boolean closeFromExtreme(Double key, Double min, Double max) {
            return Math.abs(key - min) < (min * DIFF_PERC_FROM_EXTREME / 100.0) || Math.abs(key - max) < (max * DIFF_PERC_FROM_EXTREME / 100);
        }
    
        private Double getMin(List<Candle> cumulativeCandles) {
            return cumulativeCandles.stream()
                    .min(Comparator.comparing(Candle::getLow)).get().getLow();
        }
    
        private Double getMax(List<Candle> cumulativeCandles) {
            return cumulativeCandles.stream()
                    .max(Comparator.comparing(Candle::getLow)).get().getHigh();
        }
    
        private boolean similar(Double key, List<Double> used) {
            for(Double value : used){
                if(Math.abs(key - value) <= (DIFF_PERC_FOR_INTRASR_DISTANCE * value / 100)){
                    return true;
                }
            }
            return false;
        }
    
        private PointScore getScore(List<Candle> cumulativeCandles, List<Boolean> highLowValueList, Double price) {
            List<PointEvent> events = new ArrayList<>();
            Double score = 0.0;
            int pos = 0;
            int lastCutPos = -10;
            for(Candle candle : cumulativeCandles){
                //If the body of the candle cuts through the price, then deduct some score
                if(cutBody(price, candle) && (pos - lastCutPos > MIN_DIFF_FOR_CONSECUTIVE_CUT)){
                    score += scoreForCutBody;
                    lastCutPos = pos;
                    events.add(new PointEvent(PointEvent.Type.CUT_BODY, candle.getTimestamp(), scoreForCutBody));
                //If the wick of the candle cuts through the price, then deduct some score
                } else if(cutWick(price, candle) && (pos - lastCutPos > MIN_DIFF_FOR_CONSECUTIVE_CUT)){
                    score += scoreForCutWick;
                    lastCutPos = pos;
                    events.add(new PointEvent(PointEvent.Type.CUT_WICK, candle.getTimestamp(), scoreForCutWick));
                //If the if is close the high of some candle and it was in an uptrend, then add some score to this
                } else if(touchHigh(price, candle) && inUpTrend(cumulativeCandles, price, pos)){
                    Boolean highLowValue = highLowValueList.get(pos);
                    //If it is a high, then add some score S1
                    if(highLowValue != null && highLowValue){
                        score += scoreForTouchHighLow;
                        events.add(new PointEvent(PointEvent.Type.TOUCH_UP_HIGHLOW, candle.getTimestamp(), scoreForTouchHighLow));
                    //Else add S2. S2 > S1
                    } else {
                        score += scoreForTouchNormal;
                        events.add(new PointEvent(PointEvent.Type.TOUCH_UP, candle.getTimestamp(), scoreForTouchNormal));
                    }
                //If the if is close the low of some candle and it was in an downtrend, then add some score to this
                } else if(touchLow(price, candle) && inDownTrend(cumulativeCandles, price, pos)){
                    Boolean highLowValue = highLowValueList.get(pos);
                    //If it is a high, then add some score S1
                    if (highLowValue != null && !highLowValue) {
                        score += scoreForTouchHighLow;
                        events.add(new PointEvent(PointEvent.Type.TOUCH_DOWN, candle.getTimestamp(), scoreForTouchHighLow));
                    //Else add S2. S2 > S1
                    } else {
                        score += scoreForTouchNormal;
                        events.add(new PointEvent(PointEvent.Type.TOUCH_DOWN_HIGHLOW, candle.getTimestamp(), scoreForTouchNormal));
                    }
                }
                pos += 1;
            }
            return new PointScore(price, score, events);
        }
    
        private boolean inDownTrend(List<Candle> cumulativeCandles, Double price, int startPos) {
            //Either move #MIN_PERC_FOR_TREND in direction of trend, or cut through the price
            for(int pos = startPos; pos >= 0; pos-- ){
                Candle candle = cumulativeCandles.get(pos);
                if(candle.getLow() < price){
                    return false;
                }
                if(candle.getLow() - price > (price * MIN_PERC_FOR_TREND / 100)){
                    return true;
                }
            }
            return false;
        }
    
        private boolean inUpTrend(List<Candle> cumulativeCandles, Double price, int startPos) {
            for(int pos = startPos; pos >= 0; pos-- ){
                Candle candle = cumulativeCandles.get(pos);
                if(candle.getHigh() > price){
                    return false;
                }
                if(price - candle.getLow() > (price * MIN_PERC_FOR_TREND / 100)){
                    return true;
                }
            }
            return false;
        }
    
        private boolean touchHigh(Double price, Candle candle) {
            Double high = candle.getHigh();
            Double ltp = candle.getLtp();
            return high <= price && Math.abs(high - price) < ltp * DIFF_PERC_FOR_CANDLE_CLOSE / 100;
        }
    
        private boolean touchLow(Double price, Candle candle) {
            Double low = candle.getLow();
            Double ltp = candle.getLtp();
            return low >= price && Math.abs(low - price) < ltp * DIFF_PERC_FOR_CANDLE_CLOSE / 100;
        }
    
        private boolean cutBody(Double point, Candle candle) {
            return Math.max(candle.getOpen(), candle.getClose()) > point && Math.min(candle.getOpen(), candle.getClose()) < point;
        }
    
        private boolean cutWick(Double price, Candle candle) {
            return !cutBody(price, candle) && candle.getHigh() > price && candle.getLow() < price;
        }
    

    Some Helper classes:

    public class PointScore {
        Double point;
        Double score;
        List<PointEvent> pointEventList;
    
        public PointScore(Double point, Double score, List<PointEvent> pointEventList) {
            this.point = point;
            this.score = score;
            this.pointEventList = pointEventList;
        }
    }
    
    
    
        public class PointEvent {
        public enum Type{
            CUT_BODY, CUT_WICK, TOUCH_DOWN_HIGHLOW, TOUCH_DOWN, TOUCH_UP_HIGHLOW, TOUCH_UP;
        }
    
        Type type;
        Date timestamp;
        Double scoreChange;
    
        public PointEvent(Type type, Date timestamp, Double scoreChange) {
            this.type = type;
            this.timestamp = timestamp;
            this.scoreChange = scoreChange;
        }
    
        @Override
        public String toString() {
            return "PointEvent{" +
                    "type=" + type +
                    ", timestamp=" + timestamp +
                    ", points=" + scoreChange +
                    '}';
        }
    }
    

    Some example of SR created by the code.

    0 讨论(0)
  • 2020-12-12 09:32

    If you are looking for horizontal SR lines, I would rather want to know the whole distribution. But I think it is also a good assumption to just take the max of your histogram.

    # python + pandas
    
    spy["Close"][:60].plot()
    hist, border = np.histogram(spy["Close"][:60].values, density=False)
    sr = border[np.argmax(hist)]
    plt.axhline(y=sr, color='r', linestyle='-')
    
    

    You might need to tweak the bins and eventually you want to plot the whole bin not just the lower bound.

    lower_bound = border[np.argmax(hist)]
    upper_bound = border[np.argmax(hist) + 1]
    

    PS the underlying "idea" is very similar to @Nilendu's solution.

    0 讨论(0)
  • 2020-12-12 09:36

    Interpretations of Support & Resistance levels is very subjective. A lot of people do it different ways. […] When I am evaluating S&R from the charts, I am looking for two primary things:

    • Bounce off - There needs to be a visible departure (bounce off) from the horizontal line which is perceived to define the level of support or resistance.

    • Multiple touches - A single touch turning point is not sufficient to indicate establish support or resistance levels. Multiple touches to the same approximately level should be present, such that a horizontal line could be drawn through those turning points.

    0 讨论(0)
  • 2020-12-12 09:38

    The best way I have found to get SR levels is with clustering. Maxima and Minima is calculated and then those values are flattened (like a scatter plot where x is the maxima and minima values and y is always 1). You then cluster these values using Sklearn.

    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    from sklearn.cluster import AgglomerativeClustering
    
    # Calculate VERY simple waves
    mx = df.High_15T.rolling( 100 ).max().rename('waves')
    mn = df.Low_15T.rolling( 100 ).min().rename('waves')
    
    mx_waves = pd.concat([mx,pd.Series(np.zeros(len(mx))+1)],axis = 1)
    mn_waves = pd.concat([mn,pd.Series(np.zeros(len(mn))+-1)],axis = 1)    
    
    mx_waves.drop_duplicates('waves',inplace = True)
    mn_waves.drop_duplicates('waves',inplace = True)
    
    W = mx_waves.append(mn_waves).sort_index()
    W = W[ W[0] != W[0].shift() ].dropna()
    
    # Find Support/Resistance with clustering
    
    # Create [x,y] array where y is always 1
    X = np.concatenate((W.waves.values.reshape(-1,1),
                        (np.zeros(len(W))+1).reshape(-1,1)), axis = 1 )
    
    # Pick n_clusters, I chose the sqrt of the df + 2
    n = round(len(W)**(1/2)) + 2
    cluster = AgglomerativeClustering(n_clusters=n,
              affinity='euclidean', linkage='ward')
    cluster.fit_predict(X)
    W['clusters'] = cluster.labels_
    
    # I chose to get the index of the max wave for each cluster
    W2 = W.loc[W.groupby('clusters')['waves'].idxmax()]
    
    # Plotit
    fig, axis = plt.subplots()
    for row in W2.itertuples():
    
        axis.axhline( y = row.waves, 
                color = 'green', ls = 'dashed' )
    
    axis.plot( W.index.values, W.waves.values )
    plt.show()
    
    0 讨论(0)
提交回复
热议问题