89 votes

Algorithme de soutien et de résistance - Analyse technique

J'ai un graphique intra-day et j'essaie de trouver comment calculer les niveaux de support et de résistance. Quelqu'un connaît un algorithme pour le faire, ou un bon point de départ ?

112voto

Dr. ABT Points 8119

Oui, un algorithme très simple consiste à choisir une période de temps, disons 100 barres, puis à rechercher les points de retournement locaux, ou les maxima et minima. Les Maxima et Minima peuvent être calculés à partir d'un prix de clôture lissé en utilisant la première et la seconde dérivée (dy/dx et d^2y/dx). Lorsque dy/dx = zéro et d^y/dx est positif, vous avez un minima, lorsque dy/dx = zéro et d^2y/dx est négatif, vous avez un maxima.

En termes pratiques, cela pourrait être calculé en itérant sur votre série de prix de clôture lissés et en examinant trois points adjacents. Si les points sont plus bas/haut/bas en termes relatifs, vous avez un maximum, sinon plus haut/bas/haut, vous avez un minimum. Vous pouvez affiner cette méthode de détection pour examiner un plus grand nombre de points (disons 5, 7) et ne la déclencher que si les points de bordure sont éloignés d'un certain pourcentage du point central.

Une fois que vous avez les maxima et minima locaux, vous voulez alors rechercher des groupes de points de retournement situés à une certaine distance les uns des autres dans la direction Y. C'est simple. Prenez la liste des N points de retournement et calculez la distance Y entre eux et chacun des autres points de retournement découverts. Si la distance est inférieure à une constante fixe, vous avez trouvé deux points d'inflexion "proches", indiquant un éventuel soutien/résistance.

Vous pourriez alors classer vos lignes S/R, de sorte que deux points d'inflexion à 20 $ soient moins importants que trois points d'inflexion à 20 $, par exemple.

Une extension de cette méthode consisterait à calculer des lignes de tendance. Avec la liste des points de retournement découverts, prenez maintenant chaque point à son tour et sélectionnez deux autres points, en essayant d'ajuster une équation de ligne droite. Si l'équation peut être résolue dans une certaine marge d'erreur, vous avez une ligne de tendance inclinée. Dans le cas contraire, écartez-les et passez à la triplette de points suivante.

La raison pour laquelle vous avez besoin de trois points à la fois pour calculer les lignes de tendance est que deux points quelconques peuvent être utilisés dans l'équation de la ligne droite. Une autre façon de calculer les lignes de tendance serait de calculer l'équation de la ligne droite de toutes les paires de points de retournement, puis de voir si un troisième point (ou plus d'un) se trouve sur la même ligne droite avec une marge d'erreur. Si un ou plusieurs autres points se trouvent sur cette ligne, bingo, vous avez calculé une ligne de tendance Support/Résistance.

J'espère que cela vous aidera. Pas d'exemples de code, désolé, je vous donne juste quelques idées sur la façon dont cela pourrait être fait. En résumé :

Entrées du système

  • Période de rétroaction L (nombre de barres)
  • Cours de clôture des barres L
  • Facteur de lissage (pour lisser le prix de clôture)
  • Marge d'erreur ou Delta (distance minimale entre les points de virage pour constituer une correspondance)

Sorties

  • Liste des points de retournement, appelez-les tPoints[] (x,y)
  • Liste de lignes de tendance potentielles, chacune avec l'équation de la ligne (y = mx + c)

EDIT : Mise à jour

J'ai récemment appris un indicateur très simple appelé Canal de Donchian qui trace un canal du plus haut sommet sur 20 barres, et du plus bas. Il peut être utilisé pour tracer un niveau approximatif de support et de résistance. Mais le canal de Donchian ci-dessus, avec des points de retournement, est plus cool ^_^.

0 votes

Bonjour Andrew, j'ai vérifié votre idée, je n'arrive toujours pas à comprendre comment calculer les minima et maxima, parce que je n'ai pas la formule de y (x=valeur temps, y = prix), et j'en ai besoin pour obtenir les dérivées 1 et 2, pouvez-vous m'expliquer ? merci beaucoup. Yaron

0 votes

Ce que vous devez faire est d'effectuer une différentiation numérique des prix de clôture lissés pour déterminer dy/dx : fr.m.wikipedia.org/wiki/Numerical_differentiation . Ensuite, effectuez à nouveau la différentiation pour trouver d^2y/dx. Notez qu'il existe d'autres moyens plus simples de trouver les points de retournement, consultez l'indicateur zigzag : onlinetradingconcepts.com/TechnicalAnalysis/ZigZag.html

0 votes

Bonjour Andrew, merci pour votre réponse. Je pense que je peux utiliser l'EMA20 comme liste de prix lissée. Je sais comment différencier une fonction, mais pas une liste de valeurs, avez-vous une astuce pour le faire ? Merci pour tout, vous m'avez déjà beaucoup aidé

22voto

Pritesh Mhatre Points 1888

J'utilise un algorithme beaucoup moins complexe dans mon système de trading algorithmique.

Les étapes suivantes constituent un côté de l'algorithme et sont utilisées pour calculer les niveaux de support. Veuillez lire les notes sous l'algorithme pour comprendre comment calculer les niveaux de résistance.

Algorithme

  1. Décomposer les séries temporelles en segments de taille N (Disons, N = 5)

  2. Identifiez les valeurs minimales de chaque segment, vous aurez un tableau des valeurs minimales de tous les segments = :arrayOfMin

  3. Trouver le minimum de (:arrayOfMin) = :minValue

  4. Voir si l'une des valeurs restantes se situe dans l'intervalle (X% de :minValue) (Disons, X = 1,3%)

  5. Créer un tableau séparé (:supportArr)

    • ajouter des valeurs dans la plage et supprimer ces valeurs de :arrayOfMin
    • ajoutez également :minValue de l'étape 3
  6. Calculer le support (ou la résistance)

    • Faites une moyenne de ce tableau = niveau de soutien
    • Si le support est testé de nombreuses fois, il est considéré comme fort.
    • force_de_soutien \= supportArr.length
    • niveau_type (SUPPORT|RESISTANCE) = Si le prix actuel est inférieur au support, le support change de rôle et devient une résistance.
  7. Répétez les étapes 3 à 7 jusqu'à ce que :arrayOfMin soit vide.

  8. Vous aurez toutes les valeurs de support/résistance avec une force. Maintenant, lissez ces valeurs, si certains niveaux de support sont trop proches, éliminez l'un d'entre eux.

  9. Ces supports/résistances ont été calculés en tenant compte de la recherche de niveaux de support. Vous devez effectuer les étapes 2 à 9 en tenant compte de la recherche de niveaux de résistance. Veuillez consulter les notes et la mise en œuvre.

Notas:

  • Ajustez les valeurs de N et X pour obtenir des résultats plus précis.
    • Exemple, pour des actions ou des indices boursiers moins volatils, utilisez (N = 10, X = 1,2%).
    • Pour les actions très volatiles, utiliser (N = 22, X = 1,5%)
  • Pour la résistance, la procédure est exactement inverse (utiliser la fonction maximum au lieu de minimum).
  • Cet algorithme a été volontairement gardé simple pour éviter toute complexité, il peut être amélioré pour donner de meilleurs résultats.

Voici ma mise en œuvre :

public interface ISupportResistanceCalculator {

/**
 * Identifies support / resistance levels.
 * 
 * @param timeseries
 *            timeseries
 * @param beginIndex
 *            starting point (inclusive)
 * @param endIndex
 *            ending point (exclusive)
 * @param segmentSize
 *            number of elements per internal segment
 * @param rangePct
 *            range % (Example: 1.5%)
 * @return A tuple with the list of support levels and a list of resistance
 *         levels
 */
Tuple<List<Level>, List<Level>> identify(List<Float> timeseries,
        int beginIndex, int endIndex, int segmentSize, float rangePct);
}

Classe principale de la calculatrice

/**
 * 
 */
package com.perseus.analysis.calculator.technical.trend;

import static com.perseus.analysis.constant.LevelType.RESISTANCE;
import static com.perseus.analysis.constant.LevelType.SUPPORT;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import com.google.common.collect.Lists;
import com.perseus.analysis.calculator.mean.IMeanCalculator;
import com.perseus.analysis.calculator.timeseries.ITimeSeriesCalculator;
import com.perseus.analysis.constant.LevelType;
import com.perseus.analysis.model.Tuple;
import com.perseus.analysis.model.technical.Level;
import com.perseus.analysis.model.timeseries.ITimeseries;
import com.perseus.analysis.util.CollectionUtils;

/**
 * A support and resistance calculator.
 * 
 * @author PRITESH
 * 
 */
public class SupportResistanceCalculator implements
        ISupportResistanceCalculator {

    static interface LevelHelper {

        Float aggregate(List<Float> data);

        LevelType type(float level, float priceAsOfDate, final float rangePct);

        boolean withinRange(Float node, float rangePct, Float val);

    }

    static class Support implements LevelHelper {

        @Override
        public Float aggregate(final List<Float> data) {
            return Collections.min(data);
        }

        @Override
        public LevelType type(final float level, final float priceAsOfDate,
                final float rangePct) {
            final float threshold = level * (1 - (rangePct / 100));
            return (priceAsOfDate < threshold) ? RESISTANCE : SUPPORT;
        }

        @Override
        public boolean withinRange(final Float node, final float rangePct,
                final Float val) {
            final float threshold = node * (1 + (rangePct / 100f));
            if (val < threshold)
                return true;
            return false;
        }

    }

    static class Resistance implements LevelHelper {

        @Override
        public Float aggregate(final List<Float> data) {
            return Collections.max(data);
        }

        @Override
        public LevelType type(final float level, final float priceAsOfDate,
                final float rangePct) {
            final float threshold = level * (1 + (rangePct / 100));
            return (priceAsOfDate > threshold) ? SUPPORT : RESISTANCE;
        }

        @Override
        public boolean withinRange(final Float node, final float rangePct,
                final Float val) {
            final float threshold = node * (1 - (rangePct / 100f));
            if (val > threshold)
                return true;
            return false;
        }

    }

    private static final int SMOOTHEN_COUNT = 2;

    private static final LevelHelper SUPPORT_HELPER = new Support();

    private static final LevelHelper RESISTANCE_HELPER = new Resistance();

    private final ITimeSeriesCalculator tsCalc;

    private final IMeanCalculator meanCalc;

    public SupportResistanceCalculator(final ITimeSeriesCalculator tsCalc,
            final IMeanCalculator meanCalc) {
        super();
        this.tsCalc = tsCalc;
        this.meanCalc = meanCalc;
    }

    @Override
    public Tuple<List<Level>, List<Level>> identify(
            final List<Float> timeseries, final int beginIndex,
            final int endIndex, final int segmentSize, final float rangePct) {

        final List<Float> series = this.seriesToWorkWith(timeseries,
                beginIndex, endIndex);
        // Split the timeseries into chunks
        final List<List<Float>> segments = this.splitList(series, segmentSize);
        final Float priceAsOfDate = series.get(series.size() - 1);

        final List<Level> levels = Lists.newArrayList();
        this.identifyLevel(levels, segments, rangePct, priceAsOfDate,
                SUPPORT_HELPER);

        this.identifyLevel(levels, segments, rangePct, priceAsOfDate,
                RESISTANCE_HELPER);

        final List<Level> support = Lists.newArrayList();
        final List<Level> resistance = Lists.newArrayList();
        this.separateLevels(support, resistance, levels);

        // Smoothen the levels
        this.smoothen(support, resistance, rangePct);

        return new Tuple<>(support, resistance);
    }

    private void identifyLevel(final List<Level> levels,
            final List<List<Float>> segments, final float rangePct,
            final float priceAsOfDate, final LevelHelper helper) {

        final List<Float> aggregateVals = Lists.newArrayList();

        // Find min/max of each segment
        for (final List<Float> segment : segments) {
            aggregateVals.add(helper.aggregate(segment));
        }

        while (!aggregateVals.isEmpty()) {
            final List<Float> withinRange = new ArrayList<>();
            final Set<Integer> withinRangeIdx = new TreeSet<>();

            // Support/resistance level node
            final Float node = helper.aggregate(aggregateVals);

            // Find elements within range
            for (int i = 0; i < aggregateVals.size(); ++i) {
                final Float f = aggregateVals.get(i);
                if (helper.withinRange(node, rangePct, f)) {
                    withinRangeIdx.add(i);
                    withinRange.add(f);
                }
            }

            // Remove elements within range
            CollectionUtils.remove(aggregateVals, withinRangeIdx);

            // Take an average
            final float level = this.meanCalc.mean(
                    withinRange.toArray(new Float[] {}), 0, withinRange.size());
            final float strength = withinRange.size();

            levels.add(new Level(helper.type(level, priceAsOfDate, rangePct),
                    level, strength));

        }

    }

    private List<List<Float>> splitList(final List<Float> series,
            final int segmentSize) {
        final List<List<Float>> splitList = CollectionUtils
                .convertToNewLists(CollectionUtils.splitList(series,
                        segmentSize));

        if (splitList.size() > 1) {
            // If last segment it too small
            final int lastIdx = splitList.size() - 1;
            final List<Float> last = splitList.get(lastIdx);
            if (last.size() <= (segmentSize / 1.5f)) {
                // Remove last segment
                splitList.remove(lastIdx);
                // Move all elements from removed last segment to new last
                // segment
                splitList.get(lastIdx - 1).addAll(last);
            }
        }

        return splitList;
    }

    private void separateLevels(final List<Level> support,
            final List<Level> resistance, final List<Level> levels) {
        for (final Level level : levels) {
            if (level.getType() == SUPPORT) {
                support.add(level);
            } else {
                resistance.add(level);
            }
        }
    }

    private void smoothen(final List<Level> support,
            final List<Level> resistance, final float rangePct) {
        for (int i = 0; i < SMOOTHEN_COUNT; ++i) {
            this.smoothen(support, rangePct);
            this.smoothen(resistance, rangePct);
        }
    }

    /**
     * Removes one of the adjacent levels which are close to each other.
     */
    private void smoothen(final List<Level> levels, final float rangePct) {
        if (levels.size() < 2)
            return;

        final List<Integer> removeIdx = Lists.newArrayList();
        Collections.sort(levels);

        for (int i = 0; i < (levels.size() - 1); i++) {
            final Level currentLevel = levels.get(i);
            final Level nextLevel = levels.get(i + 1);
            final Float current = currentLevel.getLevel();
            final Float next = nextLevel.getLevel();
            final float difference = Math.abs(next - current);
            final float threshold = (current * rangePct) / 100;

            if (difference < threshold) {
                final int remove = currentLevel.getStrength() >= nextLevel
                        .getStrength() ? i : i + 1;
                removeIdx.add(remove);
                i++; // start with next pair
            }
        }

        CollectionUtils.remove(levels, removeIdx);
    }

    private List<Float> seriesToWorkWith(final List<Float> timeseries,
            final int beginIndex, final int endIndex) {

        if ((beginIndex == 0) && (endIndex == timeseries.size()))
            return timeseries;

        return timeseries.subList(beginIndex, endIndex);

    }

}

Voici quelques cours de soutien :

public enum LevelType {

    SUPPORT, RESISTANCE

}

public class Tuple<A, B> {

    private final A a;

    private final B b;

    public Tuple(final A a, final B b) {
        super();
        this.a = a;
        this.b = b;
    }

    public final A getA() {
        return this.a;
    }

    public final B getB() {
        return this.b;
    }

    @Override
    public String toString() {
        return "Tuple [a=" + this.a + ", b=" + this.b + "]";
    };

}

public abstract class CollectionUtils {

/**
 * Removes items from the list based on their indexes.
 * 
 * @param list
 *            list
 * @param indexes
 *            indexes this collection must be sorted in ascending order
 */
public static <T> void remove(final List<T> list,
        final Collection<Integer> indexes) {
    int i = 0;
    for (final int idx : indexes) {
        list.remove(idx - i++);
    }
}

/**
 * Splits the given list in segments of the specified size.
 * 
 * @param list
 *            list
 * @param segmentSize
 *            segment size
 * @return segments
 */
public static <T> List<List<T>> splitList(final List<T> list,
        final int segmentSize) {
    int from = 0, to = 0;
    final List<List<T>> result = new ArrayList<>();

    while (from < list.size()) {
        to = from + segmentSize;
        if (to > list.size()) {
            to = list.size();
        }
        result.add(list.subList(from, to));
        from = to;
    }

    return result;
}

}

/**
 * This class represents a support / resistance level.
 * 
 * @author PRITESH
 * 
 */
public class Level implements Serializable {

    private static final long serialVersionUID = -7561265699198045328L;

    private final LevelType type;

    private final float level, strength;

    public Level(final LevelType type, final float level) {
        this(type, level, 0f);
    }

    public Level(final LevelType type, final float level, final float strength) {
        super();
        this.type = type;
        this.level = level;
        this.strength = strength;
    }

    public final LevelType getType() {
        return this.type;
    }

    public final float getLevel() {
        return this.level;
    }

    public final float getStrength() {
        return this.strength;
    }

    @Override
    public String toString() {
        return "Level [type=" + this.type + ", level=" + this.level
                + ", strength=" + this.strength + "]";
    }

}

1 votes

Oui, ça marche, c'est sûr. Mais il n'est pas parfait. Une fois que vous avez compris l'algorithme, vous devez le modifier pour obtenir des résultats de plus en plus précis. Il vous donne cette flexibilité. Essayez d'abord de comprendre les étapes, puis je vous recommande de l'essayer avec des données boursières. Veuillez consulter la section "Notes" de la réponse.

0 votes

Les segments de l'étape 1 se croisent-ils les uns avec les autres (comme une veuve qui se déplace), ou sont-ils séparés (comme des tranches du tableau original) ?

0 votes

De bonnes idées ! T

12voto

Jacob Amos Points 866

J'ai créé un paquet qui met en œuvre des lignes de tendance de soutien et de résistance comme celles que vous demandez. Voici quelques exemples :

import numpy as np
import pandas.io.data as pd
from matplotlib.pyplot import *
gentrends('fb', window = 1.0/3.0)

Sortie

Cet exemple ne tire que les prix de clôture ajustés, mais si vous avez des données intrajournalières déjà chargées, vous pouvez également lui fournir des données brutes sous forme de tableau numpy et il mettra en œuvre le même algorithme sur ces données que si vous lui fournissiez simplement un symbole de téléscripteur.

Je ne sais pas si c'est exactement ce que vous cherchiez, mais j'espère que cela vous aidera à démarrer. Le code et quelques explications supplémentaires peuvent être trouvés sur la page GitHub où je l'ai hébergé : https://github.com/dysonance/Trendy

0 votes

Merci ! Je vais faire un essai

0 votes

Est-ce qu'il trouve simplement les deux valeurs les plus grandes et les plus petites et calcule les lignes qui passent par ces points ?

0 votes

Pour cette fonction spécifique, elle trouve le maximum et le minimum global des données, puis le second maximum et le second minimum le plus important. à l'extérieur de de la période de la fenêtre que vous lui donnez. Ainsi, si vous lui donnez une fenêtre de 30 périodes, il trouvera le max/min le plus élevé qui est au moins à 30 périodes du max/min global. Il regarde d'abord vers l'avant, mais s'il n'y a plus 30 périodes dans la série, il regarde vers l'arrière. Ici, je lui ai donné une fenêtre de 1,0/3,0, qu'il interprète comme un tiers de la longueur des données. Il existe d'autres méthodes qui fournissent des approches plus flexibles si vous êtes intéressé :)

11voto

Nilendu Points 129

J'ai trouvé une autre façon de calculer le support/résistance de façon dynamique.

Des pas :

  1. Créez une liste des prix importants - Le haut et le bas de chaque bougie dans votre fourchette sont importants. Chacun de ces prix est fondamentalement un SR (Support / Résistance) probable.

  2. Donnez une note à chaque prix.

  3. Trier les prix par score et supprimer ceux qui sont proches les uns des autres (à une distance de x% les uns des autres).

  4. Imprimer les N meilleurs prix et avoir un score minimum de Y. Ce sont vos résistances de soutien. Cela a très bien fonctionné pour moi dans ~300 actions différentes.

La technique de notation

Un prix agit comme un SR fort s'il y a de nombreuses bougies qui s'en rapprochent mais ne peuvent pas le franchir. Ainsi, pour chaque bougie qui est proche de ce prix (à une distance de y% du prix), nous ajouterons +S1 au score. Pour chaque bougie qui coupe ce prix, nous ajouterons -S2(négatif) au score.

Cela devrait vous donner une idée très simple de la manière dont vous pouvez attribuer des notes à ces éléments.

Maintenant, vous devez le modifier en fonction de vos besoins. Voici quelques modifications que j'ai faites et qui ont beaucoup amélioré les performances :

  1. Différents scores pour différents types de coupes. Si le corps d'une bougie coupe le prix, alors le changement de score est -S3 mais la mèche d'une bougie coupe le prix, le changement de score est -S4. Ici, Abs(S3) > Abs(S4) car la coupure par le corps est plus significative que la coupure par la mèche.

  2. Si la bougie qui ferme le prix mais ne peut pas le croiser est un haut (plus haut que deux bougies de chaque côté) ou un bas (plus bas que deux bougies de chaque côté), alors ajoutez un score plus élevé que les autres bougies normales qui ferment près de cela.

  3. Si la bougie de clôture proche de ce point est un haut ou un bas, et que le prix était dans une tendance à la baisse ou à la hausse (au moins y% de mouvement), alors ajoutez un score plus élevé à ce point.

  4. Vous pouvez supprimer certains prix de la liste initiale. Je ne considère un prix que s'il est le plus haut ou le plus bas parmi les N bougies situées de part et d'autre de celui-ci.

Voici un extrait de mon 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;
    }

Quelques classes d'aide :

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 +
                '}';
    }
}

Quelques exemples de SR créés par le code.

enter image description here

enter image description here

2 votes

Comment avez-vous réussi à tracer ce graphique sur TradingView ? Avez-vous un équivalent de Pine script ?

1 votes

Nilendu, votre approche semble donner d'excellents résultats. J'ai essayé de faire fonctionner votre code, mais la source que vous avez postée est incomplète (il manque des choses comme DIFF_PERC_FROM_EXTREME, et scoreForCutBody etc.) Pourriez-vous s'il vous plaît poster un ensemble plus complet de fichiers de code ? Ou au moins les valeurs que vous utilisez pour ces constantes ? Merci !

0 votes

Les variables dépendront du marché et du timeFrame. La différence DIFF_PERC... est d'environ 0,2 % lorsqu'elle est testée sur les graphiques quotidiens du marché boursier indien - NIFTY.

5voto

Pedro Lobito Points 6794

Voici une fonction python qui permet de trouver support / resistance niveaux

Cette fonction prend un tableau numpy du dernier prix négocié et retourne une et renvoie une liste de niveaux de support et de résistance respectivement. n est le nombre de d'entrées à analyser.

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 votes

Et si vous vouliez tracer ces lignes ? Comment trouveriez-vous les dates correspondantes ?

0 votes

Je suppose que vous pouvez essayer de faire correspondre les prix de support/résistance sur les données d'origine, qui devraient inclure un champ de date. J'ai déjà tracé ce type de graphique, mais je ne me souviens plus du projet !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X