Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
+181
@@ -0,0 +1,181 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# UV
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
#uv.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||||
|
.pdm.toml
|
||||||
|
.pdm-python
|
||||||
|
.pdm-build/
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
||||||
|
# Ruff stuff:
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
|
# PyPI configuration file
|
||||||
|
.pypirc
|
||||||
|
|
||||||
|
# Cursor
|
||||||
|
# Cursor is an AI-powered code editor.`.cursorignore` specifies files/directories to
|
||||||
|
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
||||||
|
# refer to https://docs.cursor.com/context/ignore-files
|
||||||
|
.cursorignore
|
||||||
|
.cursorindexingignore
|
||||||
+369
@@ -0,0 +1,369 @@
|
|||||||
|
//@version=6
|
||||||
|
indicator("EMA52横线版 (HTF/LTF 最新值)", overlay=true, max_labels_count=500, max_lines_count=500)
|
||||||
|
|
||||||
|
// ============ 输入 ============
|
||||||
|
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="显示")
|
||||||
|
includeSeconds = input.bool(false, "包含秒级别(需高级套餐)", 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="显示")
|
||||||
|
|
||||||
|
labelBgAlpha = input.int(85, "标签背景透明(0-100)", minval=0, maxval=100, group="标注")
|
||||||
|
labelSize = input.string("tiny", "标签字号", options=["tiny","small","normal","large","huge"], group="标注")
|
||||||
|
labelOffsetBars = input.int(2, "标签右移K线的距离(根数)", minval=0, maxval=50, group="标注")
|
||||||
|
|
||||||
|
// ============ 按线控制(可见性/颜色/宽度/样式) ============
|
||||||
|
// 为满足 Pine 对常量标题的要求,逐一声明。
|
||||||
|
// 长级别
|
||||||
|
groupHTF = "按线控制 - 长级别"
|
||||||
|
hShow1 = input.bool(true, "显示 HTF1", group=groupHTF)
|
||||||
|
hColor1 = input.color(color.new(color.teal, 0), "颜色 HTF1", group=groupHTF)
|
||||||
|
hWidth1 = input.int(1, "线宽 HTF1", minval=1, maxval=4, group=groupHTF)
|
||||||
|
hStyle1 = input.string("dotted", "样式 HTF1", options=["solid","dashed","dotted"], group=groupHTF)
|
||||||
|
hShow2 = input.bool(true, "显示 HTF2", group=groupHTF)
|
||||||
|
hColor2 = input.color(color.new(color.orange, 0), "颜色 HTF2", group=groupHTF)
|
||||||
|
hWidth2 = input.int(1, "线宽 HTF2", minval=1, maxval=4, group=groupHTF)
|
||||||
|
hStyle2 = input.string("dotted", "样式 HTF2", options=["solid","dashed","dotted"], group=groupHTF)
|
||||||
|
hShow3 = input.bool(true, "显示 HTF3", group=groupHTF)
|
||||||
|
hColor3 = input.color(color.new(color.fuchsia,0), "颜色 HTF3", group=groupHTF)
|
||||||
|
hWidth3 = input.int(1, "线宽 HTF3", minval=1, maxval=4, group=groupHTF)
|
||||||
|
hStyle3 = input.string("dotted", "样式 HTF3", options=["solid","dashed","dotted"], group=groupHTF)
|
||||||
|
hShow4 = input.bool(true, "显示 HTF4", group=groupHTF)
|
||||||
|
hColor4 = input.color(color.new(color.aqua, 0), "颜色 HTF4", group=groupHTF)
|
||||||
|
hWidth4 = input.int(1, "线宽 HTF4", minval=1, maxval=4, group=groupHTF)
|
||||||
|
hStyle4 = input.string("dotted", "样式 HTF4", options=["solid","dashed","dotted"], group=groupHTF)
|
||||||
|
hShow5 = input.bool(true, "显示 HTF5", group=groupHTF)
|
||||||
|
hColor5 = input.color(color.new(color.yellow, 0), "颜色 HTF5", group=groupHTF)
|
||||||
|
hWidth5 = input.int(1, "线宽 HTF5", minval=1, maxval=4, group=groupHTF)
|
||||||
|
hStyle5 = input.string("dotted", "样式 HTF5", options=["solid","dashed","dotted"], group=groupHTF)
|
||||||
|
hShow6 = input.bool(true, "显示 HTF6", group=groupHTF)
|
||||||
|
hColor6 = input.color(color.new(color.purple, 0), "颜色 HTF6", group=groupHTF)
|
||||||
|
hWidth6 = input.int(1, "线宽 HTF6", minval=1, maxval=4, group=groupHTF)
|
||||||
|
hStyle6 = input.string("dotted", "样式 HTF6", options=["solid","dashed","dotted"], group=groupHTF)
|
||||||
|
hShow7 = input.bool(true, "显示 HTF7", group=groupHTF)
|
||||||
|
hColor7 = input.color(color.new(color.lime, 0), "颜色 HTF7", group=groupHTF)
|
||||||
|
hWidth7 = input.int(1, "线宽 HTF7", minval=1, maxval=4, group=groupHTF)
|
||||||
|
hStyle7 = input.string("dotted", "样式 HTF7", options=["solid","dashed","dotted"], group=groupHTF)
|
||||||
|
hShow8 = input.bool(true, "显示 HTF8", group=groupHTF)
|
||||||
|
hColor8 = input.color(color.new(color.red, 0), "颜色 HTF8", group=groupHTF)
|
||||||
|
hWidth8 = input.int(1, "线宽 HTF8", minval=1, maxval=4, group=groupHTF)
|
||||||
|
hStyle8 = input.string("dotted", "样式 HTF8", options=["solid","dashed","dotted"], group=groupHTF)
|
||||||
|
hShow9 = input.bool(true, "显示 HTF9", group=groupHTF)
|
||||||
|
hColor9 = input.color(color.new(color.blue, 0), "颜色 HTF9", group=groupHTF)
|
||||||
|
hWidth9 = input.int(1, "线宽 HTF9", minval=1, maxval=4, group=groupHTF)
|
||||||
|
hStyle9 = input.string("dotted", "样式 HTF9", options=["solid","dashed","dotted"], group=groupHTF)
|
||||||
|
hShow10 = input.bool(true, "显示 HTF10", group=groupHTF)
|
||||||
|
hColor10 = input.color(color.new(color.navy, 0), "颜色 HTF10", group=groupHTF)
|
||||||
|
hWidth10 = input.int(1, "线宽 HTF10", minval=1, maxval=4, group=groupHTF)
|
||||||
|
hStyle10 = input.string("dotted", "样式 HTF10", options=["solid","dashed","dotted"], group=groupHTF)
|
||||||
|
hShow11 = input.bool(true, "显示 HTF11", group=groupHTF)
|
||||||
|
hColor11 = input.color(color.new(color.maroon, 0), "颜色 HTF11", group=groupHTF)
|
||||||
|
hWidth11 = input.int(1, "线宽 HTF11", minval=1, maxval=4, group=groupHTF)
|
||||||
|
hStyle11 = input.string("dotted", "样式 HTF11", options=["solid","dashed","dotted"], group=groupHTF)
|
||||||
|
hShow12 = input.bool(true, "显示 HTF12", group=groupHTF)
|
||||||
|
hColor12 = input.color(color.new(color.silver, 0), "颜色 HTF12", group=groupHTF)
|
||||||
|
hWidth12 = input.int(1, "线宽 HTF12", minval=1, maxval=4, group=groupHTF)
|
||||||
|
hStyle12 = input.string("dotted", "样式 HTF12", options=["solid","dashed","dotted"], group=groupHTF)
|
||||||
|
|
||||||
|
// 次级别
|
||||||
|
groupLTF = "按线控制 - 次级别"
|
||||||
|
lShow1 = input.bool(true, "显示 LTF1", group=groupLTF)
|
||||||
|
lColor1 = input.color(color.new(color.teal, 30), "颜色 LTF1", group=groupLTF)
|
||||||
|
lWidth1 = input.int(1, "线宽 LTF1", minval=1, maxval=4, group=groupLTF)
|
||||||
|
lStyle1 = input.string("dotted", "样式 LTF1", options=["solid","dashed","dotted"], group=groupLTF)
|
||||||
|
lShow2 = input.bool(true, "显示 LTF2", group=groupLTF)
|
||||||
|
lColor2 = input.color(color.new(color.orange, 30),"颜色 LTF2", group=groupLTF)
|
||||||
|
lWidth2 = input.int(1, "线宽 LTF2", minval=1, maxval=4, group=groupLTF)
|
||||||
|
lStyle2 = input.string("dotted", "样式 LTF2", options=["solid","dashed","dotted"], group=groupLTF)
|
||||||
|
lShow3 = input.bool(true, "显示 LTF3", group=groupLTF)
|
||||||
|
lColor3 = input.color(color.new(color.fuchsia,30),"颜色 LTF3", group=groupLTF)
|
||||||
|
lWidth3 = input.int(1, "线宽 LTF3", minval=1, maxval=4, group=groupLTF)
|
||||||
|
lStyle3 = input.string("dotted", "样式 LTF3", options=["solid","dashed","dotted"], group=groupLTF)
|
||||||
|
lShow4 = input.bool(true, "显示 LTF4", group=groupLTF)
|
||||||
|
lColor4 = input.color(color.new(color.aqua, 30),"颜色 LTF4", group=groupLTF)
|
||||||
|
lWidth4 = input.int(1, "线宽 LTF4", minval=1, maxval=4, group=groupLTF)
|
||||||
|
lStyle4 = input.string("dotted", "样式 LTF4", options=["solid","dashed","dotted"], group=groupLTF)
|
||||||
|
|
||||||
|
// 样式字符串到 line.style 的映射
|
||||||
|
f_lineStyle(string s) => s == "solid" ? line.style_solid : s == "dashed" ? line.style_dashed : line.style_dotted
|
||||||
|
|
||||||
|
// 取对应序号的配置
|
||||||
|
f_htfShow(int i) => i == 0 ? hShow1 : i == 1 ? hShow2 : i == 2 ? hShow3 : i == 3 ? hShow4 : i == 4 ? hShow5 : i == 5 ? hShow6 : i == 6 ? hShow7 : i == 7 ? hShow8 : i == 8 ? hShow9 : i == 9 ? hShow10 : i == 10 ? hShow11 : hShow12
|
||||||
|
f_htfColor(int i) => i == 0 ? hColor1 : i == 1 ? hColor2 : i == 2 ? hColor3 : i == 3 ? hColor4 : i == 4 ? hColor5 : i == 5 ? hColor6 : i == 6 ? hColor7 : i == 7 ? hColor8 : i == 8 ? hColor9 : i == 9 ? hColor10 : i == 10 ? hColor11 : hColor12
|
||||||
|
f_htfWidth(int i) => i == 0 ? hWidth1 : i == 1 ? hWidth2 : i == 2 ? hWidth3 : i == 3 ? hWidth4 : i == 4 ? hWidth5 : i == 5 ? hWidth6 : i == 6 ? hWidth7 : i == 7 ? hWidth8 : i == 8 ? hWidth9 : i == 9 ? hWidth10 : i == 10 ? hWidth11 : hWidth12
|
||||||
|
f_htfStyle(int i) => f_lineStyle(i == 0 ? hStyle1 : i == 1 ? hStyle2 : i == 2 ? hStyle3 : i == 3 ? hStyle4 : i == 4 ? hStyle5 : i == 5 ? hStyle6 : i == 6 ? hStyle7 : i == 7 ? hStyle8 : i == 8 ? hStyle9 : i == 9 ? hStyle10 : i == 10 ? hStyle11 : hStyle12)
|
||||||
|
|
||||||
|
f_ltfShow(int i) => i == 0 ? lShow1 : i == 1 ? lShow2 : i == 2 ? lShow3 : lShow4
|
||||||
|
f_ltfColor(int i) => i == 0 ? lColor1 : i == 1 ? lColor2 : i == 2 ? lColor3 : lColor4
|
||||||
|
f_ltfWidth(int i) => i == 0 ? lWidth1 : i == 1 ? lWidth2 : i == 2 ? lWidth3 : lWidth4
|
||||||
|
f_ltfStyle(int i) => f_lineStyle(i == 0 ? lStyle1 : i == 1 ? lStyle2 : i == 2 ? lStyle3 : lStyle4)
|
||||||
|
|
||||||
|
// ============ 工具 ============
|
||||||
|
f_strToSize(string s) => switch s
|
||||||
|
"tiny" => size.tiny
|
||||||
|
"small" => size.small
|
||||||
|
"normal" => size.normal
|
||||||
|
"large" => size.large
|
||||||
|
=> size.huge
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
f_isSecondsTf(string tf) =>
|
||||||
|
ln = str.length(tf)
|
||||||
|
ln > 0 and str.substring(tf, ln - 1, ln) == 'S'
|
||||||
|
|
||||||
|
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
|
||||||
|
out := tf
|
||||||
|
out
|
||||||
|
|
||||||
|
// ============ 周期间构建 ============
|
||||||
|
var string[] ALL_RES = array.new_string()
|
||||||
|
if barstate.isfirst and array.size(ALL_RES) == 0
|
||||||
|
array.push(ALL_RES, "1S")
|
||||||
|
array.push(ALL_RES, "5S")
|
||||||
|
array.push(ALL_RES, "15S")
|
||||||
|
array.push(ALL_RES, "30S")
|
||||||
|
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")
|
||||||
|
array.push(ALL_RES, "D")
|
||||||
|
array.push(ALL_RES, "2D")
|
||||||
|
array.push(ALL_RES, "3D")
|
||||||
|
array.push(ALL_RES, "W")
|
||||||
|
array.push(ALL_RES, "2W")
|
||||||
|
array.push(ALL_RES, "M")
|
||||||
|
array.push(ALL_RES, "3M")
|
||||||
|
|
||||||
|
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)
|
||||||
|
if not includeSeconds and f_isSecondsTf(res)
|
||||||
|
continue
|
||||||
|
rsec = timeframe.in_seconds(res)
|
||||||
|
if na(rsec)
|
||||||
|
continue
|
||||||
|
if rsec > curResSec
|
||||||
|
array.push(htfRes, res)
|
||||||
|
else if rsec < curResSec
|
||||||
|
array.push(ltfRes, res)
|
||||||
|
|
||||||
|
var string[] ltfSel = array.new_string()
|
||||||
|
array.clear(ltfSel)
|
||||||
|
ltfsz = array.size(ltfRes)
|
||||||
|
if ltfsz > 0
|
||||||
|
take = math.min(maxLTFInput, 4, ltfsz)
|
||||||
|
for k = 0 to take - 1
|
||||||
|
array.push(ltfSel, array.get(ltfRes, ltfsz - 1 - k))
|
||||||
|
|
||||||
|
htfCountSelected = math.min(array.size(htfRes), maxHTFInput, 12)
|
||||||
|
ltfCountSelected = math.min(array.size(ltfSel), 4)
|
||||||
|
|
||||||
|
// ============ 计算最新 EMA52 ============
|
||||||
|
var float[] htfVals = array.new_float()
|
||||||
|
var float[] ltfVals = array.new_float()
|
||||||
|
if barstate.isfirst
|
||||||
|
for _i = 0 to 11
|
||||||
|
array.push(htfVals, na)
|
||||||
|
for _j = 0 to 3
|
||||||
|
array.push(ltfVals, na)
|
||||||
|
|
||||||
|
for i = 0 to 11
|
||||||
|
float val = na
|
||||||
|
if enableHTF and i < htfCountSelected
|
||||||
|
tfStr = array.get(htfRes, i)
|
||||||
|
val := request.security(syminfo.tickerid, tfStr, ta.ema(close, 52), barmerge.gaps_off, barmerge.lookahead_off)
|
||||||
|
array.set(htfVals, i, val)
|
||||||
|
|
||||||
|
for i = 0 to 3
|
||||||
|
float val = na
|
||||||
|
if enableLTF and i < ltfCountSelected
|
||||||
|
tfStr = array.get(ltfSel, i)
|
||||||
|
val := request.security(syminfo.tickerid, tfStr, ta.ema(close, 52), barmerge.gaps_off, barmerge.lookahead_off)
|
||||||
|
array.set(ltfVals, i, val)
|
||||||
|
|
||||||
|
// ============ 可视区间 ============
|
||||||
|
rangeHi = ta.highest(high, lookbackBars)
|
||||||
|
rangeLo = ta.lowest(low, lookbackBars)
|
||||||
|
rangeSpan = rangeHi - rangeLo
|
||||||
|
pad = rangeSpan * padPercent * 0.01
|
||||||
|
minY = rangeLo - pad
|
||||||
|
maxY = rangeHi + pad
|
||||||
|
|
||||||
|
f_inRange(series float v) => protectAutoscale ? (v >= minY and v <= maxY) : true
|
||||||
|
|
||||||
|
// ============ 绘制水平线与标签(仅最新值) ============
|
||||||
|
var line[] htfLines = array.new_line()
|
||||||
|
var line[] ltfLines = array.new_line()
|
||||||
|
var label[] htfLabels = array.new_label()
|
||||||
|
var label[] ltfLabels = array.new_label()
|
||||||
|
var string lastTicker = na
|
||||||
|
var string lastTf = na
|
||||||
|
if barstate.isfirst
|
||||||
|
for _i = 0 to 11
|
||||||
|
array.push(htfLines, na)
|
||||||
|
array.push(htfLabels, na)
|
||||||
|
for _j = 0 to 3
|
||||||
|
array.push(ltfLines, na)
|
||||||
|
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 11
|
||||||
|
l = array.get(htfLines, i)
|
||||||
|
if not na(l)
|
||||||
|
line.delete(l)
|
||||||
|
array.set(htfLines, i, na)
|
||||||
|
lb = array.get(htfLabels, i)
|
||||||
|
if not na(lb)
|
||||||
|
label.delete(lb)
|
||||||
|
array.set(htfLabels, i, na)
|
||||||
|
for i = 0 to 3
|
||||||
|
l = array.get(ltfLines, i)
|
||||||
|
if not na(l)
|
||||||
|
line.delete(l)
|
||||||
|
array.set(ltfLines, i, na)
|
||||||
|
lb = array.get(ltfLabels, i)
|
||||||
|
if not na(lb)
|
||||||
|
label.delete(lb)
|
||||||
|
array.set(ltfLabels, i, na)
|
||||||
|
lastTicker := syminfo.tickerid
|
||||||
|
lastTf := timeframe.period
|
||||||
|
|
||||||
|
// 更新:在最后一根柱上画从最左端到最右端的水平线(仅一段),并在右侧放标签
|
||||||
|
if barstate.islast
|
||||||
|
// 长级别
|
||||||
|
for i = 0 to 11
|
||||||
|
// 颜色/线宽/样式来自按线控制面板
|
||||||
|
col = f_htfColor(i)
|
||||||
|
v = array.get(htfVals, i)
|
||||||
|
show = enableHTF and i < htfCountSelected and f_htfShow(i) and not na(v) and f_inRange(v)
|
||||||
|
ln = array.get(htfLines, i)
|
||||||
|
lb = array.get(htfLabels, i)
|
||||||
|
if show
|
||||||
|
x1 = bar_index - lookbackBars
|
||||||
|
x2 = bar_index + labelOffsetBars
|
||||||
|
if na(ln)
|
||||||
|
ln := line.new(x1, v, x2, v, xloc=xloc.bar_index, extend=extend.none, color=col, width=f_htfWidth(i), style=f_htfStyle(i))
|
||||||
|
array.set(htfLines, i, ln)
|
||||||
|
else
|
||||||
|
line.set_xy1(ln, x1, v)
|
||||||
|
line.set_xy2(ln, x2, v)
|
||||||
|
line.set_color(ln, col)
|
||||||
|
line.set_width(ln, f_htfWidth(i))
|
||||||
|
line.set_style(ln, f_htfStyle(i))
|
||||||
|
txt = f_prettyTf(array.get(htfRes, i)) + ' ' + str.tostring(v, format.mintick)
|
||||||
|
if na(lb)
|
||||||
|
lb := 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, lb)
|
||||||
|
else
|
||||||
|
label.set_xy(lb, bar_index + labelOffsetBars, v)
|
||||||
|
label.set_text(lb, txt)
|
||||||
|
label.set_textcolor(lb, col)
|
||||||
|
label.set_color(lb, color.new(col, labelBgAlpha))
|
||||||
|
label.set_style(lb, label.style_label_left)
|
||||||
|
label.set_size(lb, f_strToSize(labelSize))
|
||||||
|
else
|
||||||
|
if not na(ln)
|
||||||
|
line.delete(ln)
|
||||||
|
array.set(htfLines, i, na)
|
||||||
|
if not na(lb)
|
||||||
|
label.delete(lb)
|
||||||
|
array.set(htfLabels, i, na)
|
||||||
|
|
||||||
|
// 次级别
|
||||||
|
for i = 0 to 3
|
||||||
|
base = f_ltfColor(i)
|
||||||
|
col = base
|
||||||
|
v = array.get(ltfVals, i)
|
||||||
|
show = enableLTF and i < ltfCountSelected and f_ltfShow(i) and not na(v) and f_inRange(v)
|
||||||
|
ln = array.get(ltfLines, i)
|
||||||
|
lb = array.get(ltfLabels, i)
|
||||||
|
if show
|
||||||
|
x1 = bar_index - lookbackBars
|
||||||
|
x2 = bar_index + labelOffsetBars
|
||||||
|
if na(ln)
|
||||||
|
ln := line.new(x1, v, x2, v, xloc=xloc.bar_index, extend=extend.none, color=col, width=f_ltfWidth(i), style=f_ltfStyle(i))
|
||||||
|
array.set(ltfLines, i, ln)
|
||||||
|
else
|
||||||
|
line.set_xy1(ln, x1, v)
|
||||||
|
line.set_xy2(ln, x2, v)
|
||||||
|
line.set_color(ln, col)
|
||||||
|
line.set_width(ln, f_ltfWidth(i))
|
||||||
|
line.set_style(ln, f_ltfStyle(i))
|
||||||
|
txt = f_prettyTf(array.get(ltfSel, i)) + ' ' + str.tostring(v, format.mintick)
|
||||||
|
if na(lb)
|
||||||
|
lb := 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, lb)
|
||||||
|
else
|
||||||
|
label.set_xy(lb, bar_index + labelOffsetBars, v)
|
||||||
|
label.set_text(lb, txt)
|
||||||
|
label.set_textcolor(lb, col)
|
||||||
|
label.set_color(lb, color.new(col, labelBgAlpha + 10 > 100 ? 100 : labelBgAlpha + 10))
|
||||||
|
label.set_style(lb, label.style_label_left)
|
||||||
|
label.set_size(lb, f_strToSize(labelSize))
|
||||||
|
else
|
||||||
|
if not na(ln)
|
||||||
|
line.delete(ln)
|
||||||
|
array.set(ltfLines, i, na)
|
||||||
|
if not na(lb)
|
||||||
|
label.delete(lb)
|
||||||
|
array.set(ltfLabels, i, na)
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
// MACD与价格背驰检测指标
|
||||||
|
// 作者: Claude AI
|
||||||
|
// 版本: 1.0
|
||||||
|
|
||||||
|
//@version=5
|
||||||
|
indicator("MACD背驰检测", shorttitle="MACD背驰", overlay=true)
|
||||||
|
|
||||||
|
// MACD参数
|
||||||
|
fast_length = input.int(12, "MACD快线长度", minval=1)
|
||||||
|
slow_length = input.int(26, "MACD慢线长度", minval=1)
|
||||||
|
signal_length = input.int(9, "MACD信号线长度", minval=1)
|
||||||
|
|
||||||
|
// 背驰检测参数
|
||||||
|
lookback = input.int(10, "回溯检测周期", minval=5)
|
||||||
|
threshold = input.float(0.5, "背驰阈值", minval=0.1, step=0.1)
|
||||||
|
|
||||||
|
// 计算MACD
|
||||||
|
[macdLine, signalLine, histLine] = ta.macd(close, fast_length, slow_length, signal_length)
|
||||||
|
|
||||||
|
// 寻找价格的高点和低点
|
||||||
|
price_high = ta.highest(high, lookback)
|
||||||
|
price_low = ta.lowest(low, lookback)
|
||||||
|
|
||||||
|
// 判断当前柱是否是局部高点或低点
|
||||||
|
is_price_high = high == price_high and high > high[1] and high > high[2]
|
||||||
|
is_price_low = low == price_low and low < low[1] and low < low[2]
|
||||||
|
|
||||||
|
// 保存价格高点和低点及其对应的MACD值
|
||||||
|
var float last_price_high = na
|
||||||
|
var float last_price_low = na
|
||||||
|
var float last_macd_at_price_high = na
|
||||||
|
var float last_macd_at_price_low = na
|
||||||
|
|
||||||
|
// 寻找MACD的高点和低点
|
||||||
|
macd_high = ta.highest(macdLine, lookback)
|
||||||
|
macd_low = ta.lowest(macdLine, lookback)
|
||||||
|
|
||||||
|
// ----- 背驰检测逻辑 -----
|
||||||
|
|
||||||
|
// 顶背驰:价格创新高,但MACD没有创新高
|
||||||
|
bearish_divergence = false
|
||||||
|
if is_price_high
|
||||||
|
// 保存当前高点及对应的MACD值
|
||||||
|
if not na(last_price_high) and high > last_price_high and macdLine < last_macd_at_price_high
|
||||||
|
bearish_divergence := true
|
||||||
|
|
||||||
|
last_price_high := high
|
||||||
|
last_macd_at_price_high := macdLine
|
||||||
|
|
||||||
|
// 底背驰:价格创新低,但MACD没有创新低
|
||||||
|
bullish_divergence = false
|
||||||
|
if is_price_low
|
||||||
|
// 保存当前低点及对应的MACD值
|
||||||
|
if not na(last_price_low) and low < last_price_low and macdLine > last_macd_at_price_low
|
||||||
|
bullish_divergence := true
|
||||||
|
|
||||||
|
last_price_low := low
|
||||||
|
last_macd_at_price_low := macdLine
|
||||||
|
|
||||||
|
// ----- 标注背驰 -----
|
||||||
|
|
||||||
|
// 顶背驰标注
|
||||||
|
plotshape(
|
||||||
|
bearish_divergence,
|
||||||
|
title="顶背驰",
|
||||||
|
location=location.abovebar,
|
||||||
|
color=color.red,
|
||||||
|
style=shape.triangledown,
|
||||||
|
size=size.normal,
|
||||||
|
text="顶背驰")
|
||||||
|
|
||||||
|
// 底背驰标注
|
||||||
|
plotshape(
|
||||||
|
bullish_divergence,
|
||||||
|
title="底背驰",
|
||||||
|
location=location.belowbar,
|
||||||
|
color=color.green,
|
||||||
|
style=shape.triangleup,
|
||||||
|
size=size.normal,
|
||||||
|
text="底背驰")
|
||||||
|
|
||||||
|
// ----- 辅助指标 -----
|
||||||
|
|
||||||
|
// 在单独窗口绘制MACD
|
||||||
|
plot(macdLine, "MACD", color=color.blue, display=display.none)
|
||||||
|
plot(signalLine, "信号线", color=color.orange, display=display.none)
|
||||||
|
plot(histLine, "柱状图", color=histLine >= 0 ? color.green : color.red, style=plot.style_histogram, display=display.none)
|
||||||
|
|
||||||
|
// 显示背驰信息
|
||||||
|
var label divergenceLabel = na
|
||||||
|
if barstate.islast
|
||||||
|
label.delete(divergenceLabel)
|
||||||
|
if bearish_divergence
|
||||||
|
divergenceLabel := label.new(
|
||||||
|
bar_index, high,
|
||||||
|
text="顶背驰警告!\n价格创新高但MACD未跟进",
|
||||||
|
color=color.red,
|
||||||
|
style=label.style_label_down,
|
||||||
|
textcolor=color.white)
|
||||||
|
else if bullish_divergence
|
||||||
|
divergenceLabel := label.new(
|
||||||
|
bar_index, low,
|
||||||
|
text="底背驰警告!\n价格创新低但MACD未跟进",
|
||||||
|
color=color.green,
|
||||||
|
style=label.style_label_up,
|
||||||
|
textcolor=color.white)
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
// MACD柱子面积计算指标
|
||||||
|
// 作者: Claude AI
|
||||||
|
// 版本: 1.0
|
||||||
|
|
||||||
|
//@version=5
|
||||||
|
indicator("MACD柱子面积", shorttitle="MACD面积", overlay=false)
|
||||||
|
|
||||||
|
// MACD参数
|
||||||
|
fast_length = input.int(12, "快线长度", minval=1)
|
||||||
|
slow_length = input.int(26, "慢线长度", minval=1)
|
||||||
|
signal_length = input.int(9, "信号线长度", minval=1)
|
||||||
|
|
||||||
|
// 计算MACD
|
||||||
|
[macdLine, signalLine, histLine] = ta.macd(close, fast_length, slow_length, signal_length)
|
||||||
|
|
||||||
|
// 初始化面积变量
|
||||||
|
var float upArea = 0.0
|
||||||
|
var float downArea = 0.0
|
||||||
|
|
||||||
|
// 检测柱子正负性并累加面积
|
||||||
|
if histLine > 0
|
||||||
|
upArea := upArea + histLine
|
||||||
|
// 当从负值转为正值时,重置下跌面积
|
||||||
|
if histLine[1] < 0
|
||||||
|
downArea := 0.0
|
||||||
|
else if histLine < 0
|
||||||
|
downArea := downArea + math.abs(histLine)
|
||||||
|
// 当从正值转为负值时,重置上涨面积
|
||||||
|
if histLine[1] > 0
|
||||||
|
upArea := 0.0
|
||||||
|
|
||||||
|
// 绘制结果
|
||||||
|
plot(upArea, "上涨段面积", color=color.green, style=plot.style_area)
|
||||||
|
plot(downArea, "下跌段面积", color=color.red, style=plot.style_area)
|
||||||
|
plot(histLine, "MACD柱状图", color=histLine >= 0 ? color.green : color.red, style=plot.style_histogram)
|
||||||
|
plot(macdLine, "MACD", color=color.blue)
|
||||||
|
plot(signalLine, "信号线", color=color.orange)
|
||||||
|
|
||||||
|
// 添加文字标签
|
||||||
|
var label upLabel = na
|
||||||
|
var label downLabel = na
|
||||||
|
|
||||||
|
if barstate.islast
|
||||||
|
upLabel := label.new(
|
||||||
|
bar_index, upArea,
|
||||||
|
text="上涨面积: " + str.tostring(upArea, "#.##"),
|
||||||
|
color=color.green,
|
||||||
|
style=label.style_label_down,
|
||||||
|
textcolor=color.white)
|
||||||
|
|
||||||
|
downLabel := label.new(
|
||||||
|
bar_index, downArea,
|
||||||
|
text="下跌面积: " + str.tostring(downArea, "#.##"),
|
||||||
|
color=color.red,
|
||||||
|
style=label.style_label_up,
|
||||||
|
textcolor=color.white)
|
||||||
|
|
||||||
|
label.delete(upLabel[1])
|
||||||
|
label.delete(downLabel[1])
|
||||||
+322
@@ -0,0 +1,322 @@
|
|||||||
|
//@version=6
|
||||||
|
indicator("多周期EMA52 (HTF/LTF)", overlay=true, max_labels_count=500, max_lines_count=500)
|
||||||
|
|
||||||
|
// ======================== 配置输入 ========================
|
||||||
|
// 主控
|
||||||
|
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="显示")
|
||||||
|
includeSeconds = input.bool(false, "包含秒级别(需高级套餐)", 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="显示")
|
||||||
|
|
||||||
|
// 视觉(虚线效果)
|
||||||
|
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="线型")
|
||||||
|
labelBgAlpha = input.int(85, "标签背景透明(0-100)", minval=0, maxval=100, group="标注")
|
||||||
|
labelSize = input.string("tiny", "标签字号", 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
|
||||||
|
|
||||||
|
f_strToSize(string s) => switch s
|
||||||
|
"tiny" => size.tiny
|
||||||
|
"small" => size.small
|
||||||
|
"normal" => size.normal
|
||||||
|
"large" => size.large
|
||||||
|
=> size.huge
|
||||||
|
|
||||||
|
// 颜色调色板(循环使用)
|
||||||
|
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)
|
||||||
|
|
||||||
|
// 是否为秒级分辨率,如 "1S","5S"
|
||||||
|
f_isSecondsTf(string tf) =>
|
||||||
|
ln = str.length(tf)
|
||||||
|
ln > 0 and str.substring(tf, ln - 1, ln) == 'S'
|
||||||
|
|
||||||
|
// 计算可视范围边界(近 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"...;
|
||||||
|
// 天: "D","2D";周: "W","2W";月: "M","3M";秒级可能为 "1S","5S" 等。
|
||||||
|
var string[] ALL_RES = array.new_string()
|
||||||
|
if barstate.isfirst and array.size(ALL_RES) == 0
|
||||||
|
// 由低到高,便于筛选
|
||||||
|
// 秒级(若品种/权限不支持,请忽略,筛选时会自动排除)
|
||||||
|
array.push(ALL_RES, "1S")
|
||||||
|
array.push(ALL_RES, "5S")
|
||||||
|
array.push(ALL_RES, "15S")
|
||||||
|
array.push(ALL_RES, "30S")
|
||||||
|
// 分钟
|
||||||
|
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")
|
||||||
|
// 日/周/月
|
||||||
|
array.push(ALL_RES, "D")
|
||||||
|
array.push(ALL_RES, "2D")
|
||||||
|
array.push(ALL_RES, "3D")
|
||||||
|
array.push(ALL_RES, "W")
|
||||||
|
array.push(ALL_RES, "2W")
|
||||||
|
array.push(ALL_RES, "M")
|
||||||
|
array.push(ALL_RES, "3M")
|
||||||
|
|
||||||
|
// ======================== 周期间关系与选择 ========================
|
||||||
|
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)
|
||||||
|
// 非高级用户默认不包含秒级周期
|
||||||
|
if not includeSeconds and f_isSecondsTf(res)
|
||||||
|
continue
|
||||||
|
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()
|
||||||
|
if barstate.isfirst
|
||||||
|
for _i = 0 to MAX_HTF - 1
|
||||||
|
array.push(htfVals, na)
|
||||||
|
for _j = 0 to MAX_LTF - 1
|
||||||
|
array.push(ltfVals, na)
|
||||||
|
|
||||||
|
// 填充长级别数值
|
||||||
|
for i = 0 to MAX_HTF - 1
|
||||||
|
float val = na
|
||||||
|
if enableHTF and i < htfCountSelected
|
||||||
|
tfStr = array.get(htfRes, i)
|
||||||
|
// 计算该长级别的 EMA52
|
||||||
|
val := request.security(syminfo.tickerid, tfStr, ta.ema(close, 52), barmerge.gaps_off, barmerge.lookahead_off)
|
||||||
|
array.set(htfVals, i, val)
|
||||||
|
|
||||||
|
// 填充次级别数值
|
||||||
|
for i = 0 to MAX_LTF - 1
|
||||||
|
float val = na
|
||||||
|
if enableLTF and i < ltfCountSelected
|
||||||
|
tfStr = array.get(ltfSel, i)
|
||||||
|
// 计算该次级别的 EMA52(低级别向上合成,注意其更频繁更新)
|
||||||
|
val := request.security(syminfo.tickerid, tfStr, ta.ema(close, 52), barmerge.gaps_off, barmerge.lookahead_off)
|
||||||
|
array.set(ltfVals, i, val)
|
||||||
|
|
||||||
|
// ======================== 绘制(虚线 + 标签) ========================
|
||||||
|
// 顶层固定 plot(避免在局部/循环中调用 plot)
|
||||||
|
plot((enableHTF and 0 < htfCountSelected) ? f_gate(f_dash(array.get(htfVals, 0), 0 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 1', color=f_paletteColor(0), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 1 < htfCountSelected) ? f_gate(f_dash(array.get(htfVals, 1), 1 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 2', color=f_paletteColor(1), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 2 < htfCountSelected) ? f_gate(f_dash(array.get(htfVals, 2), 2 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 3', color=f_paletteColor(2), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 3 < htfCountSelected) ? f_gate(f_dash(array.get(htfVals, 3), 3 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 4', color=f_paletteColor(3), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 4 < htfCountSelected) ? f_gate(f_dash(array.get(htfVals, 4), 4 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 5', color=f_paletteColor(4), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 5 < htfCountSelected) ? f_gate(f_dash(array.get(htfVals, 5), 5 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 6', color=f_paletteColor(5), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 6 < htfCountSelected) ? f_gate(f_dash(array.get(htfVals, 6), 6 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 7', color=f_paletteColor(6), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 7 < htfCountSelected) ? f_gate(f_dash(array.get(htfVals, 7), 7 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 8', color=f_paletteColor(7), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 8 < htfCountSelected) ? f_gate(f_dash(array.get(htfVals, 8), 8 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 9', color=f_paletteColor(8), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 9 < htfCountSelected) ? f_gate(f_dash(array.get(htfVals, 9), 9 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 10', color=f_paletteColor(9), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 10 < htfCountSelected) ? f_gate(f_dash(array.get(htfVals, 10), 10 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 11', color=f_paletteColor(10), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
plot((enableHTF and 11 < htfCountSelected) ? f_gate(f_dash(array.get(htfVals, 11), 11 * 2, dashLenHTF, dashGapHTF)) : na, title='HTF EMA52 12', color=f_paletteColor(11), linewidth=lineWidthHTF, style=plot.style_line)
|
||||||
|
|
||||||
|
plot((enableLTF and 0 < ltfCountSelected) ? f_gate(f_dash(array.get(ltfVals, 0), 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) ? f_gate(f_dash(array.get(ltfVals, 1), 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) ? f_gate(f_dash(array.get(ltfVals, 2), 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) ? f_gate(f_dash(array.get(ltfVals, 3), 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 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)
|
||||||
|
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(htfVals, i)
|
||||||
|
col = f_paletteColor(i)
|
||||||
|
txt = f_prettyTf(tfStr) + ' ' + str.tostring(v, format.mintick)
|
||||||
|
// 可视区间外则隐藏标签;区间内按真实价格绘制,确保与标尺一致
|
||||||
|
if protectAutoscale and not f_inRange(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(ltfVals, i)
|
||||||
|
base = f_paletteColor(i)
|
||||||
|
col = color.new(base, 0)
|
||||||
|
txt = f_prettyTf(tfStr) + ' ' + str.tostring(v, format.mintick)
|
||||||
|
if protectAutoscale and not f_inRange(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)
|
||||||
|
|
||||||
|
// ======================== 备注 ========================
|
||||||
|
// 1) 当前周期通过 timeframe.period 自动识别,并用 timeframe.in_seconds(
|
||||||
|
// ) 转换成秒以进行相对大小比较。
|
||||||
|
// 2) 长级别/次级别集合从标准分辨率表中过滤得出,绘图数量受 MAX_* 与输入上限控制。
|
||||||
|
// 3) 虚线效果通过在 plot 中周期性输出 na 实现,性能优于逐柱绘制 line 对象。
|
||||||
|
// 4) 标签在最后一根柱更新,避免过量对象;颜色与曲线一致。
|
||||||
|
// 5) 次级别(低于当前)EMA52 也计算并显示,更新频率更高,默认更淡。
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
// 自定义MACD柱子面积计算指标
|
||||||
|
// 作者: Claude AI
|
||||||
|
// 版本: 1.0
|
||||||
|
|
||||||
|
//@version=5
|
||||||
|
indicator("自定义MACD柱子面积", shorttitle="自定义MACD面积", overlay=false)
|
||||||
|
|
||||||
|
// MACD参数
|
||||||
|
fast_length = input.int(12, "快线长度", minval=1)
|
||||||
|
slow_length = input.int(26, "慢线长度", minval=1)
|
||||||
|
signal_length = input.int(9, "信号线长度", minval=1)
|
||||||
|
|
||||||
|
// 面积计算参数
|
||||||
|
reset_on_crossover = input.bool(true, "交叉时重置面积")
|
||||||
|
use_cumulative = input.bool(false, "使用累计总面积")
|
||||||
|
|
||||||
|
// 计算MACD
|
||||||
|
[macdLine, signalLine, histLine] = ta.macd(close, fast_length, slow_length, signal_length)
|
||||||
|
|
||||||
|
// 初始化面积变量
|
||||||
|
var float upArea = 0.0
|
||||||
|
var float downArea = 0.0
|
||||||
|
var float totalUpArea = 0.0
|
||||||
|
var float totalDownArea = 0.0
|
||||||
|
|
||||||
|
// 检测柱子正负性并累加面积
|
||||||
|
if histLine > 0
|
||||||
|
if use_cumulative
|
||||||
|
totalUpArea := totalUpArea + histLine
|
||||||
|
upArea := totalUpArea
|
||||||
|
else
|
||||||
|
upArea := upArea + histLine
|
||||||
|
|
||||||
|
// 当从负值转为正值时,根据设置决定是否重置下跌面积
|
||||||
|
if histLine[1] < 0 and reset_on_crossover
|
||||||
|
downArea := 0.0
|
||||||
|
else if histLine < 0
|
||||||
|
if use_cumulative
|
||||||
|
totalDownArea := totalDownArea + math.abs(histLine)
|
||||||
|
downArea := totalDownArea
|
||||||
|
else
|
||||||
|
downArea := downArea + math.abs(histLine)
|
||||||
|
|
||||||
|
// 当从正值转为负值时,根据设置决定是否重置上涨面积
|
||||||
|
if histLine[1] > 0 and reset_on_crossover
|
||||||
|
upArea := 0.0
|
||||||
|
|
||||||
|
// 绘制结果
|
||||||
|
upColor = color.new(color.green, 50)
|
||||||
|
downColor = color.new(color.red, 50)
|
||||||
|
|
||||||
|
plot(upArea, "上涨段面积", color=upColor, style=plot.style_area)
|
||||||
|
plot(downArea, "下跌段面积", color=downColor, style=plot.style_area)
|
||||||
|
plot(histLine, "MACD柱状图", color=histLine >= 0 ? color.green : color.red, style=plot.style_histogram)
|
||||||
|
plot(macdLine, "MACD", color=color.blue)
|
||||||
|
plot(signalLine, "信号线", color=color.orange)
|
||||||
|
|
||||||
|
// 添加文字标签
|
||||||
|
var label upLabel = na
|
||||||
|
var label downLabel = na
|
||||||
|
|
||||||
|
if barstate.islast
|
||||||
|
upLabel := label.new(
|
||||||
|
bar_index, upArea,
|
||||||
|
text="上涨面积: " + str.tostring(upArea, "#.##"),
|
||||||
|
color=color.green,
|
||||||
|
style=label.style_label_down,
|
||||||
|
textcolor=color.white)
|
||||||
|
|
||||||
|
downLabel := label.new(
|
||||||
|
bar_index, downArea * -1,
|
||||||
|
text="下跌面积: " + str.tostring(downArea, "#.##"),
|
||||||
|
color=color.red,
|
||||||
|
style=label.style_label_up,
|
||||||
|
textcolor=color.white)
|
||||||
|
|
||||||
|
label.delete(upLabel[1])
|
||||||
|
label.delete(downLabel[1])
|
||||||
|
|
||||||
|
// 绘制上涨/下跌面积比率
|
||||||
|
ratio = upArea / (downArea + 0.0001)
|
||||||
|
plot(ratio, "上涨/下跌比率", color=color.purple, linewidth=2)
|
||||||
+208
@@ -0,0 +1,208 @@
|
|||||||
|
// 高级MACD背驰检测指标
|
||||||
|
// 作者: Claude AI
|
||||||
|
// 版本: 1.0
|
||||||
|
|
||||||
|
//@version=5
|
||||||
|
indicator("高级MACD背驰检测", shorttitle="高级MACD背驰", overlay=true)
|
||||||
|
|
||||||
|
// === 输入参数 ===
|
||||||
|
|
||||||
|
// MACD参数
|
||||||
|
fast_length = input.int(12, "MACD快线长度", minval=1, group="MACD设置")
|
||||||
|
slow_length = input.int(26, "MACD慢线长度", minval=1, group="MACD设置")
|
||||||
|
signal_length = input.int(9, "MACD信号线长度", minval=1, group="MACD设置")
|
||||||
|
src = input.source(close, "价格数据源", group="MACD设置")
|
||||||
|
|
||||||
|
// 背驰检测参数
|
||||||
|
lookback = input.int(10, "回溯检测周期", minval=5, group="背驰设置")
|
||||||
|
pivot_confirmation = input.int(3, "高低点确认柱数", minval=1, maxval=5, group="背驰设置")
|
||||||
|
regular_div = input.bool(true, "检测常规背驰", group="背驰类型")
|
||||||
|
hidden_div = input.bool(true, "检测隐形背驰", group="背驰类型")
|
||||||
|
|
||||||
|
// 显示设置
|
||||||
|
show_labels = input.bool(true, "显示文字标签", group="显示设置")
|
||||||
|
show_alerts = input.bool(true, "生成提醒", group="显示设置")
|
||||||
|
bull_color = input.color(color.green, "多头背驰颜色", group="显示设置")
|
||||||
|
bear_color = input.color(color.red, "空头背驰颜色", group="显示设置")
|
||||||
|
|
||||||
|
// === 计算指标 ===
|
||||||
|
|
||||||
|
// 计算MACD
|
||||||
|
[macdLine, signalLine, histLine] = ta.macd(src, fast_length, slow_length, signal_length)
|
||||||
|
|
||||||
|
// === 寻找峰谷 ===
|
||||||
|
|
||||||
|
// 函数:检测高点
|
||||||
|
isPivotHigh(len) =>
|
||||||
|
high[len] > high[len-1] and high[len] > high[len+1] and
|
||||||
|
high[len] > high[len-2] and high[len] > high[len+2]
|
||||||
|
|
||||||
|
// 函数:检测低点
|
||||||
|
isPivotLow(len) =>
|
||||||
|
low[len] < low[len-1] and low[len] < low[len+1] and
|
||||||
|
low[len] < low[len-2] and low[len] < low[len+2]
|
||||||
|
|
||||||
|
// 寻找价格和MACD的峰和谷
|
||||||
|
priceHighs = array.new_float(0)
|
||||||
|
priceLows = array.new_float(0)
|
||||||
|
macdHighs = array.new_float(0)
|
||||||
|
macdLows = array.new_float(0)
|
||||||
|
priceHighsPos = array.new_int(0)
|
||||||
|
priceLowsPos = array.new_int(0)
|
||||||
|
macdHighsPos = array.new_int(0)
|
||||||
|
macdLowsPos = array.new_int(0)
|
||||||
|
|
||||||
|
// 检测最近的高点和低点
|
||||||
|
for i = pivot_confirmation to lookback
|
||||||
|
if isPivotHigh(i)
|
||||||
|
array.push(priceHighs, high[i])
|
||||||
|
array.push(priceHighsPos, bar_index[i])
|
||||||
|
array.push(macdHighs, macdLine[i])
|
||||||
|
array.push(macdHighsPos, bar_index[i])
|
||||||
|
break
|
||||||
|
|
||||||
|
for i = pivot_confirmation to lookback
|
||||||
|
if isPivotLow(i)
|
||||||
|
array.push(priceLows, low[i])
|
||||||
|
array.push(priceLowsPos, bar_index[i])
|
||||||
|
array.push(macdLows, macdLine[i])
|
||||||
|
array.push(macdLowsPos, bar_index[i])
|
||||||
|
break
|
||||||
|
|
||||||
|
// === 背驰检测 ===
|
||||||
|
|
||||||
|
// 常规顶背驰:价格创新高,但MACD没有创新高
|
||||||
|
bearish_regular_divergence = false
|
||||||
|
if regular_div and array.size(priceHighs) >= 2 and array.size(macdHighs) >= 2
|
||||||
|
price_high_current = array.get(priceHighs, 0)
|
||||||
|
price_high_prev = array.get(priceHighs, 1)
|
||||||
|
macd_high_current = array.get(macdHighs, 0)
|
||||||
|
macd_high_prev = array.get(macdHighs, 1)
|
||||||
|
|
||||||
|
if price_high_current > price_high_prev and macd_high_current < macd_high_prev
|
||||||
|
bearish_regular_divergence := true
|
||||||
|
|
||||||
|
// 常规底背驰:价格创新低,但MACD没有创新低
|
||||||
|
bullish_regular_divergence = false
|
||||||
|
if regular_div and array.size(priceLows) >= 2 and array.size(macdLows) >= 2
|
||||||
|
price_low_current = array.get(priceLows, 0)
|
||||||
|
price_low_prev = array.get(priceLows, 1)
|
||||||
|
macd_low_current = array.get(macdLows, 0)
|
||||||
|
macd_low_prev = array.get(macdLows, 1)
|
||||||
|
|
||||||
|
if price_low_current < price_low_prev and macd_low_current > macd_low_prev
|
||||||
|
bullish_regular_divergence := true
|
||||||
|
|
||||||
|
// 隐形顶背驰:价格未创新高,但MACD创新高(上涨趋势中的调整)
|
||||||
|
bearish_hidden_divergence = false
|
||||||
|
if hidden_div and array.size(priceHighs) >= 2 and array.size(macdHighs) >= 2
|
||||||
|
price_high_current = array.get(priceHighs, 0)
|
||||||
|
price_high_prev = array.get(priceHighs, 1)
|
||||||
|
macd_high_current = array.get(macdHighs, 0)
|
||||||
|
macd_high_prev = array.get(macdHighs, 1)
|
||||||
|
|
||||||
|
if price_high_current < price_high_prev and macd_high_current > macd_high_prev
|
||||||
|
bearish_hidden_divergence := true
|
||||||
|
|
||||||
|
// 隐形底背驰:价格未创新低,但MACD创新低(下跌趋势中的反弹)
|
||||||
|
bullish_hidden_divergence = false
|
||||||
|
if hidden_div and array.size(priceLows) >= 2 and array.size(macdLows) >= 2
|
||||||
|
price_low_current = array.get(priceLows, 0)
|
||||||
|
price_low_prev = array.get(priceLows, 1)
|
||||||
|
macd_low_current = array.get(macdLows, 0)
|
||||||
|
macd_low_prev = array.get(macdLows, 1)
|
||||||
|
|
||||||
|
if price_low_current > price_low_prev and macd_low_current < macd_low_prev
|
||||||
|
bullish_hidden_divergence := true
|
||||||
|
|
||||||
|
// === 绘制背驰信号 ===
|
||||||
|
|
||||||
|
// 常规顶背驰标记
|
||||||
|
plotshape(
|
||||||
|
bearish_regular_divergence,
|
||||||
|
title="常规顶背驰",
|
||||||
|
location=location.abovebar,
|
||||||
|
color=bear_color,
|
||||||
|
style=shape.triangledown,
|
||||||
|
size=size.normal,
|
||||||
|
text="顶背驰")
|
||||||
|
|
||||||
|
// 常规底背驰标记
|
||||||
|
plotshape(
|
||||||
|
bullish_regular_divergence,
|
||||||
|
title="常规底背驰",
|
||||||
|
location=location.belowbar,
|
||||||
|
color=bull_color,
|
||||||
|
style=shape.triangleup,
|
||||||
|
size=size.normal,
|
||||||
|
text="底背驰")
|
||||||
|
|
||||||
|
// 隐形顶背驰标记
|
||||||
|
plotshape(
|
||||||
|
bearish_hidden_divergence,
|
||||||
|
title="隐形顶背驰",
|
||||||
|
location=location.abovebar,
|
||||||
|
color=color.new(bear_color, 50),
|
||||||
|
style=shape.xcross,
|
||||||
|
size=size.normal,
|
||||||
|
text="隐顶")
|
||||||
|
|
||||||
|
// 隐形底背驰标记
|
||||||
|
plotshape(
|
||||||
|
bullish_hidden_divergence,
|
||||||
|
title="隐形底背驰",
|
||||||
|
location=location.belowbar,
|
||||||
|
color=color.new(bull_color, 50),
|
||||||
|
style=shape.xcross,
|
||||||
|
size=size.normal,
|
||||||
|
text="隐底")
|
||||||
|
|
||||||
|
// === 显示标签 ===
|
||||||
|
|
||||||
|
if show_labels and barstate.islast
|
||||||
|
if bearish_regular_divergence
|
||||||
|
label.new(
|
||||||
|
bar_index, high,
|
||||||
|
text="常规顶背驰\n价格创新高但MACD未跟进",
|
||||||
|
color=bear_color,
|
||||||
|
style=label.style_label_down,
|
||||||
|
textcolor=color.white)
|
||||||
|
|
||||||
|
if bullish_regular_divergence
|
||||||
|
label.new(
|
||||||
|
bar_index, low,
|
||||||
|
text="常规底背驰\n价格创新低但MACD未跟进",
|
||||||
|
color=bull_color,
|
||||||
|
style=label.style_label_up,
|
||||||
|
textcolor=color.white)
|
||||||
|
|
||||||
|
if bearish_hidden_divergence
|
||||||
|
label.new(
|
||||||
|
bar_index, high,
|
||||||
|
text="隐形顶背驰\n价格未创新高但MACD创新高",
|
||||||
|
color=color.new(bear_color, 50),
|
||||||
|
style=label.style_label_down,
|
||||||
|
textcolor=color.white)
|
||||||
|
|
||||||
|
if bullish_hidden_divergence
|
||||||
|
label.new(
|
||||||
|
bar_index, low,
|
||||||
|
text="隐形底背驰\n价格未创新低但MACD创新低",
|
||||||
|
color=color.new(bull_color, 50),
|
||||||
|
style=label.style_label_up,
|
||||||
|
textcolor=color.white)
|
||||||
|
|
||||||
|
// === 生成提醒 ===
|
||||||
|
|
||||||
|
if show_alerts
|
||||||
|
if bearish_regular_divergence
|
||||||
|
alert("MACD常规顶背驰警告!", alert.freq_once_per_bar_close)
|
||||||
|
|
||||||
|
if bullish_regular_divergence
|
||||||
|
alert("MACD常规底背驰警告!", alert.freq_once_per_bar_close)
|
||||||
|
|
||||||
|
if bearish_hidden_divergence
|
||||||
|
alert("MACD隐形顶背驰警告!", alert.freq_once_per_bar_close)
|
||||||
|
|
||||||
|
if bullish_hidden_divergence
|
||||||
|
alert("MACD隐形底背驰警告!", alert.freq_once_per_bar_close)
|
||||||
Reference in New Issue
Block a user