【问题标题】:Support Resistance Algorithm - Technical analysis支撑阻力算法 - 技术分析
【发布时间】:2011-12-21 08:43:12
【问题描述】:

我有一个日内图表,我想弄清楚如何计算 支撑位和阻力位,有人知道这样做的算法,还是一个好的起点?

【问题讨论】:

    标签: algorithm


    【解决方案1】:

    是的,一个非常简单的算法是选择一个时间范围,比如 100 个柱,然后寻找局部转折点,或最大值和最小值。最大值和最小值可以通过使用一阶和二阶导数(dy/dx 和 d^2y/dx)从平滑收盘价计算。当 dy/dx = 零且 d^y/dx 为正时,您有一个最小值,当 dy/dx = 零且 d^2y/dx 为负时,您有一个最大值。

    实际上,这可以通过迭代平滑的收盘价序列并查看三个相邻点来计算。如果这些点相对而言更低/更高/更低,那么你有一个最大值,否则更高/更低/更高你有一个最小值。您可能希望微调此检测方法以查看更多点(例如 5、7),并且仅在边缘点距离中心点一定 % 时触发。这类似于 ZigZag 指标使用的算法。

    一旦你有了局部最大值和最小值,你就想在 Y 方向上寻找彼此一定距离内的转折点集群。这很简单。获取 N 个转折点的列表并计算它与其他每个发现的转折点之间的 Y 距离。如果距离小于一个固定常数,那么你已经找到了两个“接近”的转折点,表明可能的支撑/阻力。

    然后您可以对您的 S/R 线进行排名,例如,20 美元的两个转折点不如 20 美元的三个转折点重要。

    对此的扩展是计算趋势线。随着发现的转折点列表,现在依次选择每个点并选择另外两个点,尝试拟合直线方程。如果方程在一定的误差范围内是可解的,那么你就有一条倾斜的趋势线。如果不是,则丢弃并继续下一个三重点。

    您一次需要三个来计算趋势线的原因是任何两个点都可以用于直线方程。计算趋势线的另一种方法是计算所有转折点对的直线方程,然后查看第三个点(或多个点)是否在误差范围内位于同一直线上。如果 1 个或多个其他点确实位于这条线上,那么您已经计算了支撑/阻力趋势线。

    我希望这会有所帮助。没有代码示例对不起,我只是给你一些关于如何完成的想法。总结:

    系统输入

    • 回溯期 L(柱数)
    • L 柱的收盘价
    • 平滑因子(平滑收盘价)
    • Error Margin 或 Delta(构成匹配的转折点之间的最小距离)

    输出

    • 转折点列表,称它们为 tPoints[] (x,y)
    • 潜在趋势线列表,每条都带有线方程 (y = mx + c)

    编辑:更新

    我最近学习了一个非常简单的指标,称为 Donchian Channel,它基本上绘制了 20 个柱中最高价和最低价的通道。它可用于绘制近似支撑阻力位。但以上——有转折点的唐奇安海峡更酷^_^

    【讨论】:

    • 嗨安德鲁,我检查了你的想法,我仍然无法弄清楚如何计算最小值和最大值,因为我没有 y 的公式(x=时间价值,y=价格),我需要它来获得一阶和二阶导数,你能解释一下吗?非常感谢您。亚龙
    • 您需要做的是对平滑收盘价进行数值微分以确定 dy/dx:en.m.wikipedia.org/wiki/Numerical_differentiation。之后再次执行微分以找到 d^2y/dx。请注意,还有其他更简单的方法可以找到转折点,请查看之字形指标:onlinetradingconcepts.com/TechnicalAnalysis/ZigZag.html
    • 是的,实际上它非常简单。请在此处查看之前的答案:stackoverflow.com/questions/373186/… 要区分函数,您可以计算 [f(x+h) - f(x-h)] / 2h (其中 h=1 和 f(x) 是您的输入数组)。实际上,这意味着如果您的数组长度为 10(索引 0..9),那么您计算 diff[8] = (input[9] - input[7]) / 2.
    • ... 通过计算 [f(x-2h) - 8f(x+h) + 8f(x-h) - f(x+2h),您可以使用五个点执行更精确的微分] / 12 小时。同样对于长度为 10 的数组,这意味着计算 diff[7] = (input[5] - 8*input[8] + 8*input[6] - input[9])/12。正如您所看到的,使用更多点来计算导数意味着您在计算中引入了更多滞后。我建议对两者都进行试验。
    • @Yaron,好的,这是一个好的开始。您遇到的是离散时间问题。在数学中,因为函数 dy/dx 是连续的,所以你总是会得到零。在实际代码中,当您的数据点以离散值分隔时,您可能会发现一个大于 0,而下一个小于 0,因此介于两者之间的某个位置会超过零。这已经足够好了。也许您应该尝试提出一个新问题——如何识别股价数据中的转折点——链接到这个问题并展示你迄今为止的努力?你可能会得到一些很好的反馈。最好的问候!
    【解决方案2】:

    我在我的算法交易系统中使用了一种简单得多的算法。

    以下步骤是算法的一方面,用于计算支持水平。请阅读算法下方的注释以了解如何计算阻力水平。

    算法

    1. 将时间序列分成大小为 N 的段(例如,N = 5)
    2. 确定每个段的最小值,您将获得所有段的最小值数组 = :arrayOfMin
    3. 求 (:arrayOfMin) = :minValue 的最小值
    4. 查看剩余值是否在范围内(X% of :minValue)(例如,X = 1.3%)
    5. 创建一个单独的数组 (:supportArr)
      • 在范围内添加值并从 :arrayOfMin 中删除这些值
      • 还添加步骤 3 中的 :minValue
    6. 计算支撑(或阻力)

      • 取这个数组的平均值 = support_level
      • 如果多次测试支持,则认为它很强大。
      • strength_of_support = supportArr.length
      • level_type (SUPPORT|RESISTANCE) = 现在,如果当前价格低于支撑位,那么支撑位就会改变角色并变成阻力位
    7. 重复步骤 3 到 7,直到 :arrayOfMin 为空

    8. 您将拥有所有具有强度的支撑/阻力值。现在平滑这些值,如果任何支持水平太接近,则消除其中一个。
    9. 这些支撑/阻力是在考虑支撑位搜索的情况下计算的。 考虑到阻力位搜索,您需要执行步骤 2 到 9。请参阅注释和实施。

    注意事项:

    • 调整 N 和 X 的值以获得更准确的结果。
      • 例如,对于波动较小的股票或股票指数,使用 (N = 10, X = 1.2%)
      • 对于高波动性股票使用(N = 22,X = 1.5%)
    • 对于电阻,过程正好相反(使用最大值函数而不是最小值)
    • 此算法特意保持简单以避免复杂性,可以对其进行改进以提供更好的结果。

    这是我的实现:

    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);
    }
    

    主计算器类

    /**
     * 
     */
    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);
    
        }
    
    }
    

    以下是一些支持类:

    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 步中的片段是否与另一个片段相交(如移动寡妇),或者它们是分开的(如原始数组的切片)?
    • 好主意! min 或 max 的滑动窗口称为 donchian 通道
    【解决方案3】:

    我整理了一个包,它可以像您所询问的那样实现支撑和阻力趋势线。以下是一些示例:

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

    Output

    该示例仅提取调整后的收盘价,但如果您已经加载了盘中数据,您还可以将原始数据作为 numpy 数组提供给它,它将对该数据执行与您刚刚提供它时相同的算法股票代码。

    不确定这是否正是您想要的,但希望这有助于您入门。代码和更多解释可以在我托管的 GitHub 页面上找到:https://github.com/dysonance/Trendy

    【讨论】:

    • 谢谢!我试试看
    • 它是否只是找到两个最大值和最小值并计算从这些点经过的线?
    • 对于这个特定的函数,它会找到数据的全局最大值和最小值,然后在你输入的窗口周期之外找到第二个最大的最大值和最小值。因此,如果您给它一个 30 个周期的窗口,它将找到距离全局最大/最小值至少 30 个周期的最高最大值/最小值。它首先向前看,但如果系列中没有 30 个句点,那么它将向后看。这里我给它一个 1.0/3.0 的窗口,它解释为数据长度的三分之一。如果您有兴趣,那里还有其他方法可以提供一些更灵活的方法:)
    【解决方案4】:

    我找到了另一种动态计算支撑/阻力的方法。

    步骤:

    1. 创建重要价格列表 - 您范围内每根蜡烛的最高价和最低价很重要。这些价格中的每一个基本上都是一个可能的 SR(支撑/阻力)。

    2. 给每个价格打分。

    3. 按分数对价格进行排序并删除彼此靠近的价格(彼此相距 x%)。

    4. 打印前 N 个价格且最低分数为 Y。这些是您的支撑阻力。它在大约 300 种不同的股票中对我很有效。

    评分技术

    如果有许多蜡烛接近此价格但无法跨越此价格,则该价格充当强 SR。 因此,对于接近该价格的每根蜡烛(距离价格 y% 以内),我们将在分数上加上 +S1。 对于每根穿过该价格的蜡烛,我们将在分数上添加-S2(负)。

    这应该让您对如何为此分配分数有一个非常基本的想法。

    现在您必须根据自己的要求对其进行调整。 我做了一些调整,大大提高了性能,如下所示:

    1. 不同类型切割的不同分数。如果蜡烛的主体穿过价格,那么分数变化是-S3,但蜡烛芯穿过价格,分数变化是-S4。这里 Abs(S3) > Abs(S4) 因为身体切割比灯芯切割更重要。

    2. 如果收盘价但无法交叉的蜡烛是高点(每边高于两根蜡烛)或低点(每边少于两根蜡烛),则添加比其他正常值更高的分数蜡烛在此附近收盘。

    3. 如果蜡烛在此附近收盘时为高点或低点,并且价格处于下降趋势或上升趋势(至少 y% 移动),则在该点上添加更高的分数。

      李>
    4. 您可以从初始列表中删除一些价格。我只考虑一个价格是它两侧 N 根蜡烛中最高或最低的价格。

    这是我的代码的 sn-p。

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

    一些 Helper 类:

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

    代码创建的 SR 的一些示例。

    【讨论】:

    • 您是如何在 TradingView 上绘制的?你有对应的 Pine 脚本吗?
    • Nilendu,您的方法似乎取得了很好的效果。我试图让您的代码正常工作,但您发布的源代码不完整(缺少 DIFF_PERC_FROM_EXTREME 和 scoreForCutBody 等内容)您能否发布更完整的代码文件集?或者至少是您用于这些常量的值?谢谢!!
    • 变量将取决于市场和时间框架。在 IndianStockMarket 的日线图中测试时,DIFF_PERC... 的差异约为 0.2% - NIFTY
    【解决方案5】:

    这是一个查找support/resistance级别的python函数

    此函数接受一个包含最后交易价格的 numpy 数组并返回一个 分别列出支撑位和阻力位。 n 是数字 要扫描的条目数。

    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

    【讨论】:

    • 如果你想绘制这些线怎么办?您如何找到相应的日期?
    • 我猜你可以尝试匹配原始数据上的支撑/阻力价格,其中应该包含一个日期字段。我以前画过这个,但我不记得这个项目了!
    • 帮助。请问这需要什么输入。
    • @BenderRodriguez 一个包含最后收盘价的数组:ltp = [11.11, 12.11, 13.11, 10.11]n 是要扫描的条目数,n 可以是 len(ltp)
    【解决方案6】:

    我发现获得 SR 级别的最佳方法是使用聚类。计算最大值和最小值,然后将这些值展平(如散点图,其中 x 是最大值和最小值,y 始终为 1)。然后,您使用 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()
    

    【讨论】:

    • 赢家,赢家鸡肉晚餐。终于有人有正确的想法了。为什么要进行凝聚聚类?它似乎比 K-means 更容易,也比 DBSCAN 更合适。但是你有没有尝试过其他方法?
    • K-Means 更快,但我发现它不太准确。我尝试了其他一些,但我不记得哪些。我发现 Agglomerative 是最好的。
    • 您介意解释一下解决方案吗?我真的很喜欢为此使用聚类算法的想法,我只是没有遵循您之前所做的事情
    • 您是否尝试过使用肘部方法来达到最佳 n_clusters?下面是一个例子:github.com/judopro/Stock_Support_Resistance_ML/blob/master/… 还有,.fit_predict 是一个错误还是你没有分配它就这样运行它。
    • 我确实记得尝试过肘部方法,但我发现太多重要的点包含在错误的集群中。所以我只是想出了一个简单的等式,并不完美,但却是一个很好的改进起点。至于 fit.predict,我完全按照你看到的那样运行它。无需分配。
    【解决方案7】:

    我简要阅读了 Jacob 的贡献。我认为下面的代码可能有一些问题: # 现在是最小值 如果 min1 - 窗口

    # Now find the indices of the secondary extrema
    max2 = np.where(x == max2)[0][0]  # find the index of the 2nd max
    min2 = np.where(x == min2)[0][0]  # find the index of the 2nd min
    

    该算法确实尝试在给定窗口之外找到辅助最小值,但是由于可能存在重复值,因此与 np.where(x == min2)[0][0] 对应的位置可能位于窗口内窗口。

    【讨论】:

      【解决方案8】:

      这是 S/R 的 PineScript 代码。它不包括 Andrew 或 Nilendu 博士讨论的所有逻辑,但绝对是一个好的开始:

      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)
      

      【讨论】:

      • 这太棒了 - 我还没有阅读它以正确理解它并进行调整,但我已经将它应用到我的图表中,它与我所追求的差不多。谢谢。
      【解决方案9】:

      我不确定它是否真的是“支撑和阻力”检测,但这个呢:

      function getRanges(_nums=[], _diff=1, percent=true) {
          let nums = [..._nums];
          nums.sort((a,b) => a-b);
          
          const ranges = [];
          for (let i=0; i<nums.length; i+=1) {
              const num = nums[i];
              const diff = percent ? perc(_diff, num) : _diff;
              const range = nums.filter( j => isInRange(j, num-diff, num+diff) );
              if (range.length) {
                  ranges.push(range);
                  nums = nums.slice(range.length);
                  i = -1;
              }
          }
          return ranges;
      }
      
      function perc(percent, n) {
          return n * (percent * 0.01);
      }
      function isInRange(n, min, max) {
          return n >= min && n <= max;
      }
      
      

      假设您有一个close 价格数组:

      const nums = [12, 14, 15, 17, 18, 19, 19, 21, 28, 29, 30, 30, 31, 32, 34, 34, 36, 39, 43, 44, 48, 48, 48, 51, 52, 58, 60, 61, 67, 68, 69, 73, 73, 75, 87, 89, 94, 95, 96, 98];
      

      并且您想将数字拆分为一个数量,例如 5(或 5%)的差异,那么您将得到如下结果数组:

      const ranges = getRanges(nums, 5, false) // ranges of -5  to +5
      /* [
          [12, 14, 15, 17]
          [18, 19, 19, 21]
          [28, 29, 30, 30, 31, 32]
          [34, 34, 36, 39]
          [43, 44, 48, 48, 48]
          [51, 52]
          [58, 60, 61]
          [67, 68, 69]
          [73, 73, 75]
          [87, 89]
          [94, 95, 96, 98]
      ]
      */
      
      // or like
      //const ranges = getRanges(nums, 5, true)  // ranges of -5% to +5%
      
      

      因此,区间的长度越长,支撑/阻力区域就越重要。

      再次:不确定这是否可以归类为“支撑和阻力”)

      【讨论】:

        【解决方案10】:

        如果您正在寻找水平 SR 线,我宁愿想知道整个分布。但我认为只取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='-')
        
        

        您可能需要调整 bins,最终您想要绘制整个 bin 而不仅仅是下限。

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

        PS 底层的“想法”与@Nilendu 的解决方案非常相似。

        【讨论】:

          【解决方案11】:

          对支撑位和阻力位的解释非常主观。很多人以不同的方式做这件事。 [...] 当我从图表中评估 S&R 时,我正在寻找两个主要的东西:

          • 反弹 - 水平线需要有明显的偏离(反弹),这被认为定义了支撑或阻力水平。

          • 多次触摸 - 单次触摸转折点不足以指示建立的支撑位或阻力位。应该存在到相同近似水平的多次触摸,以便可以通过这些转折点绘制一条水平线。

          【讨论】:

          • 链接已失效 :-( ...这实际上就是为什么仅链接的答案不太受欢迎的原因
          • 是的,对不起。我错过了将此链接添加到 waybackmachine ...我编辑答案以删除链接。
          猜你喜欢
          • 2011-06-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多