算法流程
- 动量:对连续几日的最高最低值取平均值,然后与 sma 取平均值
- 取离合程度:用 close 减去这个值
- 拟合这个值:取线性回归(或ma)
pine 源码
最后取线性回归
study(shorttitle = "SQZMOM_LB", title="Squeeze Momentum Indicator", overlay=false)
length = input(20, title="BB Length")
mult = input(2.0,title="BB MultFactor")
lengthKC=input(20, title="KC Length")
multKC = input(1.5, title="KC MultFactor")
useTrueRange = input(true, title="Use TrueRange (KC)", type=bool)
// Calculate BB
source = close
basis = sma(source, length)
dev = multKC * stdev(source, length)
upperBB = basis + dev
lowerBB = basis - dev
// Calculate KC
ma = sma(source, lengthKC)
range = useTrueRange ? tr : (high - low)
rangema = sma(range, lengthKC)
upperKC = ma + rangema * multKC
lowerKC = ma - rangema * multKC
sqzOn = (lowerBB > lowerKC) and (upperBB < upperKC)
sqzOff = (lowerBB < lowerKC) and (upperBB > upperKC)
noSqz = (sqzOn == false) and (sqzOff == false)
val = linreg(source - avg(avg(highest(high, lengthKC), lowest(low, lengthKC)),sma(close,lengthKC)),
lengthKC,0)
bcolor = iff( val > 0,
iff( val > nz(val[1]), lime, green),
iff( val < nz(val[1]), red, maroon))
scolor = noSqz ? blue : sqzOn ? black : gray
plot(val, color=bcolor, style=histogram, linewidth=4)
plot(0, color=scolor, style=cross, linewidth=2)
python 源码
最后取 sma
class SqueezeMomentumIndicator(object):
_length = 20
_mult = 2.0
_lengthKC = 20
_lengthVal = 3
_multKC = 1.5
_useTrueRange = True
def __init__(self, length, mult, lengthKC, lengthVal, multKC, useTrueRange, df):
self._length = length
self._mult = mult
self._lengthKC = lengthKC
self._lengthVal = lengthVal
self._multKC = multKC
self._useTrueRange = useTrueRange
self._df = df
def index(self):
df = self._df.copy()
df['source'] = df['Close']
df['sma'] = df['source'].rolling(window=self._length).mean()
df['basis'] = df['sma'].fillna(value=0)
df['stdev'] = df['source'].rolling(window=self._length).std()
df['dev'] = self._multKC * df['stdev']
df['upperBB'] = df['basis'] + df['dev']
df['lowerBB'] = df['basis'] - df['dev']
df['ma'] = df['source'].rolling(window=self._lengthKC).mean()
df['DailyRange'] = df['High'] - df['Low']
df['HC'] = df['High'] - df['Close'].shift(1)
df['CL'] = df['Close'].shift(1) - df['Low']
df['HC'] = df['HC'].abs()
df['CL'] = df['CL'].abs()
df['tr'] = df[['DailyRange', 'HC', 'CL']].max(axis=1)
df['range'] = df['tr'] if self._useTrueRange else df['DailyRange']
df['rangema'] = df['range'].rolling(window=self._lengthKC).mean()
df['upperKC'] = df['ma'] + df['rangema'] * self._multKC
df['lowerKC'] = df['ma'] - df['rangema'] * self._multKC
df['sqzOn'] = df.apply(
lambda item: item["lowerBB"] > item["lowerKC"] and item["upperBB"] < item["upperKC"], axis=1)
df['sqzOff'] = df.apply(
lambda item: item["lowerBB"] < item["lowerKC"] and item["upperBB"] > item["upperKC"], axis=1)
df['highest'] = df['High'].rolling(window=self._lengthKC).max()
df['lowest'] = df['Low'].rolling(window=self._lengthKC).min()
df['avg_hl'] = df.apply(lambda item: (item['highest'] + item['lowest']) / 2, axis=1)
df['sma_kc'] = df['Close'].rolling(window=self._lengthKC).mean()
df['avg_hl_c'] = df.apply(lambda item: (item['avg_hl'] + item['sma_kc']) / 2, axis=1)
df['s_avg_hl_c'] = df['source'] - df['avg_hl_c']
df['val'] = df['s_avg_hl_c'].rolling(window=self._lengthVal).mean()
df['val_1'] = df['val'].shift(1)
df['val'] = df['val'].fillna(value=0)
df['val_1'] = df['val_1'].fillna(value=0)
df['color'] = df.apply(lambda item: 'lime' if item['val'] > 0 and item['val'] > item['val_1']
else ('green' if item['val'] > 0 else ('red' if item['val'] < item['val_1'] else 'maroon')), axis=1)
self._df['val'] = df['val']
self._df['color'] = df['color']
return self._df