获取策略的最高权益
在TradingView中,策略通过在图表上模拟交易来测试其在历史及实时行情中的表现。这些模拟交易的结果,会直接影响策略的资金权益(equity),使其增加或减少。
当策略表现优异时,其资金权益会持续增长。那么,一个策略所能达到的历史最高资金权益是多少呢?让我们通过PineScript代码来找出答案。
获取最高权益的函数
下面的这个自定义函数,能够返回策略运行至今所达到的最高资金权益:
// GetEquityHigh() 函数返回策略在整个回测期间
// 以及后续实时信号交易所达到的最高资金权益。
// 注意:为保证结果的准确性,此函数应在每根K线上都被调用。
GetEquityHigh() =>
var highestEquity = strategy.initial_capital
highestEquity := math.max(highestEquity, strategy.equity)
在这个 GetEquityHigh() 函数中,我们主要做了两件事。首先,我们用 var 关键字声明了一个名为 highestEquity 的持久性变量。这种变量的值能够在不同的K线之间得以保持,因此非常适合用来持续跟踪历史最高权益。我们将它的初始值设为策略的初始资本(strategy.initial_capital)。
随后,我们利用 math.max() 函数来更新 highestEquity 变量的值。math.max() 会比较该变量的当前值与策略的当前资金权益(strategy.equity),并返回两者中的较大值。我们将这个较大值重新赋给 highestEquity。
如此一来,当策略的当前权益创下新高时,highestEquity 的值也会随之更新。而当策略权益回撤时,highestEquity 会保持其之前记录下的高点不变。通过这种机制,该变量就能够记住历史最高权益值。这个变量的最终值,也就是整个函数的返回值。
需要特别注意的是,GetEquityHigh() 函数必须在每一根K线上都执行一次。如果它在某一根K线上没有运行,就会错过该K线上的资金权益数据,从而导致其最终记录的最高值不准确。
函数应用示例
将上述函数代码复制到你的策略脚本后,我们就可以利用它实现多种功能了。最直接的用法,就是调用函数来获取策略的历史最高权益:
// 获取策略的历史最高权益
equityHigh = GetEquityHigh()
我们还可以判断策略的资金权益是否在当前创下了新高。要实现这一点,只需比较函数的当前值与其在上一根K线的值即可:
// 判断当前K线是否创造了新的权益高点
newEquityHigh = GetEquityHigh() > GetEquityHigh()[1]
另一个实用的功能是计算当前资金权益从最高点回撤了多少。我们只需用历史最高权益减去当前权益(strategy.equity)即可得到:
// 计算当前权益距离最高权益的回撤值
lostEquity = GetEquityHigh() - strategy.equity
策略范例
接下来,让我们看一个在完整策略中应用 GetEquityHigh() 函数的例子。下方的脚本使用相对强弱指数(RSI)进行交易。当RSI从超卖区(30)上穿时,我们开仓做多;当RSI上穿超买区(70)时,我们平掉多仓。反之,当RSI从超买区下穿时,我们开仓做空;当RSI下穿超卖区时,我们回补空仓。
在这个策略中,我们会利用 GetEquityHigh() 函数将策略的历史最高权益曲线绘制出来,同时也会展示当前权益的变化,并用背景高亮的方式来标记创出历史新高的时刻。策略的完整代码如下:
//@version=5
strategy(title="获取权益高点")
// GetEquityHigh() 函数返回策略在整个回测期间
// 以及后续实时信号交易所达到的最高资金权益。
// 注意:为保证结果的准确性,此函数应在每根K线上都被调用。
GetEquityHigh() =>
var highestEquity = strategy.initial_capital
highestEquity := math.max(highestEquity, strategy.equity)
// 计算相对强弱指数 (RSI)
rsiValue = ta.rsi(close, 5)
// 定义交易逻辑
// 多头交易
if ta.crossover(rsiValue, 30)
strategy.entry("Enter Long", strategy.long)
if ta.crossover(rsiValue, 70)
strategy.close("Enter Long", comment="Exit Long")
// 空头交易
if ta.crossunder(rsiValue, 70)
strategy.entry("Enter Short", strategy.short)
if ta.crossunder(rsiValue, 30)
strategy.close("Enter Short", comment="Exit Short")
// 在图表上绘制当前权益和最高权益曲线
plot(strategy.equity, color=color.blue, title="策略权益")
plot(GetEquityHigh(), color=color.green, linewidth=2, title="权益高点")
// 当权益创下新高时,用背景色高亮显示
equityHigh = GetEquityHigh()
bgcolor(equityHigh > equityHigh[1] ? color.new(color.green, 70) : na)
首先,我们通过 strategy() 函数对策略进行基本配置,例如为脚本命名。接着,我们引入了前面详细介绍过的 GetEquityHigh() 函数。
然后,ta.rsi() 函数被用来计算RSI指标。我们以收盘价(close)为数据源,计算周期设定为5。
再往下是两组 if 语句,构成了交易的核心逻辑。第一组是多头逻辑:ta.crossover() 函数用于检测RSI是否从下向上穿越了30这一超卖线,一旦穿越,就通过 strategy.entry() 建立多头仓位(strategy.long)。另一个 if 语句则检查RSI是否向上穿越了70,如果是,就通过 strategy.close() 平掉多仓。
另一对 if 语句是空头逻辑:ta.crossunder() 用于检测RSI是否从上向下跌破了70,若是,则执行 strategy.entry() 建立空头仓位(strategy.short)。最后一个 if 语句则判断RSI是否跌破了30,若是,则用 strategy.close() 平掉空仓。
之后,我们绘制资金权益曲线:
// 在图表上绘制当前权益和最高权益曲线
plot(strategy.equity, color=color.blue, title="策略权益")
plot(GetEquityHigh(), color=color.green, linewidth=2, title="权益高点")
第一个 plot() 函数将策略的当前权益(strategy.equity)以蓝色实线的形式绘制出来。第二个 plot() 调用则绘制历史最高权益,其数值来源于 GetEquityHigh() 函数的返回结果,这条线我们用绿色显示。由于没有特别指定绘图样式,PineScript默认将它们都绘制成标准的折线图。
最后,我们添加高亮逻辑,以突显权益创出新高的时刻:
// 当权益创下新高时,用背景色高亮显示
equityHigh = GetEquityHigh()
bgcolor(equityHigh > equityHigh[1] ? color.new(color.green, 70) : na)
这段代码首先将 GetEquityHigh() 的返回值存入 equityHigh 变量。然后,在 bgcolor() 函数中,我们使用了一个条件运算符(?:)来判断 equityHigh 的当前值是否大于其在上一根K线的值。如果大于,说明权益创了新高,我们就返回一个带有70%透明度的绿色,bgcolor() 函数会用这个颜色来填充图表背景。如果权益没有创新高,我们就返回 na,表示不应用任何背景色。
当这个策略在图表上运行时,你将能看到代表当前权益和历史最高权益的两条曲线,并且每当权益创下新高时,图表背景都会被染成绿色:
简单总结一下:strategy.equity 变量能够返回策略在当前K线的资金权益。如果我们希望得到策略的历史最高权益,一个有效的方法是在每一根K线上都跟踪 strategy.equity 的值,并持续记录其出现过的最大值。
获取策略的最低权益
在图表上,TradingView策略通过模拟交易,在历史和实时数据中检验交易思路。如果一个想法是盈利的,策略的账户权益(equity)便会随着时间增长。
然而,并非每个策略都能表现出色。那么,一个策略在测试期间所经历的最糟糕(即最低)的账户权益是多少?让我们用PineScript代码来一探究竟。
获取最低权益的函数
下面的这个自定义函数,能够返回策略有史以来所达到的最低账户权益:
// GetEquityLow() 函数返回策略在整个回测及
// 随后的实盘信号期间所达到的最低账户权益。
// 注意:为确保结果的准确性,必须在每根K线上都调用此函数。
GetEquityLow() =>
var lowestEquity = strategy.initial_capital
lowestEquity := math.min(lowestEquity, strategy.equity)
在这个 GetEquityLow() 函数内部,我们主要做了两件事。首先,我们声明了一个名为 lowestEquity 的持久变量(var),用它来持续追踪策略的最低权益。它的初始值被设定为策略的初始资金,该值由内置变量 strategy.initial_capital 提供。
第二步,我们更新 lowestEquity 变量的值(该值同时也是函数的返回值)。我们使用 math.min() 函数来确定它的新值,该函数会比较变量的当前值与策略的实时权益(strategy.equity),并返回两者中的较小者。
这个机制的巧妙之处在于:如果策略的权益下降并创出新低,strategy.equity 会比 lowestEquity 的旧值更小,于是 lowestEquity 就被更新为这个新的低点。反之,如果权益保持不变或增长,lowestEquity 的旧值就会被保留下来。通过这种方式,lowestEquity 变量就像一个只向下移动的水位标记,始终记住历史最低点。
值得一提的是,GetEquityLow() 函数必须在每一根K线上都被调用。如果某根K线没有运行此函数,它就会错过该K线的权益数据。万一新的权益低点恰好发生在那根被遗漏的K线上,那么整个函数的最终结果就会不准确。
函数应用示例
将上述函数复制粘贴到我们的策略脚本后,便可以多种方式灵活运用它。首先,要获取截至当前K线的策略历史最低权益,只需直接调用函数:
// 获取策略的历史最低权益
equityLow = GetEquityLow()
除了获取当前值,我们还可以将它与前一根K线的值进行比较。比如,我们想知道当前K线是否刚刚创下了新的权益低点。为此,我们只需判断函数的当前返回值是否小于其前一根K线的返回值:
// 判断当前K线是否创下了新的权益低点
newEquityLow = GetEquityLow() < GetEquityLow()[1]
另一个应用是对最低权益值进行计算。例如,要查看策略当前的权益相比历史最低点回升了多少,我们可以用当前权益(strategy.equity)减去历史最低权益:
// 计算当前权益与历史最低点之间的差距
gainedEquity = strategy.equity - GetEquityLow()
策略范例
让我们通过一个完整的策略来看看如何实际使用 GetEquityLow()。下方的脚本基于指数移动平均线(EMA)的交叉信号进行多头和空头交易。
为了追踪其表现,我们将在图表上同时绘制策略的实时权益曲线和其历史最低权益水平线。当权益创下新低时,我们会将图表背景染成红色,从而可以轻松地识别出策略表现不佳的时期。策略的完整代码如下:
//@version=5
strategy(title="获取权益低点")
// GetEquityLow() 函数返回策略在整个回测及
// 随后的实盘信号期间所达到的最低账户权益。
// 注意:为确保结果的准确性,必须在每根K线上都调用此函数。
GetEquityLow() =>
var lowestEquity = strategy.initial_capital
lowestEquity := math.min(lowestEquity, strategy.equity)
// 计算移动平均线
fastEMA = ta.ema(close, 10)
slowEMA = ta.ema(close, 30)
// 生成交易信号
if ta.crossover(fastEMA, slowEMA)
strategy.entry("Enter Long", strategy.long)
if ta.crossunder(fastEMA, slowEMA)
strategy.entry("Enter Short", strategy.short)
// 绘制当前权益与历史最低权益
plot(strategy.equity, color=color.blue, title="策略权益")
plot(GetEquityLow(), color=color.red, linewidth=2, title="权益低点")
// 当权益创下新低时,为背景着色
equityLow = GetEquityLow()
bgcolor(equityLow < equityLow[1] ? color.new(color.red, 70) : na)
首先,我们使用 strategy() 函数来配置策略,并为脚本命名。接着,我们引入之前讨论过的 GetEquityLow() 函数。
然后,ta.ema() 函数被用来计算两条移动平均线。它们都基于收盘价(close)计算,但周期不同:一条是10周期快线,另一条是30周期慢线。
之后是两个 if 条件判断。第一个使用 ta.crossover() 函数判断快线是否向上穿越慢线。如果成立,则执行 strategy.entry() 函数建立一个多头仓位(strategy.long)。第二个 if 语句则使用 ta.crossunder() 函数判断快线是否向下穿越慢线。如果成立,则建立一个空头仓位(strategy.short)。
接下来,我们绘制策略的权益数据:
// 绘制当前权益与历史最低权益
plot(strategy.equity, color=color.blue, title="策略权益")
plot(GetEquityLow(), color=color.red, linewidth=2, title="权益低点")
第一个 plot() 函数用蓝色线条展示策略的实时权益(strategy.equity)曲线。第二个 plot() 调用则展示历史最低权益。我们通过 GetEquityLow() 函数获取这个值,并用红色粗线条(linewidth=2)将其绘制出来。由于我们没有指定绘图类型,PineScript会默认将它们都绘制成线图。
最后,我们检查策略权益是否创下新低,并据此为图表背景着色:
// 当权益创下新低时,为背景着色
equityLow = GetEquityLow()
bgcolor(equityLow < equityLow[1] ? color.new(color.red, 70) : na)
这里,我们先将 GetEquityLow() 的返回值存入 equityLow 变量。然后,在 bgcolor() 函数内部,我们使用条件运算符(?:)来判断 equityLow 的当前值是否小于其前一根K线的值。
如果策略的最低权益确实进一步下跌,条件运算符就会返回一个通过 color.new() 函数设置了70%透明度的红色,bgcolor() 函数随即使用这个颜色来渲染图表背景。如果未创下新低,条件运算符则返回 na,这会使当前K线的背景色保持透明。
当这个策略在图表上运行时,一条蓝色的曲线代表着实时权益的波动,一条红色的水平线标记着历史最低权益。而每当出现红色背景时,就清晰地警示我们策略的权益刚刚创下了新的低点:
简单总结一下:strategy.equity 变量能够返回策略的当前账户权益。只要我们在每一根K线上都追踪这个变量的值,我们所能观察到的历史最低值,就是该策略有史以来最差的权益表现。
获取策略的最大仓位规模
在TradingView中,一个策略在图表上模拟交易时,其持仓规模时常会发生变化。我们可能会根据策略的账户权益来动态调整订单数量,或是通过多次入场来分批建仓(即金字塔式加仓)。那么,策略要如何知道它在整个回测期间建立过的最大持仓规模是多少呢?
PineScript为此提供了三个内置变量:strategy.max_contracts_held_all 返回历史最大持仓规模(不分多空);strategy.max_contracts_held_long 返回历史最大多头持仓规模;strategy.max_contracts_held_short 返回历史最大空头持仓规模。接下来,我们将逐一深入了解这些变量。
最大持仓规模
strategy.max_contracts_held_all 变量能够返回策略在图表上交易过的所有持仓中,规模最大的那一次。该变量返回一个浮点数值。我们可以多种方式运用它,例如,直接将其绘制在图表上:
// 在图表上绘制策略的历史最大持仓
plot(strategy.max_contracts_held_all, color=color.orange,
title="最大持仓")
我们也可以将它与某个数值进行比较,以判断历史最大持仓规模是否超过了预设的风险阈值:
// 检查历史最大持仓是否曾超过100个单位
bigPositionTraded = strategy.max_contracts_held_all > 100
最大多头持仓规模
strategy.max_contracts_held_long 变量返回策略所有多头持仓中的最大规模。它同样返回一个浮点数值。我们可以用多种方式来使用它,比如,以直方图的形式在图表上展示:
// 将最大多头持仓规模以直方图形式展示
plot(strategy.max_contracts_held_long, style=plot.style_histogram,
color=color.teal, title="最大多头持仓")
或者,我们也可以基于最大多头持仓规模来进行决策。例如,比较最大多头持仓是否曾大于最大空头持仓:
// 判断最大多头持仓是否大于最大空头持仓
longIsBigger = strategy.max_contracts_held_long > strategy.max_contracts_held_short
最大空头持仓规模
strategy.max_contracts_held_short 变量返回策略所有空头持仓中的最大规模。该变量返回的是一个正的浮点数值。我们可以用多种方式来运用它,比如绘制成面积图,以观察最大空头持仓规模随时间的变化:
// 用面积图追踪最大空头持仓的变化
plot(strategy.max_contracts_held_short, style=plot.style_area,
color=color.orange, title="最大空头持仓")
或者,我们可以检验最大空头持仓是否同时也是策略总体的最大持仓:
// 检查策略的最大空头持仓是否也是其总体的最大持仓
shortIsBiggest = strategy.max_contracts_held_short == strategy.max_contracts_held_all
需要注意的是,strategy.max_contracts_held_short 返回的是一个正数。它不像 strategy.position_size 那样,将空头持仓规模报告为负数。
这一系列变量的核心特性
以上我们讨论的三个变量,都具备一些共同特性。首先是单位通用性:这些变量返回的是合约、股数、单位、手数等数量,具体单位取决于当前图表的交易品种。变量名中的contracts(合约)一词仅为通用指代,并不意味着它们只适用于期货合约。
其次是动态更新性:这些变量返回的是策略截至当前的历史最大持仓规模。随着策略逐根处理K线,这个最大值可能会不断被刷新。因此,在图表初期和末期,变量的值通常是不同的。
第三点也是一个关键区别:这些变量反映的是最大持仓规模,而不是单笔订单的规模。TradingView在计算时,会合并同一方向上的所有敞口订单。如果我们通过4笔订单(每笔100股)来分批建仓,那么持仓规模就是400,即使单笔订单规模仅为100。
另外,如果策略在某个方向上从未有过交易,那么相应的最大持仓变量就会返回零。例如,若无任何多头交易,strategy.max_contracts_held_long 的值便为0。最后,这三个变量在回测结束时的最终值,与TradingView策略测试器面板中,表现摘要里最大持仓合约数一栏所显示的值完全相同。
策略范例
让我们通过一个完整的策略来看看如何应用这些变量。以下脚本交易的是20周期的价格突破。当价格突破近期高点时做多,跌破近期低点时做空。
我们设置订单数量为账户权益的某个百分比,并允许在同一方向上进行5次金字塔式加仓。回测结束后,脚本会在图表上创建一个文本标签,显示策略的历史最大持仓规模。策略的完整代码如下:
//@version=5
strategy(title="显示最大持仓规模", overlay=true,
default_qty_type=strategy.percent_of_equity,
default_qty_value=15, pyramiding=5)
// 计算近期的最高高点和最低低点
highestHigh = ta.highest(high, 20)[1]
lowestLow = ta.lowest(low, 20)[1]
// 绘制最高高点和最低低点
plot(highestHigh, color=color.green, title="最高高点")
plot(lowestLow, color=color.red, title="最低低点")
// 生成交易信号
if close > highestHigh
strategy.entry("Enter Long", strategy.long)
if close < lowestLow
strategy.entry("Enter Short", strategy.short)
// 在最后一条确认的历史K线上,创建一个文本标签,
// 显示最大的多头、空头及总持仓规模
if barstate.islastconfirmedhistory
posSizeLabel = label.new(x=bar_index + 3, y=close,
style=label.style_label_left, color=#E6E6FA,
textcolor=color.black, size=size.large)
label.set_text(posSizeLabel,
"最大持仓: " + str.tostring(strategy.max_contracts_held_all) +
"\n\n最大多头: " + str.tostring(strategy.max_contracts_held_long) +
"\n最大空头: " + str.tostring(strategy.max_contracts_held_short))
首先,我们通过 strategy() 函数配置策略,为其命名并使其叠加在主图表上。我们还将默认下单数量设置为账户权益的15%,并通过 pyramiding 参数允许在同一方向最多进行5次加仓。
接着,ta.highest() 和 ta.lowest() 函数分别计算出过去20根K线的最高价和最低价。我们使用 [1] 来获取前一根K线的值,确保计算不包含当前K线的数据。随后,plot() 函数将近期高点和低点绘制出来,高点为绿色,低点为红色。
然后是两个 if 语句负责生成入场信号。第一个判断收盘价是否高于近期高点,若是,则 strategy.entry() 建立一个多头仓位。第二个则判断收盘价是否低于近期低点,若是,则建立一个空头仓位。
最后,我们在图表上展示最大持仓规模信息:
// 在最后一条确认的历史K线上,创建一个文本标签,
// 显示最大的多头、空头及总持仓规模
if barstate.islastconfirmedhistory
posSizeLabel = label.new(x=bar_index + 3, y=close,
style=label.style_label_left, color=#E6E6FA,
textcolor=color.black, size=size.large)
label.set_text(posSizeLabel,
"最大持仓: " + str.tostring(strategy.max_contracts_held_all) +
"\n\n最大多头: " + str.tostring(strategy.max_contracts_held_long) +
"\n最大空头: " + str.tostring(strategy.max_contracts_held_short))
此处的 if 语句通过 barstate.islastconfirmedhistory 变量判断当前是否为图表的最后一条历史K线。当条件满足时,代码首先使用 label.new() 创建一个新的文本标签。
该标签被放置在图表末端向右3根K线的位置(bar_index + 3),垂直位置在最后一根K线的收盘价(close)。标签箭头朝左(label.style_label_left),背景色为淡紫色(#E6E6FA),文字为黑色、大号字体。
我们将 label.new() 返回的标签ID存入 posSizeLabel 变量,然后用 label.set_text() 函数来设定标签要显示的文本。文本内容是由字符串和三个核心变量拼接而成。由于这些变量返回的是数值,而标签需要文本,我们使用 str.tostring() 函数将数值转换为字符串。
当我们在图表上运行该策略时,其生成的标签会清晰地告知我们策略所交易过的最大持仓规模:
简单总结一下:PineScript通过三个内置变量来报告策略截至当前的最大持仓规模。strategy.max_contracts_held_all 返回策略历史上的最大持仓规模(不区分多空),strategy.max_contracts_held_long 返回历史上的最大多头持仓规模,strategy.max_contracts_held_short 则返回历史上的最大空头持仓规模。





