场内交易员枢轴点指标(Floor Trader Pivots)
交易者们总是在寻找关键的价格水平。这些水平既可以充当阻力,或者在价格果断突破后,成为趋势加速的催化剂。一种估算日内支撑和阻力位的流行方法是使用场内交易员枢轴点(Floor Trader Pivots)。接下来,让我们深入了解这些枢轴点位,并学习如何将它们编写成TradingView指标。
场内交易员枢轴点:日内支撑与阻力的交易之道
对于日内交易和低时间周期的剥头皮策略而言,了解支撑和阻力位在何处至关重要。但我们如何提前预知它们呢?一种广受欢迎的估算方法就是所谓的场内交易员枢轴点。这些枢轴点,也被称为经典枢轴点(classic pivots),利用前一天的收盘价、最高价和最低价,来计算出当日的多个支撑和阻力位。这使其成为一种具有预测性的领先指标。
顾名思义,场内交易员枢轴点最初是由场内交易员——也就是最早的日内交易员——所使用的。在每个交易日开始时,他们会查看前一天的最高价、最低价和收盘价,以此来计算当天交易的枢轴点。
这个核心的枢轴点 (Pivot Point, PP)是当天最重要的价格水平,因为它扮演着多空力量之间的平衡点角色。也就是说,当价格位于PP之上时,市场被认为是看涨的;若价格跌破PP,则当天的方向倾向被视为看跌。这不仅使PP成为主要的支撑或阻力位,也使其成为判断主趋势和指示当天方向倾向的关键点。
PP水平之所以重要的另一个原因是,我们用它的值来计算其他的枢轴点位。让我们看看如何计算。
计算场内交易员枢轴点位
我们按如下方式计算场内交易员枢轴点:
- 枢轴点(P) = (最高价 + 最低价 + 收盘价) / 3
- 阻力位1 (R1) = (P x 2) – 最低价
- 支撑位1 (S1) = (P x 2) – 最高价
- 阻力位2 (R2) = (P – S1) + R1
- 支撑位2 (S2) = P – (R1 – S1)
- 阻力位3 (R3) = (P – S1) + R2
- 支撑位3 (S3) = P – (R2 – S1)
这些公式表明,所有枢轴点的值都(间接地)取决于前一天的价格行为。然而,具体哪个枢轴点位会发挥作用,则取决于当天的波动性。通常我们可以预期价格开盘于S1和R1之间。当日内趋势形成时,第二和第三支撑与阻力位便会进入视野。
使用场内交易员枢轴点的支撑与阻力进行交易
基于核心的PP点,场内交易员枢轴点识别出三个支撑位(S1, S2, S3)和三个阻力位(R1, R2, R3)。但我们该如何在交易中运用这些点位呢?
交易枢轴点主要有两种方法:
- 将枢轴点位视为支撑和阻力价格区域进行交易。
- 在枢轴点位发生突破时进行交易,以期价格向下一个枢轴点位移动。
请注意,这两种方法并不相互排斥。因此,我们可以交易一个枢轴点位的突破,稍后又可以将同一点位用作支撑或阻力区。
我们如何交易枢轴点也取决于市场状况。当价格波动缓慢且没有强劲趋势时,可以对向S1和R1的移动进行逆势操作,预期价格会回归至PP点。
但当存在强劲趋势时,每一次对支撑或阻力的突破都会吸引更多的市场参与者,这为趋势增添了动力,推动日内行情继续发展。随着价格连续突破更高级别的支撑或阻力枢轴点,会有更多的交易者试图从中获利。在这种环境下,我们可以在R1做多,在S1做空,并将R2和R3作为多头的盈利目标,S2和S3则作为空头的盈利目标。
一旦市场对某个特定的枢轴点位形成有效突破,该点位的支撑或阻力角色就会发生反转。例如,当价格突破R1后,一旦价格回落,R1就会变为支撑位。同样,跌破S1会使该点位在价格反弹并再次上涨时成为一个需要克服的阻力。(然而,枢轴点位的名称不会改变:S1仍然是第一支撑位,即使其角色已反转为阻力位。)
将场内交易员枢轴点编写为TradingView脚本
现在,让我们看看如何将场内交易员枢轴点编写成一个TradingView指标脚本。以下是我们在本文中将要实现的功能:
- 场内交易员枢轴点的标准行为是使用昨日的全部价格数据。但许多交易品种有主要的交易时段以及盘前和盘后时段,有些品种甚至接近全天候交易。为了使指标更具灵活性,我们将使用三种数据类型之一:昨日全部数据、特定交易时段的价格数据,或用于更高时间框架分析的周数据。
- 我们将使用TradingView的绘图功能来显示不同的枢轴点位。通过输入选项,我们可以启用或禁用这些水平线,从而配置显示哪些点位。
- 为了帮助识别不同的枢轴点位,我们会在图表上绘制它们的名称标签。
- 为了在价格穿越枢轴点位时收到通知,我们会编写几个TradingView警报。
为了保持结构清晰,我们使用以下模板来编写场内交易员枢轴点指标:
//@version=5
// 步骤一:脚本设置
// 步骤二:计算指标数值
// 步骤三:输出指标数据
// 步骤四:创建指标警报
如果你想跟着本文的代码讨论一起操作,请在TradingView的Pine编辑器中创建一个新的指标脚本,并粘贴以上模板。
为了让你对我们即将编写的代码有个直观的印象,下图是最终完成的场内交易员枢轴点指标在图表上的样子:
现在,就让我们一起开始编写这个TradingView指标吧。
步骤一:设置指标参数与输入选项
在第一步中,我们定义指标的设置和输入选项。为了配置前者,我们调用 indicator() 函数:
// 步骤一:脚本设置
indicator(title="Floor Trader Pivots", overlay=true)
我们在这里定义了两个参数。title 参数用于指定我们脚本的名称。通过将 overlay 设为 true,我们让指标能够直接叠加显示在主图表的交易品种上。
接下来,我们创建几个输入选项,以便能手动地轻松配置脚本的各项参数。我们使用多个函数来创建这些设置,这些函数同时会返回输入项的当前值。当我们将返回的值存储在一个变量中后,便可以在脚本的后续部分通过该变量来访问用户的设置。
第一个设置是关于要使用的枢轴点类型:
// 输入设置
// 为枢轴点类型创建一个下拉菜单
pivotType = input.string("Daily", title="Pivot Type",
options=["Daily", "Intraday", "Weekly"])
我们在这里使用 input.string() 函数创建了一个基于文本的输入项。它的名称(title)是“Pivot Type”。这个输入会生成一个包含三个选项的下拉菜单:“Daily”(每日)、“Intraday”(日内)和“Weekly”(每周)。我们将其默认值设为“Daily”,并使用 pivotType 变量来追踪其当前设置。这个输入项让我们可以配置场内交易员枢轴点所使用的计算周期。
接下来,我们创建几个布尔型(真/假)的输入选项:
// 为各枢轴点位创建显示开关
plotPP = input.bool(true, title="Plot PP")
plotS1R1 = input.bool(true, title="Plot S1 and R1")
plotS2R2 = input.bool(true, title="Plot S2 and R2")
plotS3R3 = input.bool(false, title="Plot S3 and R3")
我们在这里创建的四个输入都是布尔型的输入选项(input.bool())。它们会在脚本的设置面板中生成复选框,用于启用或禁用特定的枢轴点位。我们默认启用了大部分枢轴点位(true),但默认关闭了距离较远的S3和R3点位(false)。我们通过 plotPP、plotS1R1、plotS2R2 和 plotS3R3 这几个输入变量来追踪这些设置的当前值。
然后,我们创建输入项来配置交易时段选项:
// 配置交易时段选项
sessRange = input.session("0800-1600", title="Trading Session")
showSess = input.bool(false, title="Highlight Session?")
我们在这里创建的第一个输入,“Trading Session”,是使用 input.session() 函数创建的一个交易时段输入选项。这个选项允许我们输入一个开始和结束时间。我们将其默认时段设为从早上8点到下午16点("0800-1600")。我们使用 sessRange 变量来追踪该输入的当前设置。
通过下一个输入,“Highlight Session?”(高亮显示交易时段?),我们创建了一个布尔型输入选项(input.bool())。我们用它来配置指标是否应该为我们自定义的交易时段内的K线添加背景色。这有助于将这些K线与其他K线区分开来,从而可以轻松地看出哪些价格行为被纳入了枢轴点位的计算。
我们创建的最后一个输入是另一个布尔型复选框:
// 启用或禁用枢轴点标签
showLabels = input.bool(false, title="Show Labels?")
这个“Show Labels?”(显示标签?)的输入项定义了我们是否希望在图表上显示各枢轴点位的名称标签。我们默认禁用此功能(false)。
顺便一提,我们创建的这些输入选项看起来是这样的:
步骤二:计算指标数值
在编写场内交易员枢轴点指标的下一步中,我们将计算各种数值。这一步大致需要做两件事:首先,获取每日、每周和特定交易时段的最高价、最低价和收盘价;然后,使用之前的公式计算枢轴点位。
2.1)获取日线和周线的最高价、最低价和收盘价数据
为了能更方便地获取日线和周线数据,我们首先定义一个自定义函数:
// 步骤二:计算指标数值
// 创建一个函数来获取日线和周线数据
GetData(res, data) =>
request.security(syminfo.tickerid, res, data[1],
lookahead=barmerge.lookahead_on)
这个自定义函数实际上是TradingView request.security() 函数的一个简化封装。这样我们可以预设该函数的部分参数,从而在后续编写更少的代码。request.security() 的作用是从不同于当前脚本运行的交易品种和/或时间周期加载数据。
我们的自定义函数有两个参数:res 指定要加载的数据的时间周期(分辨率),data 则定义了要获取哪种数据。
在函数内部,我们用四个参数调用 security():
- 第一个参数
syminfo.tickerid返回当前图表交易品种的唯一标识符,这确保我们始终加载与图表完全相同的品种数据。 - 第二个参数
res是我们调用GetData()自定义函数时传入的值,它定义了要加载数据的时间周期。 - 第三个参数
data[1]使用了data参数,但通过历史引用运算符[1]向前回溯了一根K线。这样,我们获取的是前一个交易日的最高价、最低价和收盘价,而非当天尚未完成的数据。 - 最后一个参数
lookahead被设为barmerge.lookahead_on,这确保了函数在处理实时数据时的行为与处理历史数据时完全一致。(否则,security()会窥视未来的实时价格,而我们计算枢轴点只需要昨日的已完成数据。)
创建好自定义函数后,我们用它来获取日线和周线数据:
// 获取日线和周线的价格数据
dailyHigh = GetData("D", high)
dailyLow = GetData("D", low)
dailyClose = GetData("D", close)
weeklyHigh = GetData("W", high)
weeklyLow = GetData("W", low)
weeklyClose = GetData("W", close)
我们在这里创建的三个日线变量(dailyHigh, dailyLow, dailyClose)通过 GetData() 函数获取它们的值。我们用字符串 "D" 来调用函数,指定加载日线数据。然后,我们将 high、low 或 close 作为第二个参数传入,以获取该日线周期对应的数据类型。
三个周线数据变量(weeklyHigh, weeklyLow, weeklyClose)也以同样的方式获取数据,只是这次我们将第一个参数设为 "W" 来获取周线价格数据。
2.2)获取特定交易时段的最高价、最低价和收盘价
接下来,我们编写代码来找出自定义交易时段内的最高价、最低价和收盘价。但要获取这些数据,我们首先必须确定哪些K线属于我们定义的时段。为此,我们创建三个变量:
// 确定时段枢轴点数据
// 首先判断K线与交易时段的时间关系
inSession = not na(time(timeframe.period, sessRange)[1])
sessStart = inSession and not inSession[1]
sessEnd = not inSession and inSession[1]
第一个变量 inSession,当当前K线位于我们之前通过“Trading Session”输入项定义的时段内时,其值为 true。给这个变量赋值的这行代码看起来相当复杂,但让我们从内到外地来解析它。
首先,我们调用 time() 函数。第一个参数 timeframe.period 是一个TradingView内置变量,返回图表的当前时间周期。第二个参数 sessRange 是我们的输入变量,存储了“Trading Session”输入项的值(默认从8:00到16:00)。
我们在 time() 后面加上了历史引用 [1]。这样我们看的是已完成的K线所覆盖的时间范围,而不是K线的开盘时间。(也就是说,一个在8:00收盘的30分钟K线,其数据并不包含8点开始的交易,而8:30的K线才是8:00-16:00时段内的第一根。)
time() 函数的作用是:如果K线在指定时段内,则返回该K线的时间;如果不在,则返回 na(不可用)值。但我们想要的是一个简单的 true 或 false 值,来直接表明当前K线是否在时段内。
为了得到这个布尔值,我们将 time() 的结果用 na() 函数包裹。当 time() 返回 na 时,na() 返回 true,反之返回 false。这样,当K线在时段外时我们得到 true,在时段内时得到 false。
然而,这与我们的目标正好相反。因此,我们在 na() 前面加上 not 逻辑运算符。not 会返回其后表达式的逻辑相反值。这样,我们最终在 inSession 变量中得到了一个准确的布尔值:K线在时段内时为 true,否则为 false。
幸运的是,我们创建的另外两个变量要直接得多。第一个,sessStart,通过判断当前K线的 inSession 值为 true 而前一根K线为 false(即 not inSession[1])来确定当前K线是否为交易时段的第一根K线。
另一个变量,sessEnd,则判断当前K线是否不在自定义时段内(not inSession)而前一根K线在 (inSession[1])。这样,sessEnd 只在时段结束后的第一根K线上为 true。(sessStart 和 sessEnd 稍后都将被用于计算时段的高、低、收盘价。)
说到高、低、收盘价数据,接下来我们就为自定义时段确定这些数据点:
// 确定时段内的价格数据
sessHigh = 0.0
sessLow = 0.0
sessClose = 0.0
sessHigh := sessStart ? high :
inSession ? math.max(high, sessHigh[1]) : na
sessLow := sessStart ? low :
inSession ? math.min(low, sessLow[1]) : na
sessClose := sessEnd ? close[1] : na
我们首先声明了三个浮点变量:sessHigh、sessLow 和 sessClose。然后,我们使用条件运算符(?:)来根据不同情况更新它们的值。
为了设置 sessHigh,第一个条件运算符会判断 sessStart。如果当前是时段的第一根K线,我们知道时段的最高价就是这根K线的最高价(high)。
如果不是第一根K线,第二个条件运算符会判断 inSession。如果当前K线仍在时段内,我们就使用 math.max(high, sessHigh[1]) 来更新最高价,即取当前K线的最高价和迄今为止的最高价中的较大者。这样,sessHigh 只在创出新高时才会被更新。
如果K线已在时段之外,sessHigh 的值则为 na。这有效地关闭了在时段外的数据收集。
sessLow 变量的赋值逻辑非常相似,只是它使用 math.min() 来追踪时段内的最低价。
最后一个变量 sessClose 的值由一个条件运算符决定。当 sessEnd 为 true 时(即当前是时段结束后的第一根K线),我们将 sessClose 设为前一根K线的收盘价(close[1]),这正是我们需要的时段收盘价。在其他情况下,其值均为 na。
上面创建的三个变量持有的是当前时段的数据。但场内交易员枢轴点是基于前一个时段的数据计算的。因此,我们还需要创建三个额外的变量来持有前一个时段的数据:
// 计算前一个日内交易时段的高、低、收盘价
highPrevSess = 0.0
lowPrevSess = 0.0
closePrevSess = 0.0
highPrevSess := fixnan(sessHigh)
lowPrevSess := fixnan(sessLow)
closePrevSess := fixnan(sessClose)
if not sessEnd
highPrevSess := highPrevSess[1]
lowPrevSess := lowPrevSess[1]
closePrevSess := closePrevSess[1]
我们首先创建了三个浮点变量:highPrevSess、lowPrevSess 和 closePrevSess。
然后,我们使用 fixnan() 函数来更新它们的值。该函数的作用是向前填充,即用最近一个非 na 的值来填充当前的 na 值。对于我们的代码来说,这意味着 fixnan() 会抓取到上一个交易时段结束时 sessHigh、sessLow 和 sessClose 的最终有效值。
接下来,一个 if 语句会判断 sessEnd 是否为 false。如果是,我们就将这三个变量更新为它们在前一根K线上的值。这使得这些变量的值能够保持不变,直到下一个交易时段结束并提供新的数据。这种保持恒定的技巧,让我们可以方便地在当前交易时段的每一根K线上使用前一时段的数据来计算枢轴点。
2.3)使用日线、周线还是特定时段的数据?
接下来,我们编写TradingView代码来决定是应该绘制每日、每周还是特定时段的场内交易员枢轴点。为此,我们创建了三个变量(theHigh, theLow, theClose)。这些变量的值将取决于我们之前创建的“Pivot Type”输入选项。
代码如下所示:
// 现在,确定使用哪种价格数据
// 来进行枢轴点计算
theHigh = if pivotType == "Daily"
dailyHigh
else if pivotType == "Intraday"
highPrevSess
else
weeklyHigh
theLow = if pivotType == "Daily"
dailyLow
else if pivotType == "Intraday"
lowPrevSess
else
weeklyLow
theClose = if pivotType == "Daily"
dailyClose
else if pivotType == "Intraday"
closePrevSess
else
weeklyClose
我们通过一系列 if 语句来设定这些变量的值。对于 theHigh 变量,我们首先检查 pivotType 输入变量是否等于(==) "Daily"。如果是,即用户在下拉菜单中选择了“Daily”,我们就将 theHigh 设为我们之前计算出的 dailyHigh 变量。
如果第一个 if 条件不满足,Pine Script会测试下一个 if 条件。这里我们判断 pivotType 是否等于 "Intraday"。如果是,我们就将 theHigh 设为计算出的特定时段最高价(highPrevSess)。
如果第二个条件仍不满足,我们就知道“Pivot Type”输入选项必然被设为了“Weekly”(因为下拉菜单中没有其他选项)。因此,我们将 theHigh 变量的值设为 weeklyHigh,即我们之前存储的周线最高价。
我们为 theLow 和 theClose 变量赋值的过程完全相同。我们依次检查“Pivot Type”的设置,并相应地使用日线、特定时段或周线的最低价和收盘价。
2.4)计算场内交易员枢轴点位
现在我们已经获取了所需的高、低、收盘价数据,终于可以计算不同的场内交易员枢轴点位了。方法如下:
// 最后,计算枢轴点位
pp = (theHigh + theLow + theClose) / 3
r1 = (pp * 2) - theLow
s1 = (pp * 2) - theHigh
r2 = (pp - s1) + r1
s2 = pp - (r1 - s1)
r3 = (pp - s1) + r2
s3 = pp - (r2 - s1)
这段代码非常直接:我们为每个枢轴点位(pp, r1, s1, r2, s2, r3, s3)都创建了一个变量。每个变量的值都基于我们在本文前面讨论过的相同公式进行计算。计算中使用的高、低、收盘价数据则是我们刚才确定的 theHigh、theLow 和 theClose 变量。
步骤三:绘制指标的数据与信号
在编写场内交易员枢轴点指标的第三步中,我们将绘制脚本的数据。这一步我们要在图表上显示三样东西:首先是枢轴价格水平本身;其次是一个可选的、带有水平名称的标签(如“S1”);第三,在需要时,高亮显示落在自定义交易时段内的K线。
3.1)在图表上绘制场内交易员枢轴点位
首先,我们来绘制不同的场内交易员枢轴点价格水平:
// 步骤三:输出指标数据
// 绘制各个枢轴点位
plot(plotS3R3 ? r3 : na, title="R3", style=plot.style_circles,
linewidth=2, color=color.fuchsia)
plot(plotS2R2 ? r2 : na, title="R2", style=plot.style_circles,
linewidth=2, color=#1E90FF)
plot(plotS1R1 ? r1 : na, title="R1", style=plot.style_circles,
linewidth=2, color=#EEC900)
plot(plotPP ? pp : na, title="PP", style=plot.style_circles,
linewidth=2, color=#48D1CC)
plot(plotS1R1 ? s1 : na, title="S1", style=plot.style_circles,
linewidth=2, color=#EEC900)
plot(plotS2R2 ? s2 : na, title="S2", style=plot.style_circles,
linewidth=2, color=#1E90FF)
plot(plotS3R3 ? s3 : na, title="S3", style=plot.style_circles,
linewidth=2, color=color.fuchsia)
这个代码片段中的每个语句都执行TradingView的 plot() 函数在图表上绘制一个价格水平。虽然代码看起来很多,但每一行的作用基本相同,只是绘制的价格水平不同。
让我们分析其中一个 plot() 语句,来了解它们共同的工作原理:
plot(plotS3R3 ? r3 : na, title="R3", style=plot.style_circles,
linewidth=2, color=color.fuchsia)
这里我们调用 plot() 函数来绘制一个圆圈图(style=plot.style_circles)。我们是否绘制以及绘制什么值,取决于一个条件。为此,条件运算符(?:)会评估 plotS3R3 这个输入变量。
当该变量为 true 时(即用户启用了“Plot S3 and R3”的输入选项),条件运算符会返回 r3 变量的值,然后 plot() 函数会将这些值绘制在图表上。如果该输入选项被禁用,运算符则返回 na(其效果是禁用该绘图)。这使得我们只在相应的输入选项被启用时,才在图表上显示R3的价格。
通过 plot() 的 linewidth 参数,我们让圆圈比默认的更粗一些。颜色我们使用 color.fuchsia。通过 title 参数,我们为绘图指定一个描述性的名称。
所有其他的 plot() 函数调用都遵循相同的模式,区别仅在于被绘制的值、颜色、绘图名称,以及在绘制前所检查的输入变量。
3.2)为枢轴点位创建名称标签
接下来,我们在图表上显示场内交易员枢轴点的标签。但在执行之前,我们首先要确定何时绘制这些标签:
// 如果适用,在图表上显示枢轴点名称
newPivots = showLabels == false ? false :
pivotType == "Intraday" ? sessEnd :
pivotType == "Daily" ? dayofmonth != dayofmonth[1] :
dayofweek == dayofweek.monday and dayofmonth != dayofmonth[1]
我们计划在枢轴点更新的第一根K线上绘制标签。因此,对于每日枢轴点,那就是当天的第一根K线;对于每周枢轴点,是周一早上的第一根K线;对于日内时段枢轴点,则是该交易时段结束后的第一根K线。我们在这里创建的 newPivots 变量将只在那个“枢轴点更新K线”上为 true,而在所有其他K线上为 false。
为了设定 newPivots 的值,我们使用了一系列嵌套的条件运算符(?:)。第一个条件运算符检查 showLabels 输入变量是否等于 false。如果是,即用户关闭了“Show Labels?”选项,我们就直接将 newPivots 设为 false,不绘制任何标签。
我们首先检查这个输入变量还有一个原因:当 showLabels 为 false 时,第一个条件运算符返回 false 并结束此语句的执行。这意味着后续的条件运算符只在 showLabels 为 true 时才会执行,这样我们就无需在后面的逻辑中重复检查这个变量了。
第二个条件运算符(?:)检查 pivotType 输入变量是否等于 "Intraday"。如果是,条件运算符会返回 sessEnd 的值。我们之前已将这个布尔变量设为只在交易时段结束后的第一根K线上为 true。这根K线正是我们计算新枢轴点位的K线,因此也应该在这里显示标签。
第三个条件运算符评估 pivotType 是否等于 "Daily"。如果是,我们就比较当前K线的月份日期(dayofmonth)是否不等于(!=)上一根K线的日期(dayofmonth[1])。这个条件只在一个日历天的第一根K线上成立,那也正是我们绘制新的每日枢轴点的第一根K线。
如果 pivotType 也不是 "Daily",我们就知道它必然被设为了“Weekly”。在这种情况下,我们检查当前K线是否是周一(dayofweek == dayofweek.monday)的当天第一根K线(dayofmonth != dayofmonth[1])。如果是,那就是计算新枢轴点位(并显示标签)的时刻。
现在我们知道了何时显示枢轴点标签,就可以在图表上显示它们了。我们使用以下代码来实现:
plotchar(newPivots and plotS3R3 ? r3 : na,
char='', text="R3", offset=1,
location=location.absolute,
color=color.fuchsia, title="R3 label")
plotchar(newPivots and plotS2R2 ? r2 : na,
char='', text="R2", offset=1,
location=location.absolute,
color=#1E90FF, title="R2 label")
plotchar(newPivots and plotS1R1 ? r1 : na,
char='', text="R1", offset=1,
location=location.absolute,
color=#EEC900, title="R1 label")
plotchar(newPivots and plotS1R1 ? s1 : na,
char='', text="S1", offset=1,
location=location.absolute,
color=#EEC900, title="S1 label")
plotchar(newPivots and plotS2R2 ? s2 : na,
char='', text="S2", offset=1,
location=location.absolute,
color=#1E90FF, title="S2 label")
plotchar(newPivots and plotS3R3 ? s3 : na,
char='', text="S3", offset=1,
location=location.absolute,
color=color.fuchsia, title="S3 label")
这里有6个 plotchar() 函数调用,但它们之间的差异很小。让我们分析其中一个来了解全部工作原理:
plotchar(newPivots and plotS3R3 ? r3 : na,
char='', text="R3", offset=1,
location=location.absolute,
color=color.fuchsia, title="R3 label")
plotchar() 函数的作用是当其第一个参数的值为 true 或正数时,在图表上显示一个字符。这里我们使用条件运算符(?:)将该参数设为两个值之一。
我们评估的条件是 newPivots 和 plotS3R3 这两个变量是否都为 true。前者在当前K线是新的枢轴点计算周期开始的第一根K线时为 true。后者则来自一个输入选项,该选项使得禁用S3和R3枢轴点成为可能。当这些点位被关闭时,我们自然不应该在图表上绘制“R3”的文本,因此我们在这里检查该输入变量。
当两个变量都为 true 时,条件运算符会返回 r3 变量。这使得在R3的价格水平上绘制文本标签成为可能。否则,运算符会返回 na,这就会禁用在枢轴点值没有变化的其他K线上的文本显示。
plotchar() 的 char 参数用于指定要绘制哪个字符。但我们并不想绘制单个字符,所以我们将此参数设为空字符串 ''。取而代之的是,我们将函数的 text 参数设为 "R3",这会在图表上打印出“R3”。
为了让文本出现在R3枢轴点的价格上,我们将 plotchar() 的 location 参数设为 location.absolute。使用该值后,plotchar() 的第一个参数就指定了文本应该显示的y轴价格坐标。我们之前在这里使用了 r3 变量,因此文本就会出现在该枢轴点位上。
通过 offset 参数,我们将文本向右移动了一根K线。这使得“R3”这两个字母的标签在图表上轮廓更清晰。我们使用 color 参数让文本以 color.fuchsia 的颜色显示,这与我们之前用 plot() 函数绘制R3枢轴线时使用的颜色相同。
我们以同样的方式使用 plotchar() 函数来绘制其他的枢轴点标签,当然,除了明显的区别之外:每个函数调用都使用了不同的文本标签、文本位置、颜色,并检查了另一个输入变量。
3.3)在图表上高亮显示自定义交易时段
第三步的最后一部分代码,是高亮显示那些落在自定义日内交易时段内的K线。代码如下:
// 在图表上高亮日内价格数据时段
bgcolor(showSess and inSession and pivotType == "Intraday" ?
color.new(color.orange, 95) : na)
bgcolor() 函数会从上到下高亮显示K线的背景。通常它会为每根K线都着色,但在这里我们使用了一个条件,来只高亮我们感兴趣的K线。
我们在这里通过条件运算符(?:)评估的条件包含三个表达式。第一个是 showSess,这个输入变量在用户启用了“Highlight Session?”选项时为 true,关闭时则为 false。
第二个表达式是 inSession。我们之前编写过该变量,使其只在K线位于我们用“Trading Session”输入项定义的时段内时才为 true。最后一个表达式则判断 pivotType 输入变量是否等于 "Intraday"。如果我们没有将枢轴点类型设为显示特定时段的枢轴点,那么高亮显示哪些K线属于该时段也就没有意义了。
我们使用 and 逻辑运算符将这三个表达式组合在一起,因此它们必须全部为 true,整个条件才为 true。当条件满足时,条件运算符会返回橙色(color.orange)供 bgcolor() 使用,我们通过 color.new() 函数为这个颜色设置了95%的透明度。否则,运算符会返回 na,这个值会禁用我们不想高亮的那些K线的背景色。
步骤四:为TradingView指标添加警报条件
在第四步也是最后一步中,我们创建各种警报条件。下面是我们为场内交易员枢轴点指标创建警报的方法:
// 步骤四:创建指标警报
alertcondition(condition=ta.cross(close, s3),
title="Pivot S3 Cross",
message="Prices crossed Pivot S3 level")
alertcondition(condition=ta.cross(close, s2),
title="Pivot S2 Cross",
message="Prices crossed Pivot S2 level")
alertcondition(condition=ta.cross(close, s1),
title="Pivot S1 Cross",
message="Prices crossed Pivot S1 level")
alertcondition(condition=ta.cross(close, pp),
title="Pivot PP Cross",
message="Prices crossed the main Pivot Point level")
alertcondition(condition=ta.cross(close, r1),
title="Pivot R1 Cross",
message="Prices crossed Pivot R1 level")
alertcondition(condition=ta.cross(close, r2),
title="Pivot R2 Cross",
message="Prices crossed Pivot R2 level")
alertcondition(condition=ta.cross(close, r3),
title="Pivot R3 Cross",
message="Prices crossed Pivot R3 level")
我们使用 alertcondition() 函数来创建指标的警报条件。这里我们为每个枢轴价格水平都创建了一个警报条件,但它们的逻辑都非常相似,所以我们只讨论其中一个:
alertcondition(condition=ta.cross(close, s3),
title="Pivot S3 Cross",
message="Prices crossed Pivot S3 level")
我们在这里定义了 alertcondition() 的三个参数。第一个,condition,指定了警报应该在何时触发。这里我们使用TradingView的 ta.cross() 函数来判断K线的收盘价(close)是否穿过了S3的价格水平(s3)。当K线上穿或下穿该枢轴点位时,cross() 函数会返回 true(警报随之触发)。
通过 alertcondition() 的 title 参数,我们为警报指定一个描述性的名称,这有助于在“创建警报”窗口中将其与其他警报区分开。message 参数则指定了当警报触发时显示的默认消息。
注意
我们在指标中编写的警报必须在它们能够触发之前手动启用。也就是说,仅仅执行 alertcondition() 函数并不足以触发警报。否则警报不会生效。
在TradingView图表上绘制场内交易员枢轴点指标
现在我们已经为TradingView编写了场内交易员枢轴点指标,让我们来看看它在图表上的实际表现。下方的亚马逊15分钟图显示了带有R2到S2点位的指标:
在该月的6号和8号,亚马逊的股价在PP和S1这两个日内枢轴点位附近找到了显著支撑。在这几天里,R2点位限制了日内上升趋势的顶部,价格在触及该点位后便回撤至PP点。然而,在5号和7号,价格果断地突破了S2和R2,开启了当天的单边趋势。当价格收盘于R2之上时做多,会是一笔相当可观的盈利交易。
下面的图表显示了一张带有周线级别场内交易员枢轴点的苹果公司60分钟图:
在这里,价格大部分时间都维持在R1到S1的区间内运行。当价格确实移动到该区间之外时,R2点位充当了重要的阻力,并阻止了价格的进一步上涨。
下图是将场内交易员枢轴点指标应用于比特币(BTC/USD)15分钟图表的效果:
这里的每日枢轴点很好地识别出了关键的支撑和阻力位。价格开盘后在PP点附近交易。一旦该点位被跌破,价格便在S1找到了支撑。在企稳后,BTC/USD试图重新夺回PP点位,但未能触及。这次失败的尝试之后立刻伴随着更大的卖压,价格相继跌破S1,甚至一度短暂跌破S2。此后,价格再次企稳,这次则位于S2点位正上方。
这最后一个例子表明,场内交易员枢轴点甚至在那些于其原始交易方法诞生时还不存在的交易品种上(如加密货币),也同样有效。
完整代码:适用于TradingView的场内交易员枢轴点指标
场内交易员枢轴点指标的完整代码如下。关于代码的细节和解释,请参阅上文的讨论。
//@version=5
// 步骤一:脚本设置
indicator(title="Floor Trader Pivots", overlay=true)
// 输入设置
// 为枢轴点类型创建一个下拉菜单
pivotType = input.string("Daily", title="Pivot Type",
options=["Daily", "Intraday", "Weekly"])
// 为各枢轴点位创建显示开关
plotPP = input.bool(true, title="Plot PP")
plotS1R1 = input.bool(true, title="Plot S1 and R1")
plotS2R2 = input.bool(true, title="Plot S2 and R2")
plotS3R3 = input.bool(false, title="Plot S3 and R3")
// 配置交易时段选项
sessRange = input.session("0800-1600", title="Trading Session")
showSess = input.bool(false, title="Highlight Session?")
// 启用或禁用枢轴点标签
showLabels = input.bool(false, title="Show Labels?")
// 步骤二:计算指标数值
// 创建一个函数来获取日线和周线数据
GetData(res, data) =>
request.security(syminfo.tickerid, res, data[1],
lookahead=barmerge.lookahead_on)
// 获取日线和周线的价格数据
dailyHigh = GetData("D", high)
dailyLow = GetData("D", low)
dailyClose = GetData("D", close)
weeklyHigh = GetData("W", high)
weeklyLow = GetData("W", low)
weeklyClose = GetData("W", close)
// 确定时段枢轴点数据
// 首先判断K线与交易时段的时间关系
inSession = not na(time(timeframe.period, sessRange)[1])
sessStart = inSession and not inSession[1]
sessEnd = not inSession and inSession[1]
// 确定时段内的价格数据
sessHigh = 0.0
sessLow = 0.0
sessClose = 0.0
sessHigh := sessStart ? high :
inSession ? math.max(high, sessHigh[1]) : na
sessLow := sessStart ? low :
inSession ? math.min(low, sessLow[1]) : na
sessClose := sessEnd ? close[1] : na
// 计算前一个日内交易时段的高、低、收盘价
highPrevSess = 0.0
lowPrevSess = 0.0
closePrevSess = 0.0
highPrevSess := fixnan(sessHigh)
lowPrevSess := fixnan(sessLow)
closePrevSess := fixnan(sessClose)
if not sessEnd
highPrevSess := highPrevSess[1]
lowPrevSess := lowPrevSess[1]
closePrevSess := closePrevSess[1]
// 现在,确定使用哪种价格数据
// 来进行枢轴点计算
theHigh = if pivotType == "Daily"
dailyHigh
else if pivotType == "Intraday"
highPrevSess
else
weeklyHigh
theLow = if pivotType == "Daily"
dailyLow
else if pivotType == "Intraday"
lowPrevSess
else
weeklyLow
theClose = if pivotType == "Daily"
dailyClose
else if pivotType == "Intraday"
closePrevSess
else
weeklyClose
// 最后,计算枢轴点位
pp = (theHigh + theLow + theClose) / 3
r1 = (pp * 2) - theLow
s1 = (pp * 2) - theHigh
r2 = (pp - s1) + r1
s2 = pp - (r1 - s1)
r3 = (pp - s1) + r2
s3 = pp - (r2 - s1)
// 步骤三:输出指标数据
// 绘制各个枢轴点位
plot(plotS3R3 ? r3 : na, title="R3", style=plot.style_circles,
linewidth=2, color=color.fuchsia)
plot(plotS2R2 ? r2 : na, title="R2", style=plot.style_circles,
linewidth=2, color=#1E90FF)
plot(plotS1R1 ? r1 : na, title="R1", style=plot.style_circles,
linewidth=2, color=#EEC900)
plot(plotPP ? pp : na, title="PP", style=plot.style_circles,
linewidth=2, color=#48D1CC)
plot(plotS1R1 ? s1 : na, title="S1", style=plot.style_circles,
linewidth=2, color=#EEC900)
plot(plotS2R2 ? s2 : na, title="S2", style=plot.style_circles,
linewidth=2, color=#1E90FF)
plot(plotS3R3 ? s3 : na, title="S3", style=plot.style_circles,
linewidth=2, color=color.fuchsia)
// 如果适用,在图表上显示枢轴点名称
newPivots = showLabels == false ? false :
pivotType == "Intraday" ? sessEnd :
pivotType == "Daily" ? dayofmonth != dayofmonth[1] :
dayofweek == dayofweek.monday and dayofmonth != dayofmonth[1]
plotchar(newPivots and plotS3R3 ? r3 : na,
char='', text="R3", offset=1,
location=location.absolute,
color=color.fuchsia, title="R3 label")
plotchar(newPivots and plotS2R2 ? r2 : na,
char='', text="R2", offset=1,
location=location.absolute,
color=#1E90FF, title="R2 label")
plotchar(newPivots and plotS1R1 ? r1 : na,
char='', text="R1", offset=1,
location=location.absolute,
color=#EEC900, title="R1 label")
plotchar(newPivots and plotS1R1 ? s1 : na,
char='', text="S1", offset=1,
location=location.absolute,
color=#EEC900, title="S1 label")
plotchar(newPivots and plotS2R2 ? s2 : na,
char='', text="S2", offset=1,
location=location.absolute,
color=#1E90FF, title="S2 label")
plotchar(newPivots and plotS3R3 ? s3 : na,
char='', text="S3", offset=1,
location=location.absolute,
color=color.fuchsia, title="S3 label")
// 在图表上高亮日内价格数据时段
bgcolor(showSess and inSession and pivotType == "Intraday" ?
color.new(color.orange, 95) : na)
// 步骤四:创建指标警报
alertcondition(condition=ta.cross(close, s3),
title="Pivot S3 Cross",
message="Prices crossed Pivot S3 level")
alertcondition(condition=ta.cross(close, s2),
title="Pivot S2 Cross",
message="Prices crossed Pivot S2 level")
alertcondition(condition=ta.cross(close, s1),
title="Pivot S1 Cross",
message="Prices crossed Pivot S1 level")
alertcondition(condition=ta.cross(close, pp),
title="Pivot PP Cross",
message="Prices crossed the main Pivot Point level")
alertcondition(condition=ta.cross(close, r1),
title="Pivot R1 Cross",
message="Prices crossed Pivot R1 level")
alertcondition(condition=ta.cross(close, r2),
title="Pivot R2 Cross",
message="Prices crossed Pivot R2 level")
alertcondition(condition=ta.cross(close, r3),
title="Pivot R3 Cross",
message="Prices crossed Pivot R3 level")







