添加新的识别

This commit is contained in:
jackyu66git
2025-11-19 10:35:13 +08:00
parent 76da4f4ef4
commit cde6ddb134
4 changed files with 406 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
//@version=6
indicator("Close FFT Spectrum", overlay=true, max_lines_count=500, max_labels_count=500, max_boxes_count=500)
int windowLen = input.int(64, "FFT窗口长度", minval=16, maxval=256, step=8)
bool normalizeSpectrum = input.bool(true, "幅值归一化(除以窗口长度)")
calcSpectrum(float[] samples, bool normalize) =>
int size = array.size(samples)
float[] mags = array.new_float()
if size == 0
mags
float norm = normalize ? float(size) : 1.0
int freqLimit = size / 2
if freqLimit < 1
freqLimit := 1
float twoPi = 2.0 * math.pi
for freq = 0 to freqLimit
float sumReal = 0.0
float sumImag = 0.0
for n = 0 to size - 1
float sample = array.get(samples, size - 1 - n)
float angle = twoPi * float(freq) * float(n) / float(size)
sumReal += sample * math.cos(angle)
sumImag -= sample * math.sin(angle)
float magnitude = math.sqrt(sumReal * sumReal + sumImag * sumImag) / norm
array.push(mags, magnitude)
mags
var float[] priceBuffer = array.new_float()
var int lastWindowSetting = na
if na(lastWindowSetting) or lastWindowSetting != windowLen
lastWindowSetting := windowLen
array.clear(priceBuffer)
if not na(close)
if array.size(priceBuffer) >= windowLen
array.shift(priceBuffer)
array.push(priceBuffer, close)
bool ready = array.size(priceBuffer) == windowLen
float[] spectrum = ready ? calcSpectrum(priceBuffer, normalizeSpectrum) : array.new_float()
int freqCount = array.size(spectrum)
float dcComponent = ready and freqCount > 0 ? array.get(spectrum, 0) : na
plot(dcComponent, title="直流分量", color=color.orange, linewidth=2)
+121
View File
@@ -0,0 +1,121 @@
//@version=6
indicator("Close FFT Spectrum Pane", overlay=false, max_lines_count=500, max_labels_count=500, max_boxes_count=500)
int windowLen = input.int(64, "FFT窗口长度", minval=16, maxval=256, step=8)
int freqIndexInput = input.int(1, "观察的频率序号 (0=直流)", minval=0, maxval=256)
bool normalizeSpectrum = input.bool(true, "幅值归一化(除以窗口长度)")
int smoothLen = input.int(1, "幅值平滑长度 (1 = 不平滑)", minval=1, maxval=20)
bool showTable = input.bool(true, "显示主频表格")
int topCount = input.int(5, "主频数量", minval=1, maxval=12)
calcSpectrum(float[] samples, bool normalize) =>
int size = array.size(samples)
float[] mags = array.new_float()
if size == 0
mags
float norm = normalize ? float(size) : 1.0
int freqLimit = size / 2
if freqLimit < 1
freqLimit := 1
float twoPi = 2.0 * math.pi
for freq = 0 to freqLimit
float sumReal = 0.0
float sumImag = 0.0
for n = 0 to size - 1
float sample = array.get(samples, size - 1 - n)
float angle = twoPi * float(freq) * float(n) / float(size)
sumReal += sample * math.cos(angle)
sumImag -= sample * math.sin(angle)
float magnitude = math.sqrt(sumReal * sumReal + sumImag * sumImag) / norm
array.push(mags, magnitude)
mags
getTopIndexes(float[] mags, int count) =>
int available = array.size(mags)
int limit = count < available ? count : available
int[] results = array.new_int()
if limit == 0
results
float[] scratch = array.copy(mags)
for rank = 0 to limit - 1
float bestVal = na
int bestIdx = -1
for i = 0 to array.size(scratch) - 1
float candidate = array.get(scratch, i)
if na(candidate)
continue
if na(bestVal) or candidate > bestVal
bestVal := candidate
bestIdx := i
if bestIdx == -1
break
array.push(results, bestIdx)
array.set(scratch, bestIdx, na)
results
var float[] priceBuffer = array.new_float()
var int lastWindowSetting = na
if na(lastWindowSetting) or lastWindowSetting != windowLen
lastWindowSetting := windowLen
array.clear(priceBuffer)
if not na(close)
if array.size(priceBuffer) >= windowLen
array.shift(priceBuffer)
array.push(priceBuffer, close)
bool ready = array.size(priceBuffer) == windowLen
float[] spectrum = ready ? calcSpectrum(priceBuffer, normalizeSpectrum) : array.new_float()
int freqCount = array.size(spectrum)
int clampedFreqIdx = freqCount > 0 ? math.min(freqIndexInput, freqCount - 1) : 0
float paneMagnitude = ready and freqCount > 0 ? array.get(spectrum, clampedFreqIdx) : na
float panePlotted = smoothLen > 1 ? ta.sma(paneMagnitude, smoothLen) : paneMagnitude
plot(panePlotted, title="频率幅值", color=color.blue, linewidth=2)
var table spectrumTable = na
var int lastTableRows = na
if showTable
if na(spectrumTable) or na(lastTableRows) or lastTableRows != topCount + 1
if not na(spectrumTable)
table.delete(spectrumTable)
spectrumTable := table.new(position.top_right, 3, topCount + 1, bgcolor=color.new(color.black, 80), frame_color=color.new(color.gray, 60))
lastTableRows := topCount + 1
else if not na(spectrumTable)
table.delete(spectrumTable)
spectrumTable := na
lastTableRows := na
if showTable and not na(spectrumTable)
color headerColor = color.new(color.white, 0)
color valueColor = color.new(color.aqua, 0)
color periodColor = color.new(color.silver, 0)
table.cell(spectrumTable, 0, 0, "频率k", text_color=headerColor, text_halign=text.align_center)
table.cell(spectrumTable, 1, 0, "幅值", text_color=headerColor, text_halign=text.align_center)
table.cell(spectrumTable, 2, 0, "周期(Bar)", text_color=headerColor, text_halign=text.align_center)
if ready and freqCount > 0
int[] leaders = getTopIndexes(spectrum, topCount)
int leaderCount = array.size(leaders)
for row = 0 to topCount - 1
int tableRow = row + 1
if row < leaderCount
int freqIdx = array.get(leaders, row)
float amp = array.get(spectrum, freqIdx)
float period = freqIdx == 0 ? na : float(windowLen) / float(freqIdx)
float rounded = na(period) ? na : math.round(period * 100.0) / 100.0
string freqText = str.tostring(freqIdx)
string ampText = str.tostring(amp, format.mintick)
string periodText = na(rounded) ? "∞" : str.tostring(rounded, format.mintick)
table.cell(spectrumTable, 0, tableRow, freqText, text_color=headerColor)
table.cell(spectrumTable, 1, tableRow, ampText, text_color=valueColor)
table.cell(spectrumTable, 2, tableRow, periodText, text_color=periodColor)
else
table.cell(spectrumTable, 0, tableRow, "-", text_color=headerColor)
table.cell(spectrumTable, 1, tableRow, "-", text_color=valueColor)
table.cell(spectrumTable, 2, tableRow, "-", text_color=periodColor)
else
for row = 1 to topCount
table.cell(spectrumTable, 0, row, "-", text_color=headerColor)
table.cell(spectrumTable, 1, row, "-", text_color=valueColor)
table.cell(spectrumTable, 2, row, "-", text_color=periodColor)
+26
View File
@@ -40,6 +40,19 @@ weightVolume = input.float(20, "成交量权重 %", minval=0, maxval=100,
weightRecency = input.float(10, "时效性权重 %", minval=0, maxval=100, group=strengthGroup) weightRecency = input.float(10, "时效性权重 %", minval=0, maxval=100, group=strengthGroup)
reactionBars = input.int(5, "反应检测K线数", minval=1, maxval=20, group=strengthGroup, tooltip="检测触碰后多少根K线的价格反应") reactionBars = input.int(5, "反应检测K线数", minval=1, maxval=20, group=strengthGroup, tooltip="检测触碰后多少根K线的价格反应")
// EMA设置
emaGroup = "EMA线"
showEMA = input.bool(true, "显示EMA", group=emaGroup)
ema6 = input.int(6, "EMA周期1", minval=1, group=emaGroup)
ema12 = input.int(12, "EMA周期2", minval=1, group=emaGroup)
ema24 = input.int(24, "EMA周期3", minval=1, group=emaGroup)
ema52 = input.int(52, "EMA周期4", minval=1, group=emaGroup)
emaWidth = input.int(2, "EMA线宽", minval=1, maxval=5, group=emaGroup)
color6 = input.color(#FF6B9D, "EMA6色", group=emaGroup)
color12 = input.color(#C44569, "EMA12色", group=emaGroup)
color24 = input.color(#FFA502, "EMA24色", group=emaGroup)
color52 = input.color(#26A69A, "EMA52色", group=emaGroup)
// 显示设置 // 显示设置
displayGroup = "显示设置" displayGroup = "显示设置"
maxVisibleLevels = input.int(12, "最多显示水平数", minval=1, maxval=50, group=displayGroup) maxVisibleLevels = input.int(12, "最多显示水平数", minval=1, maxval=50, group=displayGroup)
@@ -87,6 +100,12 @@ atrValue = ta.atr(atrLength)
clusterTolerance = atrValue * clusterTolATR clusterTolerance = atrValue * clusterTolATR
avgVolume = ta.sma(volume, 50) avgVolume = ta.sma(volume, 50)
// 计算EMA线
ema6Value = showEMA ? ta.ema(close, ema6) : na
ema12Value = showEMA ? ta.ema(close, ema12) : na
ema24Value = showEMA ? ta.ema(close, ema24) : na
ema52Value = showEMA ? ta.ema(close, ema52) : na
// ========== 辅助函数 ========== // // ========== 辅助函数 ========== //
// 标签大小转换 // 标签大小转换
@@ -460,6 +479,13 @@ if not na(pivotLow)
f_cleanup_old_levels() f_cleanup_old_levels()
f_draw_levels() f_draw_levels()
// 绘制EMA线
plot(showEMA ? ema6Value : na, "EMA6", color=color6, linewidth=emaWidth, style=plot.style_line)
plot(showEMA ? ema12Value : na, "EMA12", color=color12, linewidth=emaWidth, style=plot.style_line)
plot(showEMA ? ema24Value : na, "EMA24", color=color24, linewidth=emaWidth, style=plot.style_line)
plot(showEMA ? ema52Value : na, "EMA52", color=color52, linewidth=emaWidth, style=plot.style_line)
// 计算最近的支撑和压力 // 计算最近的支撑和压力
var float nearestSupport = na var float nearestSupport = na
var float nearestResistance = na var float nearestResistance = na
+213
View File
@@ -0,0 +1,213 @@
//@version=6
indicator(title="裸K趋势反转识别", shorttitle="Naked K Reversal", overlay=true, max_labels_count=500)
// ========================= 输入参数 =========================
// 基础设置
showLabels = input.bool(true, "显示信号标记")
showBackground = input.bool(true, "显示背景着色")
showAlerts = input.bool(true, "启用告警")
// 裸K形态参数
pinbarThreshold = input.float(0.6, "PinBar阈值(0.6-0.8)", minval=0.3, maxval=0.9, step=0.05)
insideBarThreshold = input.float(0.8, "内包线阈值(0.7-0.9)", minval=0.5, maxval=0.95, step=0.05)
engulfingThreshold = input.float(0.5, "吞没线阈值(0.4-0.7)", minval=0.3, maxval=0.8, step=0.05)
// 趋势确认参数
trendConfirmationBars = input.int(3, "趋势确认根数", minval=1, maxval=10)
reversalConfirmationBars = input.int(2, "反转确认根数", minval=1, maxval=5)
// 成交量确认
useVolumeConfirmation = input.bool(true, "使用成交量确认")
volumeMultiplier = input.float(1.2, "成交量倍数", minval=1.0, maxval=3.0, step=0.1)
// 灵敏度
sensitivity = input.float(1.0, "灵敏度", minval=0.5, maxval=2.0, step=0.1)
// 颜色设置
colBullish = color.new(color.green, 80)
colBearish = color.new(color.red, 80)
colNeutral = color.new(color.gray, 85)
// ========================= 裸K形态识别 =========================
// 1. PinBar (锤子线/上吊线)
barRange = high - low
bodyRange = math.abs(close - open)
upperShadow = high - math.max(open, close)
lowerShadow = math.min(open, close) - low
isPinBar = barRange > 0 and bodyRange / barRange < pinbarThreshold
isBullishPinBar = isPinBar and lowerShadow > upperShadow * 2
isBearishPinBar = isPinBar and upperShadow > lowerShadow * 2
// 2. 内包线 (Inside Bar)
isInsideBar = high <= high[1] and low >= low[1]
insideBarStrength = (high[1] - low[1]) > 0 ? (high - low) / (high[1] - low[1]) : 1
isStrongInsideBar = isInsideBar and insideBarStrength < insideBarThreshold
// 3. 吞没线 (Engulfing)
isBullishEngulfing = close > open and close[1] < open[1] and
close > open[1] and open < close[1] and
bodyRange > bodyRange[1] * engulfingThreshold
isBearishEngulfing = close < open and close[1] > open[1] and
close < open[1] and open > close[1] and
bodyRange > bodyRange[1] * engulfingThreshold
// 4. 孕线 (Harami)
isBullishHarami = close > open and close[1] < open[1] and
high < high[1] and low > low[1] and
bodyRange < bodyRange[1] * 0.7
isBearishHarami = close < open and close[1] > open[1] and
high < high[1] and low > low[1] and
bodyRange < bodyRange[1] * 0.7
// 5. 乌云盖顶 (Dark Cloud Cover)
isDarkCloud = close < open and close[1] > open[1] and
open > close[1] and close < (open[1] + close[1]) / 2
// 6. 刺透形态 (Piercing Pattern)
isPiercing = close > open and close[1] < open[1] and
open < low[1] and close > (open[1] + close[1]) / 2
// ========================= 趋势判断 =========================
// 简单移动平均线趋势判断
emaFast = ta.ema(close, 10)
emaSlow = ta.ema(close, 20)
isUptrend = emaFast > emaSlow and close > emaSlow
isDowntrend = emaFast < emaSlow and close < emaSlow
// 趋势强度
var int uptrendCount = 0
var int downtrendCount = 0
uptrendCount := isUptrend ? uptrendCount + 1 : 0
downtrendCount := isDowntrend ? downtrendCount + 1 : 0
uptrendConfirmed = uptrendCount >= trendConfirmationBars
downtrendConfirmed = downtrendCount >= trendConfirmationBars
// ========================= 成交量确认 =========================
volumeMa = ta.sma(volume, 20)
volumeConfirmed = not useVolumeConfirmation or volume > volumeMa * volumeMultiplier
// ========================= 反转信号生成 =========================
// 看涨反转信号
bullishReversalSignal = (isBullishPinBar or isBullishEngulfing or isBullishHarami or isPiercing) and
downtrendConfirmed and volumeConfirmed
// 看跌反转信号
bearishReversalSignal = (isBearishPinBar or isBearishEngulfing or isBearishHarami or isDarkCloud) and
uptrendConfirmed and volumeConfirmed
// 反转确认计数
var int bullishReversalCount = 0
var int bearishReversalCount = 0
bullishReversalCount := bullishReversalSignal ? bullishReversalCount + 1 : 0
bearishReversalCount := bearishReversalSignal ? bearishReversalCount + 1 : 0
bullishReversalConfirmed = bullishReversalCount >= reversalConfirmationBars
bearishReversalConfirmed = bearishReversalCount >= reversalConfirmationBars
// ========================= 状态机 =========================
// 0: 无趋势, 1: 上涨趋势, -1: 下跌趋势, 2: 反转中
var int marketState = 0
prevState = nz(marketState[1], 0)
// 状态转换逻辑
int newState = prevState
if prevState == 0 // 无趋势状态
if uptrendConfirmed
newState := 1
else if downtrendConfirmed
newState := -1
else if prevState == 1 // 上涨趋势
if bearishReversalConfirmed
newState := -1
else if not uptrendConfirmed
newState := 0
else if prevState == -1 // 下跌趋势
if bullishReversalConfirmed
newState := 1
else if not downtrendConfirmed
newState := 0
marketState := barstate.isconfirmed ? newState : nz(marketState[1], prevState)
// ========================= 可视化 =========================
// 背景着色
bgColor = marketState == 1 ? colBullish : marketState == -1 ? colBearish : colNeutral
bgcolor(showBackground ? bgColor : na, title="市场状态背景")
// 裸K形态标记
plotshape(showLabels and isBullishPinBar, title="看涨PinBar", style=shape.triangleup,
location=location.belowbar, color=color.green, size=size.small)
plotshape(showLabels and isBearishPinBar, title="看跌PinBar", style=shape.triangledown,
location=location.abovebar, color=color.red, size=size.small)
plotshape(showLabels and isBullishEngulfing, title="看涨吞没", style=shape.circle,
location=location.belowbar, color=color.lime, size=size.small)
plotshape(showLabels and isBearishEngulfing, title="看跌吞没", style=shape.circle,
location=location.abovebar, color=color.maroon, size=size.small)
plotshape(showLabels and isStrongInsideBar, title="内包线", style=shape.diamond,
location=location.top, color=color.orange, size=size.small)
// 反转信号标记
plotshape(showLabels and bullishReversalConfirmed, title="看涨反转确认",
style=shape.labelup, location=location.belowbar, color=color.green,
text="↑", textcolor=color.white, size=size.normal)
plotshape(showLabels and bearishReversalConfirmed, title="看跌反转确认",
style=shape.labeldown, location=location.abovebar, color=color.red,
text="↓", textcolor=color.white, size=size.normal)
// 趋势线
plot(emaFast, color=color.new(color.blue, 0), linewidth=1, title="EMA快线")
plot(emaSlow, color=color.new(color.orange, 0), linewidth=2, title="EMA慢线")
// ========================= 告警系统 =========================
alertcondition(showAlerts and bullishReversalConfirmed, title="看涨反转信号",
message="裸K看涨反转信号确认")
alertcondition(showAlerts and bearishReversalConfirmed, title="看跌反转信号",
message="裸K看跌反转信号确认")
// ========================= 信息显示 =========================
// 在图表上显示当前状态
var table infoTable = table.new(position.top_right, 2, 6, bgcolor=color.new(color.gray, 90),
border_width=1)
if barstate.islast
table.cell(infoTable, 0, 0, "裸K趋势反转识别", text_color=color.white,
bgcolor=color.new(color.blue, 70))
table.cell(infoTable, 1, 0, "状态", text_color=color.white,
bgcolor=color.new(color.blue, 70))
table.cell(infoTable, 0, 1, "市场状态")
table.cell(infoTable, 1, 1, marketState == 1 ? "上涨趋势" : marketState == -1 ? "下跌趋势" : "震荡",
text_color=marketState == 1 ? color.green : marketState == -1 ? color.red : color.gray)
table.cell(infoTable, 0, 2, "趋势强度")
table.cell(infoTable, 1, 2, str.tostring(math.max(uptrendCount, downtrendCount)))
table.cell(infoTable, 0, 3, "看涨信号")
table.cell(infoTable, 1, 3, bullishReversalConfirmed ? "确认" : "等待",
text_color=bullishReversalConfirmed ? color.green : color.gray)
table.cell(infoTable, 0, 4, "看跌信号")
table.cell(infoTable, 1, 4, bearishReversalConfirmed ? "确认" : "等待",
text_color=bearishReversalConfirmed ? color.red : color.gray)
table.cell(infoTable, 0, 5, "成交量确认")
table.cell(infoTable, 1, 5, volumeConfirmed ? "是" : "否",
text_color=volumeConfirmed ? color.green : color.orange)
// ========================= 使用说明 =========================
// 本脚本专注于裸K形态识别趋势反转信号
// 主要识别形态:PinBar、吞没线、内包线、孕线、乌云盖顶、刺透形态
// 结合趋势确认和成交量验证,提高信号可靠性