保持敬畏之心
交易是一场持久战

Pine Script(241):获取策略的胜率与亏损率

#Pine Script入门教学

获取策略的胜率

TradingView策略能够在图表上模拟各种交易设想。运行一段时间后,有些交易盈利,有些则亏损。那么,一个策略最终盈利的交易究竟占多大比例呢?让我们来学习如何使用PineScript计算策略的胜率(Hit Rate)。

计算胜率的函数

下面的这个自定义函数可以返回策略的胜率:

// GetWinRate() 函数返回策略的胜率(即盈利交易的百分比)。
// 可将 'includeEvens' 参数设置为 'true',
// 以便将盈亏两平的交易也视作盈利。
GetWinRate(includeEvens = false) =>
	winTradeCount = strategy.wintrades + (includeEvens ? strategy.eventrades : 0)
	(winTradeCount / strategy.closedtrades) * 100

GetWinRate() 函数有一个可选参数:includeEvens。当此参数为 true 时,函数会将盈亏两平的交易也算作盈利交易。但通常我们不会如此慷慨,因此该参数的默认值为 false

在函数内部,我们分两步操作。第一步,我们创建一个 winTradeCount 变量来统计胜利的交易总数。它的值由 strategy.wintrades 变量(即盈利交易数)加上一个条件值构成。这个条件值取决于 includeEvens 参数,我们通过条件运算符(?:)来判断:如果 includeEvenstrue,我们就加上盈亏两平的交易数(strategy.eventrades);如果为 false,则不计入保本交易,即加上0。

第二步,我们计算胜率的百分比。我们将第一步得到的胜利交易总数(winTradeCount)除以策略的已平仓交易总数(strategy.closedtrades),然后将结果乘以100,即可得到胜率。该胜率便是此函数的最终返回值。

函数应用示例

将上述函数代码复制粘贴到我们的策略脚本后,便可以多种方式灵活运用它。如果我们想获取标准的胜率(不包含保本交易),只需这样调用函数:

// 获取策略的胜率
winRate = GetWinRate()

我们也可以将胜率用于布尔逻辑判断。例如,验证策略胜率是否达到了某个最低要求:

// 验证胜率是否高于45%的最低标准
okayWinRate = GetWinRate() > 45

另一个应用场景是比较当前K线的胜率与历史K线的胜率,以观察其近期变化。例如,要查看包含了保本交易的胜率相较于上一根K线是否有所提升,可以这样写:

// 查看包含保本交易的胜率,相较于上一根K线是否有所提升
winRateIncreased = GetWinRate(true) > GetWinRate(true)[1]

GetWinRate() 函数返回的数值也可以被其他PineScript函数使用。例如,要找出过去40根K线内的最高胜率,我们可以将此函数与 ta.highest() 函数结合:

// 获取过去40根K线内的最高胜率
winPercHigh = ta.highest(GetWinRate(), 40)

策略范例

让我们通过一个完整的策略来演示如何使用 GetWinRate() 函数。以下脚本交易的是价格对近期高点和低点的突破,但其出场逻辑比较特殊:当价格穿越高低点范围的中点时,便退出当前头寸。

为了追踪策略的表现,我们将在图表上绘制其胜率曲线,并用高亮的背景色来标记那些胜率有所提升的K线周期。策略的完整代码如下:

//@version=5
strategy(title="获取策略胜率")

// GetWinRate() 函数返回策略的胜率(即盈利交易的百分比)。
// 可将 'includeEvens' 参数设置为 'true',
// 以便将盈亏两平的交易也视作盈利。
GetWinRate(includeEvens = false) =>
	winTradeCount = strategy.wintrades + (includeEvens ? strategy.eventrades : 0)
	(winTradeCount / strategy.closedtrades) * 100

// 计算近期最高点、最低点及其中点
highestHigh = ta.highest(high, 20)[1]
lowestLow   = ta.lowest(low, 20)[1]
midPoint    = (highestHigh + lowestLow) / 2

// 生成入场交易信号
if high > highestHigh
    strategy.entry("Enter Long", strategy.long)

if low < lowestLow
    strategy.entry("Enter Short", strategy.short)

// 生成出场交易信号
if ta.crossunder(close, midPoint)
    strategy.close("Enter Long", comment="Exit Long")

if ta.crossover(close, midPoint)
    strategy.close("Enter Short", comment="Exit Short")

// 在图表上绘制策略的当前胜率
plot(GetWinRate(), color=color.blue, title="胜率")

// 当胜率提升时,将背景渲染为绿色
winRate = GetWinRate()
bgcolor(winRate > winRate[1] ? color.new(color.green, 70) : na)

首先,我们通过 strategy() 函数配置策略并为其命名。接着,我们引入之前讨论过的 GetWinRate() 函数。

然后,ta.highest() 函数计算了过去20根K线的最高价,ta.lowest() 则计算了同期的最低价。我们将这两个值相加除以二,得到它们的中点 midPoint

随后是两个 if 语句生成的入场信号。第一个判断当前K线的最高价是否向上突破了近期高点,若是,则 strategy.entry() 函数建立一个多头仓位。第二个 if 语句则判断当前K线的最低价是否向下跌破了近期低点,若是,则建立一个空头仓位。

接下来的两个 if 语句负责出场。第一个使用 ta.crossunder() 判断收盘价是否下穿了高低点的中线,若是,则 strategy.close() 函数平掉多头仓位。第二个则使用 ta.crossover() 判断收盘价是否上穿了中线,若是,则平掉空头仓位。

之后,我们在图表上绘制策略的胜率:

// 在图表上绘制策略的当前胜率
plot(GetWinRate(), color=color.blue, title="胜率")

此处的 plot() 函数负责显示 GetWinRate() 函数的返回值。由于未明确指定绘图类型,PineScript默认会以常规线图的形式,用蓝色将其绘制出来。

最后,我们来判断策略的胜率是否有所提升:

// 当胜率提升时,将背景渲染为绿色
winRate = GetWinRate()
bgcolor(winRate > winRate[1] ? color.new(color.green, 70) : na)

这里,我们首先将 GetWinRate() 的返回值赋给一个变量。这一步并非必需,但它展示了将函数结果存入变量再使用的可行性。

然后 bgcolor() 函数负责为图表背景着色。我们不希望一直着色,因此通过条件运算符(?:)来判断当前的胜率是否相较于前一根K线有所提升。如果胜率确实提升了,条件运算符便返回一个通过 color.new() 函数设置了70%透明度的绿色,bgcolor() 随即使用该颜色渲染背景。如果胜率没有提升,运算符则返回 na,从而关闭背景着色功能。

在图表上,该策略展示了其胜率如何随着每一笔交易而实时变化。每当胜率提升时,绿色的背景便会高亮显示那一刻:

简单总结一下:要计算一个策略的胜率,我们将盈利的交易笔数(strategy.wintrades)除以已平仓的总交易笔数(strategy.closedtrades)。如果我们想采取一种更乐观的视角,也可以将盈亏两平的交易(strategy.eventrades)视作胜利的交易——毕竟,它们至少没有亏钱。

获取策略的亏损率

当TradingView策略在图表上模拟交易时,有些会盈利,有些则会亏损。那么,所有交易中亏损的比例究竟是多少呢?让我们用PineScript来精确计算这个指标。

亏损率计算函数

下面的这个自定义函数,能够返回策略的亏损百分比(亏损率):

// GetLoseRate() 函数返回策略的亏损率(非盈利交易的百分比)。
// 将 'includeEvens' 参数设为 'true',可以将保本交易也视为亏损交易。
GetLoseRate(includeEvens = false) =>
    loseTradeCount = strategy.losstrades + 
         (includeEvens ? strategy.eventrades : 0)
    (loseTradeCount / strategy.closedtrades) * 100

这个 GetLoseRate() 函数包含一个可选参数:includeEvens。当其值被设为 true 时,函数会将保本交易(break-even trades)也计入亏损。从风险回报的角度看,这种做法是合理的,毕竟,这些保本交易占用了资金、承担了市场风险,却没有带来任何回报。如果我们不设置此参数,其默认值为 false,此时函数将只统计那些实际产生亏损的交易。

函数内部的逻辑分为两步。首先,我们确定需要统计的亏损交易总数。我们以实际的亏损交易数(strategy.losstrades)为基础,然后通过一个条件运算符(?:)来决定是否要加上保本交易数。该运算符会检查 includeEvens 参数的值:如果参数为 true,运算符就返回策略的保本交易数(strategy.eventrades);反之则返回0。这样,我们就能灵活地控制是否将保本交易计入亏损。我们将计算出的亏损交易总数储存在 loseTradeCount 变量中。

然后,为了计算亏损率,我们将这个亏损总数除以策略的已平仓交易总数(strategy.closedtrades)。最后,将结果乘以100,即可将其转换为百分比形式。这个百分比就是函数的最终返回值。

函数应用示例

将上述函数代码复制到你的策略脚本后,你就可以在多处灵活地调用它了。若想获取策略当前最严格意义上的亏损率(不含保本交易),可以这样调用:

// 获取策略的亏损百分比
loseRate = GetLoseRate()

我们也可以利用亏损率进行逻辑判断。例如,检查亏损率是否低于某个可接受的水平:

// 判断亏损率是否低于55%这个可接受的阈值
okayLoseRate = GetLoseRate() < 55

另一个应用场景是比较当前与历史的亏损率,以观察其变化趋势。例如,要判断包含保本交易的广义亏损率是否有所下降,可以这样写:

// 检查包含保本交易的亏损率,相较于上一根K线是否有所下降
loseRateDecreased = GetLoseRate(true) < GetLoseRate(true)[1]

GetLoseRate() 函数的返回值也可以作为其他PineScript函数的输入。例如,下面的代码就利用 ta.lowest() 函数来找出过去50根K线内的最低亏损率:

// 获取最近50根K线内的最低亏损率
losePercLow = ta.lowest(GetLoseRate(), 50)

策略范例

让我们通过一个完整的策略来看看如何应用 GetLoseRate()。下方的脚本使用三条移动平均线进行交易。当快线穿越中线时,我们顺着慢线的方向(即市场大趋势)开立多头或空头仓位。

为了持续追踪策略的表现,我们会在图表上绘制其亏损率的变化曲线。当亏损率上升(意味着策略表现恶化)时,我们还将用背景色进行高亮警示。策略的完整代码如下:

//@version=5
strategy(title="获取策略亏损率")

// GetLoseRate() 函数返回策略的亏损率(非盈利交易的百分比)。
// 将 'includeEvens' 参数设为 'true',可以将保本交易也视为亏损交易。
GetLoseRate(includeEvens = false) =>
    loseTradeCount = strategy.losstrades + 
         (includeEvens ? strategy.eventrades : 0)
    (loseTradeCount / strategy.closedtrades) * 100

// 计算三条EMA均线
fastEMA = ta.ema(close, 10)
medEMA  = ta.ema(close, 30)
slowEMA = ta.ema(close, 80)

// 定义交易逻辑
if ta.crossover(fastEMA, medEMA) and close > slowEMA
    strategy.entry("Enter Long", strategy.long)
if ta.crossunder(fastEMA, medEMA) and close < slowEMA
    strategy.entry("Enter Short", strategy.short)

// 在图表上绘制策略的亏损率曲线
plot(GetLoseRate(), color=color.blue, title="亏损率")

// 当亏损率上升时,将背景涂成红色
loseRate = GetLoseRate()
bgcolor(loseRate > loseRate[1] ? color.new(color.red, 75) : na)

首先,我们用 strategy() 函数进行策略声明并命名。接着,我们引入了前面详细介绍过的 GetLoseRate() 自定义函数。

然后,ta.ema() 函数被用来计算三条不同周期的指数移动平均线,周期分别为10、30和80。它们都基于K线的收盘价(close)进行计算。

随后的两个 if 语句构成了交易的入场逻辑。第一个使用 ta.crossover() 函数判断快线是否上穿了中线,同时要求当前收盘价高于慢线(确认处于上升趋势)。当这两个条件都满足时,strategy.entry() 执行多头开仓。另一个 if 语句则用 ta.crossunder() 函数判断快线是否下穿了中线,并要求收盘价低于慢线(确认处于下降趋势)。当条件满足时,执行空头开仓。

然后,我们绘制策略的亏损率:

// 在图表上绘制策略的亏损率曲线
plot(GetLoseRate(), color=color.blue, title="亏损率")

这里的 plot() 函数直接调用 GetLoseRate() 并将其返回值绘制出来。由于没有指定绘图样式,PineScript会默认画一条常规的折线,颜色为蓝色。

最后,我们根据亏损率的变化为图表背景上色:

// 当亏损率上升时,将背景涂成红色
loseRate = GetLoseRate()
bgcolor(loseRate > loseRate[1] ? color.new(color.red, 75) : na)

在此,我们先将 GetLoseRate() 的返回值赋给一个变量 loseRate。这一步并非必需,但它展示了一种良好的编程习惯:将函数结果存入变量,以便后续可以对该变量进行复用或修改。

我们用 bgcolor() 函数来改变图表背景色,但并非在每根K线上都改变。我们通过条件运算符(?:)来判断亏损率是否上升(即当前值大于上一根K线的值)。如果确实上升了,运算符就返回一个带有75%透明度的红色(color.new(color.red, 75)),bgcolor() 随即用这个颜色填充背景。反之,如果亏损率没有上升,则返回 na 值,这会取消背景色。

当策略应用于图表时,它会展示出亏损率随时间演变的曲线。每当有新的亏损交易导致亏损率上升时,图表背景就会被染成红色以作提醒:

简单总结一下:要计算一个策略的亏损百分比,我们只需将亏损交易的总笔数(strategy.losstrades)除以已平仓的总交易笔数(strategy.closedtrades)。如果想更严苛地评估策略表现,可以将保本交易(strategy.eventrades)也计入亏损总数中。毕竟,这些交易虽然没有直接亏钱,但它们占用了资本并承担了风险,却没有带来任何回报。

赞(0)
未经允许不得转载:图道交易 » Pine Script(241):获取策略的胜率与亏损率
分享到

评论 抢沙发

登录

找回密码

注册