【发布时间】:2011-12-21 08:43:12
【问题描述】:
我有一个日内图表,我想弄清楚如何计算 支撑位和阻力位,有人知道这样做的算法,还是一个好的起点?
【问题讨论】:
标签: algorithm
我有一个日内图表,我想弄清楚如何计算 支撑位和阻力位,有人知道这样做的算法,还是一个好的起点?
【问题讨论】:
标签: algorithm
是的,一个非常简单的算法是选择一个时间范围,比如 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 个或多个其他点确实位于这条线上,那么您已经计算了支撑/阻力趋势线。
我希望这会有所帮助。没有代码示例对不起,我只是给你一些关于如何完成的想法。总结:
系统输入
输出
编辑:更新
我最近学习了一个非常简单的指标,称为 Donchian Channel,它基本上绘制了 20 个柱中最高价和最低价的通道。它可用于绘制近似支撑阻力位。但以上——有转折点的唐奇安海峡更酷^_^
【讨论】:
我在我的算法交易系统中使用了一种简单得多的算法。
以下步骤是算法的一方面,用于计算支持水平。请阅读算法下方的注释以了解如何计算阻力水平。
算法
计算支撑(或阻力)
重复步骤 3 到 7,直到 :arrayOfMin 为空
注意事项:
这是我的实现:
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 + "]";
}
}
【讨论】:
我整理了一个包,它可以像您所询问的那样实现支撑和阻力趋势线。以下是一些示例:
import numpy as np
import pandas.io.data as pd
from matplotlib.pyplot import *
gentrends('fb', window = 1.0/3.0)
该示例仅提取调整后的收盘价,但如果您已经加载了盘中数据,您还可以将原始数据作为 numpy 数组提供给它,它将对该数据执行与您刚刚提供它时相同的算法股票代码。
不确定这是否正是您想要的,但希望这有助于您入门。代码和更多解释可以在我托管的 GitHub 页面上找到:https://github.com/dysonance/Trendy
【讨论】:
我找到了另一种动态计算支撑/阻力的方法。
步骤:
创建重要价格列表 - 您范围内每根蜡烛的最高价和最低价很重要。这些价格中的每一个基本上都是一个可能的 SR(支撑/阻力)。
给每个价格打分。
按分数对价格进行排序并删除彼此靠近的价格(彼此相距 x%)。
打印前 N 个价格且最低分数为 Y。这些是您的支撑阻力。它在大约 300 种不同的股票中对我很有效。
评分技术
如果有许多蜡烛接近此价格但无法跨越此价格,则该价格充当强 SR。 因此,对于接近该价格的每根蜡烛(距离价格 y% 以内),我们将在分数上加上 +S1。 对于每根穿过该价格的蜡烛,我们将在分数上添加-S2(负)。
这应该让您对如何为此分配分数有一个非常基本的想法。
现在您必须根据自己的要求对其进行调整。 我做了一些调整,大大提高了性能,如下所示:
不同类型切割的不同分数。如果蜡烛的主体穿过价格,那么分数变化是-S3,但蜡烛芯穿过价格,分数变化是-S4。这里 Abs(S3) > Abs(S4) 因为身体切割比灯芯切割更重要。
如果收盘价但无法交叉的蜡烛是高点(每边高于两根蜡烛)或低点(每边少于两根蜡烛),则添加比其他正常值更高的分数蜡烛在此附近收盘。
如果蜡烛在此附近收盘时为高点或低点,并且价格处于下降趋势或上升趋势(至少 y% 移动),则在该点上添加更高的分数。
李>您可以从初始列表中删除一些价格。我只考虑一个价格是它两侧 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 的一些示例。
【讨论】:
DIFF_PERC... 的差异约为 0.2% - NIFTY
这是一个查找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
【讨论】:
ltp = [11.11, 12.11, 13.11, 10.11] 和 n 是要扫描的条目数,n 可以是 len(ltp)
我发现获得 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()
【讨论】:
.fit_predict 是一个错误还是你没有分配它就这样运行它。
我简要阅读了 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] 对应的位置可能位于窗口内窗口。
【讨论】:
这是 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)
【讨论】:
我不确定它是否真的是“支撑和阻力”检测,但这个呢:
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%
因此,区间的长度越长,支撑/阻力区域就越重要。
(再次:不确定这是否可以归类为“支撑和阻力”)
【讨论】:
如果您正在寻找水平 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 的解决方案非常相似。
【讨论】:
对支撑位和阻力位的解释非常主观。很多人以不同的方式做这件事。 [...] 当我从图表中评估 S&R 时,我正在寻找两个主要的东西:
反弹 - 水平线需要有明显的偏离(反弹),这被认为定义了支撑或阻力水平。
多次触摸 - 单次触摸转折点不足以指示建立的支撑位或阻力位。应该存在到相同近似水平的多次触摸,以便可以通过这些转折点绘制一条水平线。
【讨论】: