技术分析

Volume Profile POC/VAH/VAL:Pine v6 让你直接拿

2026-09-22·8 分钟阅读

你盯着 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 新函数的参数到底是什么意思、以及开始用之前你会撞到的三个坑。

先讲结论:Volume Profile POC 是这根 K 棒(或这个时间范围内)成交量最大的价格格;VAH/VAL 是把总成交量往 POC 附近收拢、搜集到 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)长多长,代表这个格子内累积了多少成交量。三个缩写都是从这张直方图抽出来的。

POCVAHVAL
全名Point of ControlValue Area HighValue 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,官方对外开放的函数集)目前的限制。

想拿整段时间、不是单一 K 棒的 profile 该怎么办
官方文件没有给 request.footprint() 一个「跨 N 根 K 棒累积」的参数;要自己叠,就得逐根 K 棒把 reqFootprint.rows() 展开、把同一个价格 row 的 buy/sell 加总——这件事这一版 API 没帮你做。或者直接在图上叠 Fixed Range Volume Profile 那个指标,把数字目测抄下来当常数喂给脚本(不够精细但立刻能跑)。

request.footprint 的三个参数:ticks_per_row、va_percent、imbalance_percent

函数签名很短:

pine
request.footprint(ticks_per_row, va_percent, imbalance_percent) → series footprint

三个参数都是 simple——也就是必须在 script 一开始就决定的常数,不能用 series 值动态塞进来。这个限制在文件里明讲,违反会编译不过。

参数类型预设解释
ticks_per_rowsimple int(必填)row 宽度=ticks_per_row × syminfo.mintick。mintick=0.1 且给 100 就是每格 10 美元宽
va_percentsimple float70要搜集多少百分比的总成交量才算进 Value Area
imbalance_percentsimple float300相邻两格 buy 对 sell 差几倍才算 imbalance,预设 3 倍

request.footprint 的参数。来源:TradingView Pine Script v6「Requesting data from other timeframes and contexts」页的 request.footprint 段(查证日期 2026 年 8 月)。

70%
va_percent 预设值──搜集这么多成交量才停
300%
imbalance_percent 预设值──一边要三倍另一边才算 imbalance
1
整支脚本能调用 request.footprint 的次数上限

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 范例对得起来:

pine
//@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)比较符合「上下边界」的直觉。

先检查 narequest.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 存进 varvarip 也绕不过去——限制在函数调用这一层,不是在 ID 的储存上(varvarip 的机制在 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 当支撑阻力之前,先过一次这个决策

两件事要先想清楚:这个信号代表什么、以及订阅者拿不拿得到。

1
你要抓的是「单一 K 棒内部的公平价」还是「这几天大家累积下来的公平价」?
单一 K 棒request.footprint() 的 POC/VAH/VAL 就对了,这是它擅长的事
累积 N 根逐根 K 棒 footprint.rows() 展开自己叠,或直接在图上叠 Fixed Range Volume Profile 目测数字喂回脚本。这一版 API 没帮你做
2
订阅这支脚本的人用的是什么 TradingView plan?
Premium/Ultimate直接发布
Basic/Pro/Essential拿不到资料。把限制写进脚本描述,或改用 hl2/hlc3 之类跟成交量无关的替代价位

一个实务上比较安全的用法:拿 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 版试会拿到什么?
官方原文:「Accounts with lower-tier plans cannot use scripts that request volume footprint data with the request.footprint() function.」 实务上 request.footprint()na,下游计算全部变 na,图上什么线都画不出来。
footprint 的 POC 跟 Session Volume Profile 指标画出来的 POC 一样吗?
不一样:前者是单一 K 棒内部的最大量价位,后者是整个 session 累积的。24 根小时线各有各的 POC,叠起来才会逼近 Session VP 的 POC。

接进 TVSBot

TVSBot 这一端要做什么配合才能收到 POC 信号?
Pine 算好价位之后,让 alert() 送一份 JSON 过来。TVSBot 目前的 webhook schema 必填是 secretstrategy(后台建的策略 slot)、action(也接受 side 别名,也就是 Pine 惯用的名字)、symbolexchange,非 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 等于最低价,等于 highlow,信号就没意义了。反过来调到 30 会让 VA 太窄,只框住 POC 附近几格,通常无法作为区间边界。
delta 为什么有时候是正的但价格反而下跌?
那是市场结构里称为吸收(absorption)的样子:买方明显比卖方积极、但价格没有跟着上涨,代表卖方在该价位的大单全被接走了。order flow 分析常把这种型态视为「有力卖家在防守」,通常伴随后续下跌——这是行为解读不是必然的因果。

Get started

想把今天学到的东西自动化跑起来?

把 Pine 算出来的 POC/VAH/VAL 信号 webhook 给 TVSBot——用你自己的 API key(也就是交易所给你的访问凭证),先 dry-run,账户层级风控自己设。

免费开始使用