Volume Profile POC/VAH/VAL:Pine v6 让你直接拿
你盯着 K 线发现蜡烛穿到某个看不见的价位就掉头,回头把 Volume Profile 叠上去,才知道刚好卡在昨天的 POC——这个 Volume Profile POC 你其实一直看得到,只是每次都事后诸葛。
以前把它自动化的方式只有两种:拿 request.security_lower_tf() 抓分秒级 tick 再自己凑出成交分布、或是换去 volume footprint 那张特殊 chart 用肉眼判读。Pine Script(也就是 TradingView 内建的策略脚本语言)v6 加了第三种——request.footprint() 让 POC、VAH、VAL 直接变成脚本里可以用的一个数字。
这篇不打算把 Volume Profile 的所有玄学都讨论一遍——像 HVN/LVN 的策略含义、Market Profile 的字母编码、Composite Profile 怎么叠,这篇都跳过。要处理的是三件比较窄的事:POC/VAH/VAL 分别代表什么、request.footprint() 这个 v6 新函数的参数到底是什么意思、以及开始用之前你会撞到的三个坑。
va_percent(预设 70%)为止的最高/最低价格格。Pine v6 的 request.footprint() 让你在脚本里直接调用 footprint.poc()、footprint.vah()、footprint.val() 拿到这三个 volume_row——但只有 Premium 与 Ultimate plan 拿得到,而且整支脚本只能调用一次。先把 Volume Profile POC 讲清楚:POC、VAH、VAL 各自标的是什么
Volume Profile 这张图把时间轴上的成交量重新排列成沿着价格轴的横向长条——每一个价格格(row)长多长,代表这个格子内累积了多少成交量。三个缩写都是从这张直方图抽出来的。
| POC | VAH | VAL | ||
|---|---|---|---|---|
| 全名 | Point of Control | Value Area High | Value Area Low | |
| 定义 | 范围内成交量最大的那一格 | 从 POC 往两边收拢、到 va_percent(预设 70%)为止的最高价格格 | 同上但取最低价格格 | |
| 用途 | 最多人成交的价位、心理锚点 | Value Area 上沿 | Value Area 下沿 |
Value Area 的算法在文件里的说法是「similar to the algorithm used by our Volume Profile indicators」——这是由官方定义推导出的结论,不是 TradingView 白纸黑字写出来的一句话:官方没有明讲「这是 Market Profile 那套一 sigma 的 70% 规则」,只讲预设是 70% 且可以改。看到别的地方写 68% 或 68.27% 那多半是把统计上的一个 sigma 直接套进来——那不是官方数字,能不能对得起实际成交要自己验。
为什么一根蜡烛里就有 POC——footprint 跟 Volume Profile 不是同一张图
一般人讲 Volume Profile 的时候,脑里的图多半是 TradingView 的「Fixed Range Volume Profile」或「Session Volume Profile」——一段时间(一天、一个 session、你选的区间)里累积的分布(Volume Profile 的一般用法在这篇有整理过)。footprint 不是那样:它把「一根蜡烛」自己拆成一个小型的 Volume Profile,这根蜡烛横跨的价格范围被切成一格一格 row,每一格记着这根 K 棒内部的 buy/sell 成交量。
这件事对你自动化的影响是:你在 Pine v6 用 request.footprint() 拿到的 POC,是「这根 K 棒」的 POC,不是「整个交易日」的 POC。要拿日 POC 就把脚本挂在日线上;要拿周 POC 就挂在周线上——换句话说,你要哪个时间范围的 profile,就把脚本挂在哪个时间范围上。这是这一版 API(也就是 Application Programming Interface,官方对外开放的函数集)目前的限制。
request.footprint() 一个「跨 N 根 K 棒累积」的参数;要自己叠,就得逐根 K 棒把 reqFootprint.rows() 展开、把同一个价格 row 的 buy/sell 加总——这件事这一版 API 没帮你做。或者直接在图上叠 Fixed Range Volume Profile 那个指标,把数字目测抄下来当常数喂给脚本(不够精细但立刻能跑)。request.footprint 的三个参数:ticks_per_row、va_percent、imbalance_percent
函数签名很短:
request.footprint(ticks_per_row, va_percent, imbalance_percent) → series footprint三个参数都是 simple——也就是必须在 script 一开始就决定的常数,不能用 series 值动态塞进来。这个限制在文件里明讲,违反会编译不过。
| 参数 | 类型 | 预设 | 解释 |
|---|---|---|---|
ticks_per_row | simple int(必填) | 无 | row 宽度=ticks_per_row × syminfo.mintick。mintick=0.1 且给 100 就是每格 10 美元宽 |
va_percent | simple float | 70 | 要搜集多少百分比的总成交量才算进 Value Area |
imbalance_percent | simple float | 300 | 相邻两格 buy 对 sell 差几倍才算 imbalance,预设 3 倍 |
request.footprint 的参数。来源:TradingView Pine Script v6「Requesting data from other timeframes and contexts」页的 request.footprint 段(查证日期 2026 年 8 月)。
imbalance 的判定公式官方写得很直白:max(buy, sell) ≥ (imbalance_percent / 100) × min(buy, sell)——预设 300 代表「多数那一边要至少是少数那一边的三倍」。所以把 imbalance_percent 调小(例如 150)会抓到更多 imbalance;调大(例如 500)只会留下最极端的那些。
拿 POC / VAH / VAL 的最小可跑脚本
以下是把三个价位画在图上的最小骨架,跟 Pine Script v6 官方 concept 页的 request.footprint 范例对得起来:
//@version=6
indicator("POC / VAH / VAL demo", overlay = true)
int ticksInput = input.int(100, "Ticks per row", minval = 1)
footprint fp = request.footprint(ticksInput)
bool hasData = not na(fp)
// footprint.poc / vah / val 回的是 volume_row ID,还要再过一层拿价格
float pocMid = hasData
? (volume_row.up_price(footprint.poc(fp)) + volume_row.down_price(footprint.poc(fp))) / 2
: na
float vahTop = hasData ? volume_row.up_price(footprint.vah(fp)) : na
float valBot = hasData ? volume_row.down_price(footprint.val(fp)) : na
plot(pocMid, "POC", color.new(color.orange, 0), 2, plot.style_circles)
plot(vahTop, "VAH", color.new(color.aqua, 0))
plot(valBot, "VAL", color.new(color.aqua, 0))关键是 footprint.poc()、footprint.vah()、footprint.val() 回的都是 volume_row ID 而不是价格——真正的价格要再过一层 volume_row.up_price()(该格的上沿)或 volume_row.down_price()(该格的下沿)才拿得到。POC 通常用 mid(上下沿平均)当代表;VAH/VAL 用外侧(VAH 用 up_price、VAL 用 down_price)比较符合「上下边界」的直觉。
先检查 na:request.footprint() 在没 footprint 资料的 K 棒回 na,而 footprint.*() 的 id 参数不接受 na。省掉这个检查会拿到 runtime error 而不是 na——脚本会直接停在那根 K 棒。
三个一开始就要知道的坑
坑一:需要 Premium 或 Ultimate plan
这是硬条件。官方原文:「Volume footprints are available exclusively to users who have a Premium or Ultimate plan.」 你把脚本发给订阅者,如果他不在这两个档位里,图表就是空的——Pine 不会提示为什么,订阅者只会觉得脚本坏了。发布之前把这个限制写进脚本描述。
坑二:整支脚本只能调用一次 request.footprint
多调用一次就是 runtime error。想比较「不同 ticks_per_row 下 POC 会不会跑」这种问题,只能改参数、重跑脚本,一次比不到两组。把 footprint ID 存进 var/varip 也绕不过去——限制在函数调用这一层,不是在 ID 的储存上(var/varip 的机制在 Pine Script 的 Alert 没有记忆那篇有拆过)。
坑三:1D 以上时 total_volume 可能跟 volume 对不上
这个坑很容易安静地坏掉。官方明讲:「On timeframes higher than or equal to '1D', a footprint's total volume might differ significantly from the value of the volume variable.」——原因是 EOD(收盘后)资料可能包含 block trade 与 OTC 成交,而 intraday feed 没有。footprint 走 intraday feed、volume 内建变数走 EOD feed,两边本来就在描述不完全一样的东西。你的策略在小时线/分钟线用得爽,一升到日线可能突然对不上——这种错不会吵,它会安静地坏掉。
拿 POC 当支撑阻力之前,先过一次这个决策
两件事要先想清楚:这个信号代表什么、以及订阅者拿不拿得到。
request.footprint() 的 POC/VAH/VAL 就对了,这是它擅长的事footprint.rows() 展开自己叠,或直接在图上叠 Fixed Range Volume Profile 目测数字喂回脚本。这一版 API 没帮你做一个实务上比较安全的用法:拿 footprint.vah()/footprint.val() 当信号滤网而不是进场点——上一根 K 棒的收盘在 VAH 之上、且 delta 是正的,这根才允许做多。这种用法不押「POC 就是绝对支撑」的强断言,只在 Value Area 边界确认完再让进场条件通过。真要拿 POC 当支撑阻力,记得回测时要把 slippage 拉大——被最多人成交过的价位通常也是最挤的地方,你的单不是第一个到。
诚实的一段:这条资料的上限在哪
request.footprint() 给你的是「这根 K 棒的 footprint」——它不是完整的 order flow 工具链。真正的 order flow 资料,例如逐笔 tick 级的 buy/sell 分类、跨 session 累积的成交分布、market delta 的即时曲线变化,这个 API 都不给。想做这一类分析要嘛回 request.security_lower_tf() 抓分秒级 K 棒自己拼、要嘛换到专门的 order flow 平台。TradingView 没有承诺 request.footprint() 会扩充到那个程度,这是本人依据 v6 目前 API 表面积推论的现状,不是官方白纸黑字的说法。
另一层限制是逐棒 delta 的准确度取决于 volume categorization——TradingView 分类的方式是看每一根 intrabar(也就是这根 K 棒内部的更短时距,例如 1s/1min/60min,随图表 timeframe 而变)的收盘价相对于开盘价:收盘高于开盘视为 buy、低于视为 sell;只有两者相等时,才会 fallback 去比对前一根 intrabar 的收盘价。真实的 order flow 需要 order book 深度才能完全分对,这个近似对大部分 timeframe 够用,但在极快速的行情或流动性不足的标的上会出现分类偏差。这是所有基于 TradingView tape 的 delta 分析共通的天花板。
常见问题
配置与兼容性
我用 Basic/Pro 版试会拿到什么?
request.footprint() 回 na,下游计算全部变 na,图上什么线都画不出来。footprint 的 POC 跟 Session Volume Profile 指标画出来的 POC 一样吗?
接进 TVSBot
TVSBot 这一端要做什么配合才能收到 POC 信号?
alert() 送一份 JSON 过来。TVSBot 目前的 webhook schema 必填是 secret、strategy(后台建的策略 slot)、action(也接受 side 别名,也就是 Pine 惯用的名字)、symbol、exchange,非 close 动作再带 qty;其他栏位(market_type/order_type/qty_type 等)有预设值可省略。Pine 端算的具体价位(POC 那个数字本身)不进 payload——TVSBot 只根据触发判定要不要送单,价位逻辑留在 Pine 那一侧,这是设计上的分工不是暂时没接。信号解读
把 va_percent 从 70 改成 100 会怎么样?
va_percent = 100 代表整根 K 棒的成交量都算进 Value Area——VAH 就等于这根 K 棒的最高价、VAL 等于最低价,等于 high/low,信号就没意义了。反过来调到 30 会让 VA 太窄,只框住 POC 附近几格,通常无法作为区间边界。delta 为什么有时候是正的但价格反而下跌?
Get started
把 Pine 算出来的 POC/VAH/VAL 信号 webhook 给 TVSBot——用你自己的 API key(也就是交易所给你的访问凭证),先 dry-run,账户层级风控自己设。
免费开始使用