添加新的indicator
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This is a collection of TradingView Pine Script (v5/v6) indicators and strategies for technical analysis. All scripts are standalone `.pine` files with no build system, package manager, or dependencies — they are pasted directly into the TradingView Pine Editor.
|
||||||
|
|
||||||
|
## Working with Pine Script
|
||||||
|
|
||||||
|
- There is no local build, lint, or test toolchain. Pine Script can only be validated by pasting into the TradingView web editor.
|
||||||
|
- All files use `//@version=6` (or occasionally v5). Pine Script v6 is the current target.
|
||||||
|
- `indicator()` declares an indicator (overlay on chart), `strategy()` declares a backtestable strategy with order execution.
|
||||||
|
- Common overlay settings: `overlay=true`, `max_lines_count=500`, `max_labels_count=500`, `max_boxes_count=500`, `max_bars_back=5000`.
|
||||||
|
|
||||||
|
## File Organization by Category
|
||||||
|
|
||||||
|
- **缠论 (Chan Theory)**: `chan_theory*.pine` — Classic Chinese technical theory implementing 分型 (fractals), 笔 (strokes), 线段 (segments), 中枢 (pivot zones). The `chan_theory_complete.pine` is the most comprehensive version (~62KB), combining both Chan Theory and trend identification. Earlier files are iterative improvements.
|
||||||
|
- **EMA Strategies**: `ema_strategy_4lines.pine`, `ema_macd_trend_indicator.pine`, `ema_macd_trend_strategy.pine`, `ema26_ema52.pine`, `ema26_ema52_diff.pine` — EMA cross/alignment-based trading strategies with MACD confirmation.
|
||||||
|
- **Multi-Timeframe EMA52**: `多周期EMA52_Pro.pine`, `多周期MA52.pine` — Draws EMA/MA lines from higher/lower timeframes onto the current chart. `_Pro` is the latest and most feature-rich.
|
||||||
|
- **支撑压力位 (Support/Resistance)**: `支撑压力位*.pine` — Various support/resistance detection indicators (DeepSeek, QwenMax, optimized versions).
|
||||||
|
- **趋势判断 (Trend Detection)**: `趋势行情判断*.pine` — Trend condition assessment with optimized/super-optimized variants.
|
||||||
|
- **背驰/背离 (Divergence)**: `macd_divergence.pine`, `high_macd_divergence.pine`, `kdt_divergence.pine`, `last_divergence_simple.pine` — MACD and K-line momentum theory divergence detection.
|
||||||
|
- **Bollinger Bands**: `bollinger_atr_strategy.pine`, `bollinger_band_strategy_new.pine`.
|
||||||
|
- **Other**: `裸K趋势反转识别.pine` (naked candlestick pattern reversal), `close_fft_spectrum.pine` (FFT spectrum analysis), `三重滤网均线交易策略.pine` (triple-screen MA strategy), `自定义MACD柱子面积.pine` (custom MACD histogram area).
|
||||||
|
|
||||||
|
## Code Patterns
|
||||||
|
|
||||||
|
- All user-configurable parameters use `input.*()` grouped with the `group=` parameter (e.g., `group="显示设置"`).
|
||||||
|
- Display toggles follow the pattern: `showX = input.bool(true, "显示X", group="显示设置")` with conditional plotting via `plot(showX ? value : na, ...)`.
|
||||||
|
- Color configuration uses `input.color()` for user-customizable colors.
|
||||||
|
- Chinese is used for parameter labels, tooltips, and comments. Variable names are a mix of English and pinyin (e.g., `show_fenxing`, `color_bi_up`).
|
||||||
|
- `var` prefix is used for persistent state variables (e.g., `var bool cycle_in_progress = false`).
|
||||||
|
- Many scripts are iterative refinements — when making changes, prefer editing the latest/most complete version (look for `_Pro`, `_complete`, `_超级优化版`, `_优化版` suffixes).
|
||||||
|
|
||||||
|
## Multi-File Relationship
|
||||||
|
|
||||||
|
Files in the same category are typically evolutionary versions (simple → optimized → super-optimized → complete). When asked to fix or improve an indicator, identify the latest version first. The README.md and various `*.md` files contain usage instructions and comparison guides for different versions.
|
||||||
@@ -0,0 +1,696 @@
|
|||||||
|
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
|
||||||
|
// © 缠论核心版 - 从 chan_theory_complete 提取:K线包含处理、分型、笔、线段、中枢 + EMA均线趋势
|
||||||
|
|
||||||
|
//@version=6
|
||||||
|
indicator("缠论核心版", shorttitle="缠论核心", overlay=true, max_lines_count=500, max_labels_count=500, max_boxes_count=500, max_bars_back=5000)
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 参数设置
|
||||||
|
// ============================================================================
|
||||||
|
show_merged_k = input.bool(true, "显示处理后K线", group="K线显示")
|
||||||
|
show_fenxing = input.bool(true, "显示分型", group="分型显示")
|
||||||
|
show_bi = input.bool(true, "显示笔", group="笔显示")
|
||||||
|
show_bi_zhongshu = input.bool(true, "显示笔中枢", group="笔显示")
|
||||||
|
show_xianduan = input.bool(true, "显示线段", group="线段显示")
|
||||||
|
show_xd_zhongshu = input.bool(true, "显示线段中枢", group="线段显示")
|
||||||
|
show_ema24 = input.bool(true, "显示EMA24", group="均线显示")
|
||||||
|
show_ema52 = input.bool(true, "显示EMA52", group="均线显示")
|
||||||
|
show_ema104 = input.bool(true, "显示EMA104", group="均线显示")
|
||||||
|
show_ema156 = input.bool(true, "显示EMA156", group="均线显示")
|
||||||
|
show_ema_trend = input.bool(true, "显示均线趋势", group="均线显示")
|
||||||
|
|
||||||
|
min_bi_bars = input.int(4, "笔最小K线数", minval=3, maxval=10, group="缠论参数")
|
||||||
|
use_macd_filter = input.bool(false, "MACD过滤分型", tooltip="顶分型要求MACD>0,底分型要求MACD<0", group="缠论参数")
|
||||||
|
|
||||||
|
// EMA参数设置
|
||||||
|
ema1_len = input.int(24, "EMA1周期", minval=5, maxval=100, group="均线参数", tooltip="最快均线,默认24")
|
||||||
|
ema2_len = input.int(52, "EMA2周期", minval=10, maxval=200, group="均线参数", tooltip="次快均线,默认52")
|
||||||
|
ema3_len = input.int(104, "EMA3周期", minval=20, maxval=300, group="均线参数", tooltip="次慢均线,默认104")
|
||||||
|
ema4_len = input.int(156, "EMA4周期", minval=30, maxval=500, group="均线参数", tooltip="EMA156仅用于显示,不参与三均线排列/交叉")
|
||||||
|
trend_confirm_bars = input.int(3, "趋势确认K线数", minval=1, maxval=20, group="均线参数", tooltip="排列持续N根K线才确认趋势,增大可减少震荡假信号")
|
||||||
|
|
||||||
|
color_bi_up = input.color(color.red, "上升笔颜色", group="笔颜色")
|
||||||
|
color_bi_down = input.color(color.green, "下降笔颜色", group="笔颜色")
|
||||||
|
color_bi_zs = input.color(color.new(color.yellow, 80), "笔中枢颜色", group="笔颜色")
|
||||||
|
color_xd = input.color(color.blue, "线段颜色", group="线段颜色")
|
||||||
|
color_xd_zs = input.color(color.new(color.orange, 70), "线段中枢颜色", group="线段颜色")
|
||||||
|
color_ema24 = input.color(color.yellow, "EMA24颜色", group="均线颜色")
|
||||||
|
color_ema52 = input.color(color.orange, "EMA52颜色", group="均线颜色")
|
||||||
|
color_ema104 = input.color(color.blue, "EMA104颜色", group="均线颜色")
|
||||||
|
color_ema156 = input.color(color.purple, "EMA156颜色", group="均线颜色")
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// MACD计算
|
||||||
|
// ============================================================================
|
||||||
|
[macd_line, signal_line, macd_hist] = ta.macd(close, 12, 26, 9)
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// EMA均线计算
|
||||||
|
// ============================================================================
|
||||||
|
ema24 = ta.ema(close, ema1_len)
|
||||||
|
ema52 = ta.ema(close, ema2_len)
|
||||||
|
ema104 = ta.ema(close, ema3_len)
|
||||||
|
ema156 = ta.ema(close, ema4_len)
|
||||||
|
|
||||||
|
// 绘制EMA均线
|
||||||
|
plot(show_ema24 ? ema24 : na, "EMA24", color=color_ema24, linewidth=2)
|
||||||
|
plot(show_ema52 ? ema52 : na, "EMA52", color=color_ema52, linewidth=2)
|
||||||
|
plot(show_ema104 ? ema104 : na, "EMA104", color=color_ema104, linewidth=2)
|
||||||
|
plot(show_ema156 ? ema156 : na, "EMA156", color=color_ema156, linewidth=2)
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 三均线趋势排列检测
|
||||||
|
// ============================================================================
|
||||||
|
is_bullish_aligned = ema24 > ema52 and ema52 > ema104
|
||||||
|
is_bearish_aligned = ema24 < ema52 and ema52 < ema104
|
||||||
|
is_in_oscillation = not is_bullish_aligned and not is_bearish_aligned
|
||||||
|
|
||||||
|
ema_golden_cross = ta.crossover(ema52, ema104)
|
||||||
|
ema_death_cross = ta.crossunder(ema52, ema104)
|
||||||
|
|
||||||
|
plotshape(show_ema52 and show_ema104 and ema_golden_cross and not is_in_oscillation, title="EMA金叉",
|
||||||
|
style=shape.triangleup, location=location.belowbar,
|
||||||
|
color=color.lime, size=size.small, text="金叉")
|
||||||
|
plotshape(show_ema52 and show_ema104 and ema_death_cross and not is_in_oscillation, title="EMA死叉",
|
||||||
|
style=shape.triangledown, location=location.abovebar,
|
||||||
|
color=color.red, size=size.small, text="死叉")
|
||||||
|
|
||||||
|
var int bullish_bars = 0
|
||||||
|
var int bearish_bars = 0
|
||||||
|
|
||||||
|
if is_bullish_aligned
|
||||||
|
bullish_bars := bullish_bars + 1
|
||||||
|
else
|
||||||
|
bullish_bars := 0
|
||||||
|
|
||||||
|
if is_bearish_aligned
|
||||||
|
bearish_bars := bearish_bars + 1
|
||||||
|
else
|
||||||
|
bearish_bars := 0
|
||||||
|
|
||||||
|
is_bullish_confirmed = bullish_bars >= trend_confirm_bars
|
||||||
|
is_bearish_confirmed = bearish_bars >= trend_confirm_bars
|
||||||
|
|
||||||
|
bgcolor(show_ema_trend and is_bullish_confirmed and not is_in_oscillation ? color.new(color.lime, 90) : na, title="多头背景")
|
||||||
|
bgcolor(show_ema_trend and is_bearish_confirmed and not is_in_oscillation ? color.new(color.red, 90) : na, title="空头背景")
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// K线包含处理(缠论标准)
|
||||||
|
// ============================================================================
|
||||||
|
var float[] mk_high = array.new<float>()
|
||||||
|
var float[] mk_low = array.new<float>()
|
||||||
|
var int[] mk_idx = array.new<int>()
|
||||||
|
var int[] mk_start_idx = array.new<int>()
|
||||||
|
var float[] mk_macd = array.new<float>()
|
||||||
|
|
||||||
|
var int mk_trend = 0
|
||||||
|
|
||||||
|
is_contain(h1, l1, h2, l2) =>
|
||||||
|
(h1 >= h2 and l1 <= l2) or (h2 >= h1 and l2 <= l1)
|
||||||
|
|
||||||
|
mk_size = array.size(mk_high)
|
||||||
|
|
||||||
|
if mk_size == 0
|
||||||
|
array.push(mk_high, high)
|
||||||
|
array.push(mk_low, low)
|
||||||
|
array.push(mk_idx, bar_index)
|
||||||
|
array.push(mk_start_idx, bar_index)
|
||||||
|
array.push(mk_macd, macd_hist)
|
||||||
|
else
|
||||||
|
prev_h = array.get(mk_high, mk_size - 1)
|
||||||
|
prev_l = array.get(mk_low, mk_size - 1)
|
||||||
|
prev_start = array.get(mk_start_idx, mk_size - 1)
|
||||||
|
|
||||||
|
if is_contain(prev_h, prev_l, high, low)
|
||||||
|
if mk_size >= 2
|
||||||
|
prev2_h = array.get(mk_high, mk_size - 2)
|
||||||
|
prev2_l = array.get(mk_low, mk_size - 2)
|
||||||
|
mk_trend := prev_h > prev2_h ? 1 : (prev_l < prev2_l ? -1 : mk_trend)
|
||||||
|
|
||||||
|
new_h = mk_trend >= 0 ? math.max(prev_h, high) : math.min(prev_h, high)
|
||||||
|
new_l = mk_trend >= 0 ? math.max(prev_l, low) : math.min(prev_l, low)
|
||||||
|
|
||||||
|
array.set(mk_high, mk_size - 1, new_h)
|
||||||
|
array.set(mk_low, mk_size - 1, new_l)
|
||||||
|
array.set(mk_idx, mk_size - 1, bar_index)
|
||||||
|
array.set(mk_macd, mk_size - 1, macd_hist)
|
||||||
|
else
|
||||||
|
mk_trend := high > prev_h ? 1 : -1
|
||||||
|
array.push(mk_high, high)
|
||||||
|
array.push(mk_low, low)
|
||||||
|
array.push(mk_idx, bar_index)
|
||||||
|
array.push(mk_start_idx, bar_index)
|
||||||
|
array.push(mk_macd, macd_hist)
|
||||||
|
|
||||||
|
if array.size(mk_high) > 5000
|
||||||
|
array.shift(mk_high)
|
||||||
|
array.shift(mk_low)
|
||||||
|
array.shift(mk_idx)
|
||||||
|
array.shift(mk_start_idx)
|
||||||
|
array.shift(mk_macd)
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 基于处理后K线的分型识别
|
||||||
|
// ============================================================================
|
||||||
|
var int[] fx_bars = array.new<int>()
|
||||||
|
var float[] fx_prices = array.new<float>()
|
||||||
|
var int[] fx_types = array.new<int>()
|
||||||
|
var float[] fx_macds = array.new<float>()
|
||||||
|
|
||||||
|
curr_mk_size = array.size(mk_high)
|
||||||
|
|
||||||
|
if curr_mk_size >= 3
|
||||||
|
h1 = array.get(mk_high, curr_mk_size - 3)
|
||||||
|
h2 = array.get(mk_high, curr_mk_size - 2)
|
||||||
|
h3 = array.get(mk_high, curr_mk_size - 1)
|
||||||
|
l1 = array.get(mk_low, curr_mk_size - 3)
|
||||||
|
l2 = array.get(mk_low, curr_mk_size - 2)
|
||||||
|
l3 = array.get(mk_low, curr_mk_size - 1)
|
||||||
|
idx2 = array.get(mk_idx, curr_mk_size - 2)
|
||||||
|
|
||||||
|
is_top = h2 > h1 and h2 > h3
|
||||||
|
is_bottom = l2 < l1 and l2 < l3
|
||||||
|
|
||||||
|
macd_at_fx = array.get(mk_macd, curr_mk_size - 2)
|
||||||
|
macd_ok = not use_macd_filter or (is_top and macd_at_fx > 0) or (is_bottom and macd_at_fx < 0)
|
||||||
|
|
||||||
|
if (is_top or is_bottom) and macd_ok
|
||||||
|
fx_arr_size = array.size(fx_bars)
|
||||||
|
fx_type = is_top ? 1 : -1
|
||||||
|
fx_price = is_top ? h2 : l2
|
||||||
|
should_add = true
|
||||||
|
|
||||||
|
if fx_arr_size > 0
|
||||||
|
last_type = array.get(fx_types, fx_arr_size - 1)
|
||||||
|
last_bar = array.get(fx_bars, fx_arr_size - 1)
|
||||||
|
last_price = array.get(fx_prices, fx_arr_size - 1)
|
||||||
|
|
||||||
|
if last_type == fx_type
|
||||||
|
if is_top and fx_price > last_price and (not use_macd_filter or macd_at_fx > 0)
|
||||||
|
array.set(fx_bars, fx_arr_size - 1, idx2)
|
||||||
|
array.set(fx_prices, fx_arr_size - 1, fx_price)
|
||||||
|
array.set(fx_macds, fx_arr_size - 1, macd_at_fx)
|
||||||
|
else if is_bottom and fx_price < last_price and (not use_macd_filter or macd_at_fx < 0)
|
||||||
|
array.set(fx_bars, fx_arr_size - 1, idx2)
|
||||||
|
array.set(fx_prices, fx_arr_size - 1, fx_price)
|
||||||
|
array.set(fx_macds, fx_arr_size - 1, macd_at_fx)
|
||||||
|
should_add := false
|
||||||
|
else
|
||||||
|
if idx2 - last_bar < min_bi_bars
|
||||||
|
should_add := false
|
||||||
|
if fx_type == 1 and fx_price <= last_price
|
||||||
|
should_add := false
|
||||||
|
if fx_type == -1 and fx_price >= last_price
|
||||||
|
should_add := false
|
||||||
|
|
||||||
|
if should_add
|
||||||
|
array.push(fx_bars, idx2)
|
||||||
|
array.push(fx_prices, fx_price)
|
||||||
|
array.push(fx_types, fx_type)
|
||||||
|
array.push(fx_macds, macd_at_fx)
|
||||||
|
|
||||||
|
if array.size(fx_bars) > 2000
|
||||||
|
array.shift(fx_bars)
|
||||||
|
array.shift(fx_prices)
|
||||||
|
array.shift(fx_types)
|
||||||
|
array.shift(fx_macds)
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 绘制
|
||||||
|
// ============================================================================
|
||||||
|
if barstate.islast
|
||||||
|
fx_count = array.size(fx_bars)
|
||||||
|
merged_k_count = array.size(mk_high)
|
||||||
|
|
||||||
|
// ==================== 绘制处理后的K线 ====================
|
||||||
|
if show_merged_k and merged_k_count > 0
|
||||||
|
for i = 0 to merged_k_count - 1
|
||||||
|
m_start = array.get(mk_start_idx, i)
|
||||||
|
m_end = array.get(mk_idx, i)
|
||||||
|
m_h = array.get(mk_high, i)
|
||||||
|
m_l = array.get(mk_low, i)
|
||||||
|
|
||||||
|
if bar_index - m_end <= 5000
|
||||||
|
box.new(m_start, m_h, m_end, m_l, border_color=color.purple, bgcolor=color.new(color.purple, 90), border_width=1)
|
||||||
|
|
||||||
|
// ==================== 绘制分型 ====================
|
||||||
|
if show_fenxing and fx_count > 0
|
||||||
|
for i = 0 to fx_count - 1
|
||||||
|
fx_bar = array.get(fx_bars, i)
|
||||||
|
fx_price = array.get(fx_prices, i)
|
||||||
|
fx_type = array.get(fx_types, i)
|
||||||
|
|
||||||
|
if bar_index - fx_bar <= 5000
|
||||||
|
if fx_type == 1
|
||||||
|
label.new(fx_bar, fx_price, "T",
|
||||||
|
color=color.new(color.red, 100),
|
||||||
|
style=label.style_none,
|
||||||
|
textcolor=color.red,
|
||||||
|
size=size.normal)
|
||||||
|
else
|
||||||
|
label.new(fx_bar, fx_price, "B",
|
||||||
|
color=color.new(color.green, 100),
|
||||||
|
style=label.style_none,
|
||||||
|
textcolor=color.green,
|
||||||
|
size=size.normal)
|
||||||
|
|
||||||
|
// ==================== 构建笔 ====================
|
||||||
|
var int[] bi_start_bars = array.new<int>()
|
||||||
|
var float[] bi_start_prices = array.new<float>()
|
||||||
|
var int[] bi_end_bars = array.new<int>()
|
||||||
|
var float[] bi_end_prices = array.new<float>()
|
||||||
|
var int[] bi_dirs = array.new<int>()
|
||||||
|
|
||||||
|
array.clear(bi_start_bars)
|
||||||
|
array.clear(bi_start_prices)
|
||||||
|
array.clear(bi_end_bars)
|
||||||
|
array.clear(bi_end_prices)
|
||||||
|
array.clear(bi_dirs)
|
||||||
|
|
||||||
|
if fx_count >= 2
|
||||||
|
for i = 0 to fx_count - 2
|
||||||
|
fx1_bar = array.get(fx_bars, i)
|
||||||
|
fx1_price = array.get(fx_prices, i)
|
||||||
|
fx1_type = array.get(fx_types, i)
|
||||||
|
fx2_bar = array.get(fx_bars, i + 1)
|
||||||
|
fx2_price = array.get(fx_prices, i + 1)
|
||||||
|
fx2_type = array.get(fx_types, i + 1)
|
||||||
|
|
||||||
|
if fx1_type != fx2_type
|
||||||
|
bi_dir = fx1_type == -1 ? 1 : -1
|
||||||
|
array.push(bi_start_bars, fx1_bar)
|
||||||
|
array.push(bi_start_prices, fx1_price)
|
||||||
|
array.push(bi_end_bars, fx2_bar)
|
||||||
|
array.push(bi_end_prices, fx2_price)
|
||||||
|
array.push(bi_dirs, bi_dir)
|
||||||
|
|
||||||
|
bi_count = array.size(bi_start_bars)
|
||||||
|
|
||||||
|
// ==================== 绘制笔 ====================
|
||||||
|
if show_bi and bi_count > 0
|
||||||
|
for i = 0 to bi_count - 1
|
||||||
|
s_bar = array.get(bi_start_bars, i)
|
||||||
|
s_price = array.get(bi_start_prices, i)
|
||||||
|
e_bar = array.get(bi_end_bars, i)
|
||||||
|
e_price = array.get(bi_end_prices, i)
|
||||||
|
bi_dir = array.get(bi_dirs, i)
|
||||||
|
|
||||||
|
if bar_index - s_bar <= 5000
|
||||||
|
bi_color = bi_dir == 1 ? color_bi_up : color_bi_down
|
||||||
|
line.new(s_bar, s_price, e_bar, e_price, color=bi_color, width=2)
|
||||||
|
|
||||||
|
// ==================== 构建线段(标准特征序列法) ====================
|
||||||
|
var int[] xd_start_bars = array.new<int>()
|
||||||
|
var float[] xd_start_prices = array.new<float>()
|
||||||
|
var int[] xd_end_bars = array.new<int>()
|
||||||
|
var float[] xd_end_prices = array.new<float>()
|
||||||
|
var int[] xd_dirs = array.new<int>()
|
||||||
|
|
||||||
|
array.clear(xd_start_bars)
|
||||||
|
array.clear(xd_start_prices)
|
||||||
|
array.clear(xd_end_bars)
|
||||||
|
array.clear(xd_end_prices)
|
||||||
|
array.clear(xd_dirs)
|
||||||
|
|
||||||
|
if bi_count >= 3
|
||||||
|
var float[] feat_raw_highs = array.new<float>()
|
||||||
|
var float[] feat_raw_lows = array.new<float>()
|
||||||
|
var int[] feat_raw_end_bars = array.new<int>()
|
||||||
|
var float[] feat_raw_end_prices = array.new<float>()
|
||||||
|
array.clear(feat_raw_highs)
|
||||||
|
array.clear(feat_raw_lows)
|
||||||
|
array.clear(feat_raw_end_bars)
|
||||||
|
array.clear(feat_raw_end_prices)
|
||||||
|
|
||||||
|
var float[] feat_merged_highs = array.new<float>()
|
||||||
|
var float[] feat_merged_lows = array.new<float>()
|
||||||
|
var int[] feat_merged_end_bars = array.new<int>()
|
||||||
|
var float[] feat_merged_end_prices = array.new<float>()
|
||||||
|
array.clear(feat_merged_highs)
|
||||||
|
array.clear(feat_merged_lows)
|
||||||
|
array.clear(feat_merged_end_bars)
|
||||||
|
array.clear(feat_merged_end_prices)
|
||||||
|
|
||||||
|
xd_start_bar = array.get(bi_start_bars, 0)
|
||||||
|
xd_start_price = array.get(bi_start_prices, 0)
|
||||||
|
xd_dir = array.get(bi_dirs, 0)
|
||||||
|
xd_bi_count = 1
|
||||||
|
|
||||||
|
xd_high = math.max(array.get(bi_start_prices, 0), array.get(bi_end_prices, 0))
|
||||||
|
xd_low = math.min(array.get(bi_start_prices, 0), array.get(bi_end_prices, 0))
|
||||||
|
xd_high_bar = xd_dir == 1 ? array.get(bi_end_bars, 0) : array.get(bi_start_bars, 0)
|
||||||
|
xd_low_bar = xd_dir == 1 ? array.get(bi_start_bars, 0) : array.get(bi_end_bars, 0)
|
||||||
|
|
||||||
|
i = 1
|
||||||
|
while i < bi_count
|
||||||
|
curr_bi_dir = array.get(bi_dirs, i)
|
||||||
|
curr_bi_start_bar = array.get(bi_start_bars, i)
|
||||||
|
curr_bi_start_price = array.get(bi_start_prices, i)
|
||||||
|
curr_bi_end_bar = array.get(bi_end_bars, i)
|
||||||
|
curr_bi_end_price = array.get(bi_end_prices, i)
|
||||||
|
|
||||||
|
curr_bi_high = math.max(curr_bi_start_price, curr_bi_end_price)
|
||||||
|
curr_bi_low = math.min(curr_bi_start_price, curr_bi_end_price)
|
||||||
|
|
||||||
|
xd_bi_count += 1
|
||||||
|
|
||||||
|
if curr_bi_high > xd_high
|
||||||
|
xd_high := curr_bi_high
|
||||||
|
xd_high_bar := curr_bi_dir == 1 ? curr_bi_end_bar : curr_bi_start_bar
|
||||||
|
if curr_bi_low < xd_low
|
||||||
|
xd_low := curr_bi_low
|
||||||
|
xd_low_bar := curr_bi_dir == -1 ? curr_bi_end_bar : curr_bi_start_bar
|
||||||
|
|
||||||
|
if curr_bi_dir != xd_dir
|
||||||
|
array.push(feat_raw_highs, curr_bi_high)
|
||||||
|
array.push(feat_raw_lows, curr_bi_low)
|
||||||
|
array.push(feat_raw_end_bars, curr_bi_end_bar)
|
||||||
|
array.push(feat_raw_end_prices, curr_bi_end_price)
|
||||||
|
|
||||||
|
// === 特征序列包含处理 ===
|
||||||
|
fm_size = array.size(feat_merged_highs)
|
||||||
|
if fm_size == 0
|
||||||
|
array.push(feat_merged_highs, curr_bi_high)
|
||||||
|
array.push(feat_merged_lows, curr_bi_low)
|
||||||
|
array.push(feat_merged_end_bars, curr_bi_end_bar)
|
||||||
|
array.push(feat_merged_end_prices, curr_bi_end_price)
|
||||||
|
else
|
||||||
|
fm_prev_h = array.get(feat_merged_highs, fm_size - 1)
|
||||||
|
fm_prev_l = array.get(feat_merged_lows, fm_size - 1)
|
||||||
|
|
||||||
|
has_contain = (fm_prev_h >= curr_bi_high and fm_prev_l <= curr_bi_low) or (curr_bi_high >= fm_prev_h and curr_bi_low <= fm_prev_l)
|
||||||
|
|
||||||
|
if has_contain
|
||||||
|
fm_trend = 0
|
||||||
|
if fm_size >= 2
|
||||||
|
fm_prev2_h = array.get(feat_merged_highs, fm_size - 2)
|
||||||
|
fm_prev2_l = array.get(feat_merged_lows, fm_size - 2)
|
||||||
|
fm_trend := fm_prev_h > fm_prev2_h ? 1 : (fm_prev_l < fm_prev2_l ? -1 : 0)
|
||||||
|
|
||||||
|
if fm_trend >= 0
|
||||||
|
array.set(feat_merged_highs, fm_size - 1, math.max(fm_prev_h, curr_bi_high))
|
||||||
|
array.set(feat_merged_lows, fm_size - 1, math.max(fm_prev_l, curr_bi_low))
|
||||||
|
else
|
||||||
|
array.set(feat_merged_highs, fm_size - 1, math.min(fm_prev_h, curr_bi_high))
|
||||||
|
array.set(feat_merged_lows, fm_size - 1, math.min(fm_prev_l, curr_bi_low))
|
||||||
|
array.set(feat_merged_end_bars, fm_size - 1, curr_bi_end_bar)
|
||||||
|
array.set(feat_merged_end_prices, fm_size - 1, curr_bi_end_price)
|
||||||
|
else
|
||||||
|
array.push(feat_merged_highs, curr_bi_high)
|
||||||
|
array.push(feat_merged_lows, curr_bi_low)
|
||||||
|
array.push(feat_merged_end_bars, curr_bi_end_bar)
|
||||||
|
array.push(feat_merged_end_prices, curr_bi_end_price)
|
||||||
|
|
||||||
|
// 至少3笔后检查线段是否结束
|
||||||
|
xd_broken = false
|
||||||
|
xd_break_bar = 0
|
||||||
|
xd_break_price = 0.0
|
||||||
|
|
||||||
|
if xd_bi_count >= 3
|
||||||
|
fm_size = array.size(feat_merged_highs)
|
||||||
|
|
||||||
|
// === 第一种情况:特征序列包含处理后出现分型 ===
|
||||||
|
if fm_size >= 3
|
||||||
|
fm_h1 = array.get(feat_merged_highs, fm_size - 3)
|
||||||
|
fm_h2 = array.get(feat_merged_highs, fm_size - 2)
|
||||||
|
fm_h3 = array.get(feat_merged_highs, fm_size - 1)
|
||||||
|
fm_l1 = array.get(feat_merged_lows, fm_size - 3)
|
||||||
|
fm_l2 = array.get(feat_merged_lows, fm_size - 2)
|
||||||
|
fm_l3 = array.get(feat_merged_lows, fm_size - 1)
|
||||||
|
|
||||||
|
if xd_dir == 1
|
||||||
|
if fm_h2 > fm_h1 and fm_h2 > fm_h3
|
||||||
|
xd_broken := true
|
||||||
|
else
|
||||||
|
if fm_l2 < fm_l1 and fm_l2 < fm_l3
|
||||||
|
xd_broken := true
|
||||||
|
|
||||||
|
// === 第二种情况:缺口确认反转 ===
|
||||||
|
if not xd_broken and fm_size >= 2
|
||||||
|
if xd_dir == 1
|
||||||
|
fm_prev_h2 = array.get(feat_merged_highs, fm_size - 2)
|
||||||
|
fm_prev_l2 = array.get(feat_merged_lows, fm_size - 2)
|
||||||
|
fm_curr_h2 = array.get(feat_merged_highs, fm_size - 1)
|
||||||
|
fm_curr_l2 = array.get(feat_merged_lows, fm_size - 1)
|
||||||
|
if fm_curr_h2 < fm_prev_l2
|
||||||
|
xd_broken := true
|
||||||
|
else
|
||||||
|
fm_prev_h2 = array.get(feat_merged_highs, fm_size - 2)
|
||||||
|
fm_prev_l2 = array.get(feat_merged_lows, fm_size - 2)
|
||||||
|
fm_curr_h2 = array.get(feat_merged_highs, fm_size - 1)
|
||||||
|
fm_curr_l2 = array.get(feat_merged_lows, fm_size - 1)
|
||||||
|
if fm_curr_l2 > fm_prev_h2
|
||||||
|
xd_broken := true
|
||||||
|
|
||||||
|
if xd_broken
|
||||||
|
xd_end_bar = xd_dir == 1 ? xd_high_bar : xd_low_bar
|
||||||
|
xd_end_price = xd_dir == 1 ? xd_high : xd_low
|
||||||
|
|
||||||
|
array.push(xd_start_bars, xd_start_bar)
|
||||||
|
array.push(xd_start_prices, xd_start_price)
|
||||||
|
array.push(xd_end_bars, xd_end_bar)
|
||||||
|
array.push(xd_end_prices, xd_end_price)
|
||||||
|
array.push(xd_dirs, xd_dir)
|
||||||
|
|
||||||
|
xd_start_bar := xd_end_bar
|
||||||
|
xd_start_price := xd_end_price
|
||||||
|
xd_dir := xd_dir == 1 ? -1 : 1
|
||||||
|
xd_bi_count := 1
|
||||||
|
|
||||||
|
xd_high := curr_bi_high
|
||||||
|
xd_low := curr_bi_low
|
||||||
|
xd_high_bar := curr_bi_dir == 1 ? curr_bi_end_bar : curr_bi_start_bar
|
||||||
|
xd_low_bar := curr_bi_dir == -1 ? curr_bi_end_bar : curr_bi_start_bar
|
||||||
|
|
||||||
|
array.clear(feat_raw_highs)
|
||||||
|
array.clear(feat_raw_lows)
|
||||||
|
array.clear(feat_raw_end_bars)
|
||||||
|
array.clear(feat_raw_end_prices)
|
||||||
|
array.clear(feat_merged_highs)
|
||||||
|
array.clear(feat_merged_lows)
|
||||||
|
array.clear(feat_merged_end_bars)
|
||||||
|
array.clear(feat_merged_end_prices)
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
feat_final_size = array.size(feat_merged_highs)
|
||||||
|
if xd_bi_count >= 3 and feat_final_size >= 2
|
||||||
|
xd_end_bar = xd_dir == 1 ? xd_high_bar : xd_low_bar
|
||||||
|
xd_end_price = xd_dir == 1 ? xd_high : xd_low
|
||||||
|
|
||||||
|
array.push(xd_start_bars, xd_start_bar)
|
||||||
|
array.push(xd_start_prices, xd_start_price)
|
||||||
|
array.push(xd_end_bars, xd_end_bar)
|
||||||
|
array.push(xd_end_prices, xd_end_price)
|
||||||
|
array.push(xd_dirs, xd_dir)
|
||||||
|
|
||||||
|
xd_count = array.size(xd_start_bars)
|
||||||
|
|
||||||
|
// ==================== 绘制线段 ====================
|
||||||
|
if show_xianduan and xd_count > 0
|
||||||
|
for i = 0 to xd_count - 1
|
||||||
|
s_bar = array.get(xd_start_bars, i)
|
||||||
|
s_price = array.get(xd_start_prices, i)
|
||||||
|
e_bar = array.get(xd_end_bars, i)
|
||||||
|
e_price = array.get(xd_end_prices, i)
|
||||||
|
xd_dir_draw = array.get(xd_dirs, i)
|
||||||
|
|
||||||
|
if bar_index - s_bar <= 5000
|
||||||
|
xd_color = xd_dir_draw == 1 ? color.green : color.red
|
||||||
|
line.new(s_bar, s_price, e_bar, e_price, color=xd_color, width=4)
|
||||||
|
|
||||||
|
// ==================== 构建笔中枢(线段内部) ====================
|
||||||
|
var int[] bi_zs_start_bars = array.new<int>()
|
||||||
|
var int[] bi_zs_end_bars = array.new<int>()
|
||||||
|
var float[] bi_zs_highs = array.new<float>()
|
||||||
|
var float[] bi_zs_lows = array.new<float>()
|
||||||
|
var int[] bi_zs_types = array.new<int>()
|
||||||
|
|
||||||
|
array.clear(bi_zs_start_bars)
|
||||||
|
array.clear(bi_zs_end_bars)
|
||||||
|
array.clear(bi_zs_highs)
|
||||||
|
array.clear(bi_zs_lows)
|
||||||
|
array.clear(bi_zs_types)
|
||||||
|
|
||||||
|
if xd_count > 0 and bi_count >= 3
|
||||||
|
for xd_idx = 0 to xd_count - 1
|
||||||
|
xd_s_bar = array.get(xd_start_bars, xd_idx)
|
||||||
|
xd_e_bar = array.get(xd_end_bars, xd_idx)
|
||||||
|
xd_direction = array.get(xd_dirs, xd_idx)
|
||||||
|
|
||||||
|
var int[] xd_bi_indices = array.new<int>()
|
||||||
|
array.clear(xd_bi_indices)
|
||||||
|
for bi_idx = 0 to bi_count - 1
|
||||||
|
bi_s = array.get(bi_start_bars, bi_idx)
|
||||||
|
bi_e = array.get(bi_end_bars, bi_idx)
|
||||||
|
if bi_s >= xd_s_bar and bi_e <= xd_e_bar
|
||||||
|
array.push(xd_bi_indices, bi_idx)
|
||||||
|
|
||||||
|
local_bi_count = array.size(xd_bi_indices)
|
||||||
|
|
||||||
|
if local_bi_count >= 3
|
||||||
|
j = 0
|
||||||
|
while j <= local_bi_count - 3
|
||||||
|
idx1 = array.get(xd_bi_indices, j)
|
||||||
|
idx2 = array.get(xd_bi_indices, j + 1)
|
||||||
|
idx3 = array.get(xd_bi_indices, j + 2)
|
||||||
|
|
||||||
|
bi1_h = math.max(array.get(bi_start_prices, idx1), array.get(bi_end_prices, idx1))
|
||||||
|
bi1_l = math.min(array.get(bi_start_prices, idx1), array.get(bi_end_prices, idx1))
|
||||||
|
bi2_h = math.max(array.get(bi_start_prices, idx2), array.get(bi_end_prices, idx2))
|
||||||
|
bi2_l = math.min(array.get(bi_start_prices, idx2), array.get(bi_end_prices, idx2))
|
||||||
|
bi3_h = math.max(array.get(bi_start_prices, idx3), array.get(bi_end_prices, idx3))
|
||||||
|
bi3_l = math.min(array.get(bi_start_prices, idx3), array.get(bi_end_prices, idx3))
|
||||||
|
|
||||||
|
zg = math.min(bi1_h, math.min(bi2_h, bi3_h))
|
||||||
|
zd = math.max(bi1_l, math.max(bi2_l, bi3_l))
|
||||||
|
|
||||||
|
if zg > zd
|
||||||
|
zs_start = array.get(bi_start_bars, idx1)
|
||||||
|
zs_end = array.get(bi_end_bars, idx3)
|
||||||
|
zs_type = xd_direction
|
||||||
|
|
||||||
|
last_ext = j + 2
|
||||||
|
if j + 3 < local_bi_count
|
||||||
|
for ext_j = j + 3 to local_bi_count - 1
|
||||||
|
ext_idx = array.get(xd_bi_indices, ext_j)
|
||||||
|
ext_h = math.max(array.get(bi_start_prices, ext_idx), array.get(bi_end_prices, ext_idx))
|
||||||
|
ext_l = math.min(array.get(bi_start_prices, ext_idx), array.get(bi_end_prices, ext_idx))
|
||||||
|
if ext_l < zg and ext_h > zd
|
||||||
|
zs_end := array.get(bi_end_bars, ext_idx)
|
||||||
|
last_ext := ext_j
|
||||||
|
else
|
||||||
|
break
|
||||||
|
|
||||||
|
should_add_zs = true
|
||||||
|
zs_arr_size = array.size(bi_zs_start_bars)
|
||||||
|
if zs_arr_size > 0
|
||||||
|
last_zs_end = array.get(bi_zs_end_bars, zs_arr_size - 1)
|
||||||
|
if zs_start <= last_zs_end
|
||||||
|
should_add_zs := false
|
||||||
|
|
||||||
|
if should_add_zs
|
||||||
|
array.push(bi_zs_start_bars, zs_start)
|
||||||
|
array.push(bi_zs_end_bars, zs_end)
|
||||||
|
array.push(bi_zs_highs, zg)
|
||||||
|
array.push(bi_zs_lows, zd)
|
||||||
|
array.push(bi_zs_types, zs_type)
|
||||||
|
j := last_ext + 1
|
||||||
|
else
|
||||||
|
j += 1
|
||||||
|
else
|
||||||
|
j += 1
|
||||||
|
|
||||||
|
bi_zs_count = array.size(bi_zs_start_bars)
|
||||||
|
|
||||||
|
// ==================== 构建线段中枢 ====================
|
||||||
|
var int[] xd_zs_start_bars = array.new<int>()
|
||||||
|
var int[] xd_zs_end_bars = array.new<int>()
|
||||||
|
var float[] xd_zs_highs = array.new<float>()
|
||||||
|
var float[] xd_zs_lows = array.new<float>()
|
||||||
|
var int[] xd_zs_types = array.new<int>()
|
||||||
|
|
||||||
|
array.clear(xd_zs_start_bars)
|
||||||
|
array.clear(xd_zs_end_bars)
|
||||||
|
array.clear(xd_zs_highs)
|
||||||
|
array.clear(xd_zs_lows)
|
||||||
|
array.clear(xd_zs_types)
|
||||||
|
|
||||||
|
if xd_count >= 3
|
||||||
|
i = 0
|
||||||
|
while i <= xd_count - 3
|
||||||
|
xd1_dir = array.get(xd_dirs, i)
|
||||||
|
xd2_dir = array.get(xd_dirs, i + 1)
|
||||||
|
xd3_dir = array.get(xd_dirs, i + 2)
|
||||||
|
|
||||||
|
is_up_xd_zs = xd1_dir == -1 and xd2_dir == 1 and xd3_dir == -1
|
||||||
|
is_down_xd_zs = xd1_dir == 1 and xd2_dir == -1 and xd3_dir == 1
|
||||||
|
|
||||||
|
if is_up_xd_zs or is_down_xd_zs
|
||||||
|
xd1_s_p = array.get(xd_start_prices, i)
|
||||||
|
xd1_e_p = array.get(xd_end_prices, i)
|
||||||
|
xd2_s_p = array.get(xd_start_prices, i + 1)
|
||||||
|
xd2_e_p = array.get(xd_end_prices, i + 1)
|
||||||
|
xd3_s_p = array.get(xd_start_prices, i + 2)
|
||||||
|
xd3_e_p = array.get(xd_end_prices, i + 2)
|
||||||
|
|
||||||
|
xd1_h = math.max(xd1_s_p, xd1_e_p)
|
||||||
|
xd1_l = math.min(xd1_s_p, xd1_e_p)
|
||||||
|
xd2_h = math.max(xd2_s_p, xd2_e_p)
|
||||||
|
xd2_l = math.min(xd2_s_p, xd2_e_p)
|
||||||
|
xd3_h = math.max(xd3_s_p, xd3_e_p)
|
||||||
|
xd3_l = math.min(xd3_s_p, xd3_e_p)
|
||||||
|
|
||||||
|
zg_xd = math.min(xd1_h, math.min(xd2_h, xd3_h))
|
||||||
|
zd_xd = math.max(xd1_l, math.max(xd2_l, xd3_l))
|
||||||
|
|
||||||
|
if zg_xd > zd_xd
|
||||||
|
zs_start_xd = array.get(xd_start_bars, i)
|
||||||
|
zs_end_xd = array.get(xd_end_bars, i + 2)
|
||||||
|
xd_zs_type = is_up_xd_zs ? 1 : -1
|
||||||
|
|
||||||
|
last_ext_xd = i + 2
|
||||||
|
if i + 3 < xd_count
|
||||||
|
for ext_idx = i + 3 to xd_count - 1
|
||||||
|
ext_s_p = array.get(xd_start_prices, ext_idx)
|
||||||
|
ext_e_p = array.get(xd_end_prices, ext_idx)
|
||||||
|
ext_h = math.max(ext_s_p, ext_e_p)
|
||||||
|
ext_l = math.min(ext_s_p, ext_e_p)
|
||||||
|
if ext_l < zg_xd and ext_h > zd_xd
|
||||||
|
zs_end_xd := array.get(xd_end_bars, ext_idx)
|
||||||
|
last_ext_xd := ext_idx
|
||||||
|
else
|
||||||
|
break
|
||||||
|
|
||||||
|
should_add_xd_zs = true
|
||||||
|
xd_zs_arr_size = array.size(xd_zs_start_bars)
|
||||||
|
if xd_zs_arr_size > 0
|
||||||
|
last_xd_zs_end = array.get(xd_zs_end_bars, xd_zs_arr_size - 1)
|
||||||
|
if zs_start_xd <= last_xd_zs_end
|
||||||
|
should_add_xd_zs := false
|
||||||
|
|
||||||
|
if should_add_xd_zs
|
||||||
|
array.push(xd_zs_start_bars, zs_start_xd)
|
||||||
|
array.push(xd_zs_end_bars, zs_end_xd)
|
||||||
|
array.push(xd_zs_highs, zg_xd)
|
||||||
|
array.push(xd_zs_lows, zd_xd)
|
||||||
|
array.push(xd_zs_types, xd_zs_type)
|
||||||
|
i := last_ext_xd + 1
|
||||||
|
else
|
||||||
|
i += 1
|
||||||
|
else
|
||||||
|
i += 1
|
||||||
|
else
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
xd_zs_count = array.size(xd_zs_start_bars)
|
||||||
|
|
||||||
|
// ==================== 绘制笔中枢 ====================
|
||||||
|
if show_bi_zhongshu and bi_zs_count > 0
|
||||||
|
for i = 0 to bi_zs_count - 1
|
||||||
|
zs_start = array.get(bi_zs_start_bars, i)
|
||||||
|
zs_end = array.get(bi_zs_end_bars, i)
|
||||||
|
zs_h = array.get(bi_zs_highs, i)
|
||||||
|
zs_l = array.get(bi_zs_lows, i)
|
||||||
|
zs_type = array.get(bi_zs_types, i)
|
||||||
|
|
||||||
|
if bar_index - zs_start <= 5000
|
||||||
|
zs_border = zs_type == 1 ? color.green : color.red
|
||||||
|
zs_bg = zs_type == 1 ? color.new(color.green, 85) : color.new(color.red, 85)
|
||||||
|
box.new(zs_start, zs_h, zs_end, zs_l,
|
||||||
|
border_color=zs_border, bgcolor=zs_bg, border_width=1)
|
||||||
|
|
||||||
|
// ==================== 绘制线段中枢 ====================
|
||||||
|
if show_xd_zhongshu and xd_zs_count > 0
|
||||||
|
for i = 0 to xd_zs_count - 1
|
||||||
|
zs_start = array.get(xd_zs_start_bars, i)
|
||||||
|
zs_end = array.get(xd_zs_end_bars, i)
|
||||||
|
zs_h = array.get(xd_zs_highs, i)
|
||||||
|
zs_l = array.get(xd_zs_lows, i)
|
||||||
|
xd_zs_type = array.get(xd_zs_types, i)
|
||||||
|
|
||||||
|
if bar_index - zs_start <= 5000
|
||||||
|
xd_zs_border = xd_zs_type == 1 ? color.lime : color.maroon
|
||||||
|
xd_zs_bg = xd_zs_type == 1 ? color.new(color.lime, 80) : color.new(color.maroon, 80)
|
||||||
|
box.new(zs_start, zs_h, zs_end, zs_l,
|
||||||
|
border_color=xd_zs_border, bgcolor=xd_zs_bg, border_width=2)
|
||||||
@@ -0,0 +1,411 @@
|
|||||||
|
//@version=6
|
||||||
|
indicator("多周期EMA52_Pro", overlay=true, max_labels_count=500, max_lines_count=500)
|
||||||
|
|
||||||
|
// ======================== 配置输入 ========================
|
||||||
|
// 主控
|
||||||
|
enableCTF = input.bool(true, "启用本级别", group="显示")
|
||||||
|
enableHTF = input.bool(true, "启用长级别", group="显示")
|
||||||
|
enableLTF = input.bool(true, "启用次级别", group="显示")
|
||||||
|
maxHTFInput = input.int(10, "最多显示长级别数量", minval=1, maxval=12, group="显示")
|
||||||
|
maxLTFInput = input.int(3, "最多显示次级别数量", minval=1, maxval=4, group="显示")
|
||||||
|
protectAutoscale = input.bool(true, "防拉伸: 仅绘制可视区间内", group="显示")
|
||||||
|
lookbackBars = input.int(300, "可视区间近多少根K线", minval=50, maxval=5000, group="显示")
|
||||||
|
padPercent = input.float(2.0, "可视边界留白(%)", minval=0.0, maxval=20.0, step=0.1, group="显示")
|
||||||
|
|
||||||
|
// 固定 12 周期开关(本级别也会跟随对应周期开关)
|
||||||
|
showRes1 = input.bool(true, "显示 1m", group="级别开关")
|
||||||
|
showRes2 = input.bool(true, "显示 3m", group="级别开关")
|
||||||
|
showRes3 = input.bool(true, "显示 5m", group="级别开关")
|
||||||
|
showRes4 = input.bool(true, "显示 15m", group="级别开关")
|
||||||
|
showRes5 = input.bool(true, "显示 30m", group="级别开关")
|
||||||
|
showRes6 = input.bool(true, "显示 45m", group="级别开关")
|
||||||
|
showRes7 = input.bool(true, "显示 1h", group="级别开关")
|
||||||
|
showRes8 = input.bool(true, "显示 2h", group="级别开关")
|
||||||
|
showRes9 = input.bool(true, "显示 4h", group="级别开关")
|
||||||
|
showRes10 = input.bool(true, "显示 6h", group="级别开关")
|
||||||
|
showRes11 = input.bool(true, "显示 8h", group="级别开关")
|
||||||
|
showRes12 = input.bool(true, "显示 12h", group="级别开关")
|
||||||
|
|
||||||
|
// 视觉(虚线效果)
|
||||||
|
dashLenHTF = input.int(6, "长级别虚线段长度", minval=1, maxval=50, group="线型")
|
||||||
|
dashGapHTF = input.int(4, "长级别虚线间隔", minval=1, maxval=50, group="线型")
|
||||||
|
dashLenLTF = input.int(2, "次级别虚线段长度", minval=1, maxval=50, group="线型")
|
||||||
|
dashGapLTF = input.int(3, "次级别虚线间隔", minval=1, maxval=50, group="线型")
|
||||||
|
lineWidthHTF = input.int(1, "长级别线宽", minval=1, maxval=4, group="线型")
|
||||||
|
lineWidthLTF = input.int(1, "次级别线宽", minval=1, maxval=4, group="线型")
|
||||||
|
lineWidthCTF = input.int(2, "本级别线宽", minval=1, maxval=4, group="线型")
|
||||||
|
enableSmooth = input.bool(true, "平滑显示(EMA)", group="线型")
|
||||||
|
smoothLenHTF = input.int(3, "长级别平滑长度", minval=1, maxval=50, group="线型")
|
||||||
|
smoothLenLTF = input.int(3, "次级别平滑长度", minval=1, maxval=50, group="线型")
|
||||||
|
smoothLenCTF = input.int(3, "本级别平滑长度", minval=1, maxval=50, group="线型")
|
||||||
|
smoothMethod = input.string("ALMA", "平滑方法", options=["EMA","DEMA","TEMA","WMA","HMA","ALMA","LinReg"], group="线型")
|
||||||
|
almaOffset = input.float(0.85, "ALMA偏移(0-1)", minval=0.0, maxval=1.0, step=0.05, group="线型")
|
||||||
|
almaSigma = input.float(6.0, "ALMA Sigma", minval=0.5, maxval=10.0, step=0.5, group="线型")
|
||||||
|
labelBgAlpha = input.int(85, "标签背景透明(0-100)", minval=0, maxval=100, group="标注")
|
||||||
|
labelSize = input.string("normal", "标签字号", options=["tiny","small","normal","large","huge"], group="标注")
|
||||||
|
labelOffsetBars = input.int(2, "标签右移K线的距离(根数)", minval=0, maxval=50, group="标注")
|
||||||
|
|
||||||
|
// 常量上限(编译期固定,用于生成固定数量的 plot 位)
|
||||||
|
const int MAX_HTF = 12
|
||||||
|
const int MAX_LTF = 4
|
||||||
|
|
||||||
|
// ======================== 工具函数 ========================
|
||||||
|
f_dash(series float v, int phase, int dashLen, int dashGap) =>
|
||||||
|
// 通过丢弃部分柱来营造虚线效果
|
||||||
|
isShow = (bar_index + phase) % (dashLen + dashGap) < dashLen
|
||||||
|
isShow ? v : na
|
||||||
|
|
||||||
|
// 自定义 DEMA / TEMA(Pine 无内置 dema/tema)
|
||||||
|
f_dema(series float v, int len) =>
|
||||||
|
ema1 = ta.ema(v, len)
|
||||||
|
ema2 = ta.ema(ema1, len)
|
||||||
|
2.0 * ema1 - ema2
|
||||||
|
|
||||||
|
f_tema(series float v, int len) =>
|
||||||
|
ema1 = ta.ema(v, len)
|
||||||
|
ema2 = ta.ema(ema1, len)
|
||||||
|
ema3 = ta.ema(ema2, len)
|
||||||
|
3.0 * (ema1 - ema2) + ema3
|
||||||
|
|
||||||
|
// 通用平滑(仅用于显示)
|
||||||
|
f_smooth(series float v, int len) =>
|
||||||
|
not enableSmooth or len <= 1 ? v :
|
||||||
|
smoothMethod == "EMA" ? ta.ema(v, len) :
|
||||||
|
smoothMethod == "DEMA" ? f_dema(v, len) :
|
||||||
|
smoothMethod == "TEMA" ? f_tema(v, len) :
|
||||||
|
smoothMethod == "WMA" ? ta.wma(v, len) :
|
||||||
|
smoothMethod == "HMA" ? ta.hma(v, len) :
|
||||||
|
smoothMethod == "ALMA" ? ta.alma(v, len, almaOffset, almaSigma) :
|
||||||
|
ta.linreg(v, len, 0)
|
||||||
|
|
||||||
|
f_strToSize(string s) => switch s
|
||||||
|
"tiny" => size.tiny
|
||||||
|
"small" => size.small
|
||||||
|
"normal" => size.normal
|
||||||
|
"large" => size.large
|
||||||
|
=> size.huge
|
||||||
|
|
||||||
|
f_showRes(string tf) => switch tf
|
||||||
|
"1" => showRes1
|
||||||
|
"3" => showRes2
|
||||||
|
"5" => showRes3
|
||||||
|
"15" => showRes4
|
||||||
|
"30" => showRes5
|
||||||
|
"45" => showRes6
|
||||||
|
"60" => showRes7
|
||||||
|
"120" => showRes8
|
||||||
|
"240" => showRes9
|
||||||
|
"360" => showRes10
|
||||||
|
"480" => showRes11
|
||||||
|
"720" => showRes12
|
||||||
|
=> true
|
||||||
|
|
||||||
|
// 颜色调色板(循环使用)
|
||||||
|
var color[] PALETTE = array.new_color()
|
||||||
|
if barstate.isfirst and array.size(PALETTE) == 0
|
||||||
|
array.push(PALETTE, color.new(color.teal, 0))
|
||||||
|
array.push(PALETTE, color.new(color.orange, 0))
|
||||||
|
array.push(PALETTE, color.new(color.fuchsia,0))
|
||||||
|
array.push(PALETTE, color.new(color.aqua, 0))
|
||||||
|
array.push(PALETTE, color.new(color.yellow, 0))
|
||||||
|
array.push(PALETTE, color.new(color.purple, 0))
|
||||||
|
array.push(PALETTE, color.new(color.lime, 0))
|
||||||
|
array.push(PALETTE, color.new(color.red, 0))
|
||||||
|
array.push(PALETTE, color.new(color.blue, 0))
|
||||||
|
array.push(PALETTE, color.new(color.navy, 0))
|
||||||
|
array.push(PALETTE, color.new(color.maroon, 0))
|
||||||
|
array.push(PALETTE, color.new(color.silver, 0))
|
||||||
|
|
||||||
|
f_paletteColor(int idx) =>
|
||||||
|
sz = math.max(1, array.size(PALETTE))
|
||||||
|
array.get(PALETTE, idx % sz)
|
||||||
|
|
||||||
|
// 计算可视范围边界(近 N 根 K 线 + 百分比留白)
|
||||||
|
rangeHi = ta.highest(high, lookbackBars)
|
||||||
|
rangeLo = ta.lowest(low, lookbackBars)
|
||||||
|
rangeSpan = rangeHi - rangeLo
|
||||||
|
pad = rangeSpan * padPercent * 0.01
|
||||||
|
minY = rangeLo - pad
|
||||||
|
maxY = rangeHi + pad
|
||||||
|
|
||||||
|
// 仅在可视区间内绘制(超出返回 na,以避免影响自动缩放)。
|
||||||
|
f_gate(series float v) => protectAutoscale ? (v >= minY and v <= maxY ? v : na) : v
|
||||||
|
|
||||||
|
// 检查是否在可视区间内
|
||||||
|
f_inRange(series float v) => protectAutoscale ? (v >= minY and v <= maxY) : true
|
||||||
|
|
||||||
|
// 周期字符串美化(如 1 -> 1m, 60 -> 1h, D -> 1D, W -> 1W, 3M -> 3M)
|
||||||
|
f_prettyTf(string tf) =>
|
||||||
|
s = timeframe.in_seconds(tf)
|
||||||
|
string out = tf
|
||||||
|
if not na(s)
|
||||||
|
if s < 3600
|
||||||
|
mins = math.max(1, math.round(s / 60))
|
||||||
|
out := str.tostring(mins) + 'm'
|
||||||
|
else if s % 3600 == 0 and s < 86400
|
||||||
|
hrs = math.round(s / 3600)
|
||||||
|
out := str.tostring(hrs) + 'h'
|
||||||
|
else if s % 86400 == 0 and s < 7 * 86400
|
||||||
|
days = math.round(s / 86400)
|
||||||
|
out := str.tostring(days) + 'D'
|
||||||
|
else if s % (7 * 86400) == 0 and s < 30 * 86400
|
||||||
|
weeks = math.round(s / (7 * 86400))
|
||||||
|
out := str.tostring(weeks) + 'W'
|
||||||
|
else
|
||||||
|
out := tf
|
||||||
|
else
|
||||||
|
// 月等返回 na 的分辨率保持原样(M/3M)
|
||||||
|
out := tf
|
||||||
|
out
|
||||||
|
|
||||||
|
// ======================== 标准时间周期表 ========================
|
||||||
|
// 备注:TradingView 分辨率字符串 —— 分钟: "1","3","5"...;小时: "60","120"...;
|
||||||
|
// 仅保留分钟与小时级别。
|
||||||
|
var string[] ALL_RES = array.new_string()
|
||||||
|
if barstate.isfirst and array.size(ALL_RES) == 0
|
||||||
|
// 由低到高,便于筛选
|
||||||
|
// 分钟
|
||||||
|
array.push(ALL_RES, "1")
|
||||||
|
array.push(ALL_RES, "3")
|
||||||
|
array.push(ALL_RES, "5")
|
||||||
|
array.push(ALL_RES, "15")
|
||||||
|
array.push(ALL_RES, "30")
|
||||||
|
array.push(ALL_RES, "45")
|
||||||
|
// 小时(以分钟表示)
|
||||||
|
array.push(ALL_RES, "60")
|
||||||
|
array.push(ALL_RES, "120")
|
||||||
|
array.push(ALL_RES, "240")
|
||||||
|
array.push(ALL_RES, "360")
|
||||||
|
array.push(ALL_RES, "480")
|
||||||
|
array.push(ALL_RES, "720")
|
||||||
|
|
||||||
|
// ======================== 周期间关系与选择 ========================
|
||||||
|
curResStr = timeframe.period
|
||||||
|
curResSec = timeframe.in_seconds(curResStr)
|
||||||
|
|
||||||
|
// 构建长/次级别列表
|
||||||
|
var string[] htfRes = array.new_string()
|
||||||
|
var string[] ltfRes = array.new_string()
|
||||||
|
array.clear(htfRes)
|
||||||
|
array.clear(ltfRes)
|
||||||
|
for i = 0 to array.size(ALL_RES) - 1
|
||||||
|
res = array.get(ALL_RES, i)
|
||||||
|
rsec = timeframe.in_seconds(res)
|
||||||
|
// timeframe.in_seconds 不支持时返回 na,需跳过
|
||||||
|
if na(rsec)
|
||||||
|
continue
|
||||||
|
if rsec > curResSec
|
||||||
|
array.push(htfRes, res)
|
||||||
|
else if rsec < curResSec
|
||||||
|
array.push(ltfRes, res)
|
||||||
|
|
||||||
|
// 选取靠近当前周期的若干次级别(更有参考意义):取 ltfRes 的末尾(更接近当前)
|
||||||
|
var string[] ltfSel = array.new_string()
|
||||||
|
array.clear(ltfSel)
|
||||||
|
ltfsz = array.size(ltfRes)
|
||||||
|
if ltfsz > 0
|
||||||
|
take = math.min(maxLTFInput, MAX_LTF, ltfsz)
|
||||||
|
for k = 0 to take - 1
|
||||||
|
array.push(ltfSel, array.get(ltfRes, ltfsz - 1 - k))
|
||||||
|
|
||||||
|
// 限制长级别数量
|
||||||
|
htfCountSelected = math.min(array.size(htfRes), maxHTFInput, MAX_HTF)
|
||||||
|
ltfCountSelected = math.min(array.size(ltfSel), MAX_LTF)
|
||||||
|
|
||||||
|
// ======================== 计算各周期 EMA52 ========================
|
||||||
|
// 预分配数值数组(保持固定最大长度)
|
||||||
|
var float[] htfVals = array.new_float()
|
||||||
|
var float[] ltfVals = array.new_float()
|
||||||
|
var float[] htfLabelVals = array.new_float()
|
||||||
|
var float[] ltfLabelVals = array.new_float()
|
||||||
|
float ctfVal = na
|
||||||
|
float ctfLabelVal = na
|
||||||
|
if barstate.isfirst
|
||||||
|
for _i = 0 to MAX_HTF - 1
|
||||||
|
array.push(htfVals, na)
|
||||||
|
array.push(htfLabelVals, na)
|
||||||
|
for _j = 0 to MAX_LTF - 1
|
||||||
|
array.push(ltfVals, na)
|
||||||
|
array.push(ltfLabelVals, na)
|
||||||
|
|
||||||
|
// 填充长级别数值
|
||||||
|
for i = 0 to MAX_HTF - 1
|
||||||
|
float val = na
|
||||||
|
float labelVal = na
|
||||||
|
if enableHTF and i < htfCountSelected
|
||||||
|
tfStr = array.get(htfRes, i)
|
||||||
|
if f_showRes(tfStr)
|
||||||
|
// 计算该长级别的 EMA52
|
||||||
|
val := request.security(syminfo.tickerid, tfStr, ta.ema(close, 52), barmerge.gaps_off, barmerge.lookahead_off)
|
||||||
|
// 标签不跟随虚线断续,仅跟随级别开关与可视区间
|
||||||
|
labelVal := f_gate(f_smooth(val, smoothLenHTF))
|
||||||
|
array.set(htfVals, i, val)
|
||||||
|
array.set(htfLabelVals, i, labelVal)
|
||||||
|
|
||||||
|
// 填充次级别数值
|
||||||
|
for i = 0 to MAX_LTF - 1
|
||||||
|
float val = na
|
||||||
|
float labelVal = na
|
||||||
|
if enableLTF and i < ltfCountSelected
|
||||||
|
tfStr = array.get(ltfSel, i)
|
||||||
|
if f_showRes(tfStr)
|
||||||
|
// 计算该次级别的 EMA52(低级别向上合成,注意其更频繁更新)
|
||||||
|
val := request.security(syminfo.tickerid, tfStr, ta.ema(close, 52), barmerge.gaps_off, barmerge.lookahead_off)
|
||||||
|
// 标签不跟随虚线断续,仅跟随级别开关与可视区间
|
||||||
|
labelVal := f_gate(f_smooth(val, smoothLenLTF))
|
||||||
|
array.set(ltfVals, i, val)
|
||||||
|
array.set(ltfLabelVals, i, labelVal)
|
||||||
|
|
||||||
|
// 本级别 EMA52
|
||||||
|
if enableCTF and f_showRes(curResStr)
|
||||||
|
ctfVal := ta.ema(close, 52)
|
||||||
|
ctfLabelVal := (enableCTF and f_showRes(curResStr)) ? f_gate(f_smooth(ctfVal, smoothLenCTF)) : na
|
||||||
|
|
||||||
|
// ======================== 绘制(虚线 + 标签) ========================
|
||||||
|
// 顶层固定 plot(避免在局部/循环中调用 plot)
|
||||||
|
plot((enableCTF and f_showRes(curResStr)) ? f_gate(f_smooth(ctfVal, smoothLenCTF)) : na, title='CTF EMA52', color=color.new(color.white, 0), linewidth=lineWidthCTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 0 < htfCountSelected and not na(array.get(htfVals, 0))) ? f_gate(f_dash(f_smooth(array.get(htfVals, 0), smoothLenHTF), 0 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 1', color=f_paletteColor(0), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 1 < htfCountSelected and not na(array.get(htfVals, 1))) ? f_gate(f_dash(f_smooth(array.get(htfVals, 1), smoothLenHTF), 1 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 2', color=f_paletteColor(1), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 2 < htfCountSelected and not na(array.get(htfVals, 2))) ? f_gate(f_dash(f_smooth(array.get(htfVals, 2), smoothLenHTF), 2 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 3', color=f_paletteColor(2), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 3 < htfCountSelected and not na(array.get(htfVals, 3))) ? f_gate(f_dash(f_smooth(array.get(htfVals, 3), smoothLenHTF), 3 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 4', color=f_paletteColor(3), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 4 < htfCountSelected and not na(array.get(htfVals, 4))) ? f_gate(f_dash(f_smooth(array.get(htfVals, 4), smoothLenHTF), 4 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 5', color=f_paletteColor(4), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 5 < htfCountSelected and not na(array.get(htfVals, 5))) ? f_gate(f_dash(f_smooth(array.get(htfVals, 5), smoothLenHTF), 5 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 6', color=f_paletteColor(5), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 6 < htfCountSelected and not na(array.get(htfVals, 6))) ? f_gate(f_dash(f_smooth(array.get(htfVals, 6), smoothLenHTF), 6 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 7', color=f_paletteColor(6), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 7 < htfCountSelected and not na(array.get(htfVals, 7))) ? f_gate(f_dash(f_smooth(array.get(htfVals, 7), smoothLenHTF), 7 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 8', color=f_paletteColor(7), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 8 < htfCountSelected and not na(array.get(htfVals, 8))) ? f_gate(f_dash(f_smooth(array.get(htfVals, 8), smoothLenHTF), 8 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 9', color=f_paletteColor(8), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 9 < htfCountSelected and not na(array.get(htfVals, 9))) ? f_gate(f_dash(f_smooth(array.get(htfVals, 9), smoothLenHTF), 9 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 10', color=f_paletteColor(9), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 10 < htfCountSelected and not na(array.get(htfVals, 10))) ? f_gate(f_dash(f_smooth(array.get(htfVals, 10), smoothLenHTF), 10 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 11', color=f_paletteColor(10), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 11 < htfCountSelected and not na(array.get(htfVals, 11))) ? f_gate(f_dash(f_smooth(array.get(htfVals, 11), smoothLenHTF), 11 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 12', color=f_paletteColor(11), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
|
||||||
|
plot((enableLTF and 0 < ltfCountSelected and not na(array.get(ltfVals, 0))) ? f_gate(f_dash(f_smooth(array.get(ltfVals, 0), smoothLenLTF), 0 * 3 + 1, dashLenLTF, dashGapLTF)) : na, title='LTF EMA52 1', color=color.new(f_paletteColor(0), 30), linewidth=lineWidthLTF, style=plot.style_line)
|
||||||
|
plot((enableLTF and 1 < ltfCountSelected and not na(array.get(ltfVals, 1))) ? f_gate(f_dash(f_smooth(array.get(ltfVals, 1), smoothLenLTF), 1 * 3 + 1, dashLenLTF, dashGapLTF)) : na, title='LTF EMA52 2', color=color.new(f_paletteColor(1), 30), linewidth=lineWidthLTF, style=plot.style_line)
|
||||||
|
plot((enableLTF and 2 < ltfCountSelected and not na(array.get(ltfVals, 2))) ? f_gate(f_dash(f_smooth(array.get(ltfVals, 2), smoothLenLTF), 2 * 3 + 1, dashLenLTF, dashGapLTF)) : na, title='LTF EMA52 3', color=color.new(f_paletteColor(2), 30), linewidth=lineWidthLTF, style=plot.style_line)
|
||||||
|
plot((enableLTF and 3 < ltfCountSelected and not na(array.get(ltfVals, 3))) ? f_gate(f_dash(f_smooth(array.get(ltfVals, 3), smoothLenLTF), 3 * 3 + 1, dashLenLTF, dashGapLTF)) : na, title='LTF EMA52 4', color=color.new(f_paletteColor(3), 30), linewidth=lineWidthLTF, style=plot.style_line)
|
||||||
|
|
||||||
|
// 右侧标签(仅在最后一根柱更新,避免重复创建)
|
||||||
|
var label[] htfLabels = array.new_label()
|
||||||
|
var label[] ltfLabels = array.new_label()
|
||||||
|
var label ctfLabel = na
|
||||||
|
var string lastTicker = na
|
||||||
|
var string lastTf = na
|
||||||
|
if barstate.isfirst
|
||||||
|
for _i = 0 to MAX_HTF - 1
|
||||||
|
array.push(htfLabels, na)
|
||||||
|
for _j = 0 to MAX_LTF - 1
|
||||||
|
array.push(ltfLabels, na)
|
||||||
|
lastTicker := syminfo.tickerid
|
||||||
|
lastTf := timeframe.period
|
||||||
|
|
||||||
|
// 更新/清理标签
|
||||||
|
// 如果品种或周期变化,清理旧标签
|
||||||
|
symbolChanged = lastTicker != syminfo.tickerid
|
||||||
|
tfChanged = lastTf != timeframe.period
|
||||||
|
if symbolChanged or tfChanged
|
||||||
|
for i = 0 to MAX_HTF - 1
|
||||||
|
lab = array.get(htfLabels, i)
|
||||||
|
if not na(lab)
|
||||||
|
label.delete(lab)
|
||||||
|
array.set(htfLabels, i, na)
|
||||||
|
for i = 0 to MAX_LTF - 1
|
||||||
|
lab = array.get(ltfLabels, i)
|
||||||
|
if not na(lab)
|
||||||
|
label.delete(lab)
|
||||||
|
array.set(ltfLabels, i, na)
|
||||||
|
if not na(ctfLabel)
|
||||||
|
label.delete(ctfLabel)
|
||||||
|
ctfLabel := na
|
||||||
|
lastTicker := syminfo.tickerid
|
||||||
|
lastTf := timeframe.period
|
||||||
|
|
||||||
|
if barstate.islast
|
||||||
|
// 长级别
|
||||||
|
for i = 0 to MAX_HTF - 1
|
||||||
|
lab = array.get(htfLabels, i)
|
||||||
|
if enableHTF and i < htfCountSelected
|
||||||
|
tfStr = array.get(htfRes, i)
|
||||||
|
v = array.get(htfLabelVals, i)
|
||||||
|
col = f_paletteColor(i)
|
||||||
|
txt = f_prettyTf(tfStr) + ' ' + str.tostring(v, format.mintick)
|
||||||
|
// 与曲线显示条件同步:平滑后且可视区间内才显示标签
|
||||||
|
if na(v)
|
||||||
|
if not na(lab)
|
||||||
|
label.delete(lab)
|
||||||
|
array.set(htfLabels, i, na)
|
||||||
|
else
|
||||||
|
if na(lab)
|
||||||
|
lab := label.new(bar_index + labelOffsetBars, v, txt, xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, textcolor=col, color=color.new(col, labelBgAlpha), size=f_strToSize(labelSize))
|
||||||
|
array.set(htfLabels, i, lab)
|
||||||
|
else
|
||||||
|
label.set_xy(lab, bar_index + labelOffsetBars, v)
|
||||||
|
label.set_text(lab, txt)
|
||||||
|
label.set_textcolor(lab, col)
|
||||||
|
label.set_color(lab, color.new(col, labelBgAlpha))
|
||||||
|
label.set_style(lab, label.style_label_left)
|
||||||
|
label.set_size(lab, f_strToSize(labelSize))
|
||||||
|
else
|
||||||
|
if not na(lab)
|
||||||
|
label.delete(lab)
|
||||||
|
array.set(htfLabels, i, na)
|
||||||
|
|
||||||
|
// 次级别
|
||||||
|
for i = 0 to MAX_LTF - 1
|
||||||
|
lab = array.get(ltfLabels, i)
|
||||||
|
if enableLTF and i < ltfCountSelected
|
||||||
|
tfStr = array.get(ltfSel, i)
|
||||||
|
v = array.get(ltfLabelVals, i)
|
||||||
|
base = f_paletteColor(i)
|
||||||
|
col = color.new(base, 0)
|
||||||
|
txt = f_prettyTf(tfStr) + ' ' + str.tostring(v, format.mintick)
|
||||||
|
if na(v)
|
||||||
|
if not na(lab)
|
||||||
|
label.delete(lab)
|
||||||
|
array.set(ltfLabels, i, na)
|
||||||
|
else
|
||||||
|
if na(lab)
|
||||||
|
lab := label.new(bar_index + labelOffsetBars, v, txt, xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, textcolor=col, color=color.new(col, labelBgAlpha + 10 > 100 ? 100 : labelBgAlpha + 10), size=f_strToSize(labelSize))
|
||||||
|
array.set(ltfLabels, i, lab)
|
||||||
|
else
|
||||||
|
label.set_xy(lab, bar_index + labelOffsetBars, v)
|
||||||
|
label.set_text(lab, txt)
|
||||||
|
label.set_textcolor(lab, col)
|
||||||
|
label.set_color(lab, color.new(col, labelBgAlpha + 10 > 100 ? 100 : labelBgAlpha + 10))
|
||||||
|
label.set_style(lab, label.style_label_left)
|
||||||
|
label.set_size(lab, f_strToSize(labelSize))
|
||||||
|
else
|
||||||
|
if not na(lab)
|
||||||
|
label.delete(lab)
|
||||||
|
array.set(ltfLabels, i, na)
|
||||||
|
|
||||||
|
// 本级别
|
||||||
|
if enableCTF and f_showRes(curResStr)
|
||||||
|
v = ctfLabelVal
|
||||||
|
col = color.new(color.white, 0)
|
||||||
|
txt = f_prettyTf(curResStr) + ' ' + str.tostring(v, format.mintick)
|
||||||
|
if na(v)
|
||||||
|
if not na(ctfLabel)
|
||||||
|
label.delete(ctfLabel)
|
||||||
|
ctfLabel := na
|
||||||
|
else
|
||||||
|
if na(ctfLabel)
|
||||||
|
ctfLabel := label.new(bar_index + labelOffsetBars, v, txt, xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, textcolor=col, color=color.new(col, labelBgAlpha), size=f_strToSize(labelSize))
|
||||||
|
else
|
||||||
|
label.set_xy(ctfLabel, bar_index + labelOffsetBars, v)
|
||||||
|
label.set_text(ctfLabel, txt)
|
||||||
|
label.set_textcolor(ctfLabel, col)
|
||||||
|
label.set_color(ctfLabel, color.new(col, labelBgAlpha))
|
||||||
|
label.set_style(ctfLabel, label.style_label_left)
|
||||||
|
label.set_size(ctfLabel, f_strToSize(labelSize))
|
||||||
|
else
|
||||||
|
if not na(ctfLabel)
|
||||||
|
label.delete(ctfLabel)
|
||||||
|
ctfLabel := na
|
||||||
|
|
||||||
|
// ======================== 备注 ========================
|
||||||
|
// 1) 当前周期通过 timeframe.period 自动识别,并用 timeframe.in_seconds(
|
||||||
|
// ) 转换成秒以进行相对大小比较。
|
||||||
|
// 2) 长级别/次级别集合从标准分辨率表中过滤得出,绘图数量受 MAX_* 与输入上限控制。
|
||||||
|
// 3) 虚线效果通过在 plot 中周期性输出 na 实现,性能优于逐柱绘制 line 对象。
|
||||||
|
// 4) 标签在最后一根柱更新,避免过量对象;颜色与曲线一致。
|
||||||
|
// 5) 次级别(低于当前)EMA52 也计算并显示,更新频率更高,默认更淡。
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user