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

Pine Script(122):math.min与math.max,把数值限制在指定范围

#Pine Script入门教学

math.min():取多个数值中的最小值

通过 Pine Script 的 math.min() 函数,我们可以从多个数值中获取最小值 。

该函数的默认语法格式如下 :

math.min(number1, number2, ...)

number1number2 是我们希望从中找出最小值的整数或浮点数。

三个点(...)表示:可以有任意数量的参数。因此,number3number4number5 都是可以的,甚至可以有更多。TradingView 并未在文档中说明参数的最大数量。在我的测试中,我可以给 math.min() 传入75个不同的值,脚本依然能快速计算且不报错。

math.min() 会返回两种可能的值之一:

  • 一个整数或浮点值,代表所提供数值中的最小值。
  • 或者是 na 值。只要 math.min() 的参数中有一个为 na,该函数就会返回 na

快速范例

我们经常使用 math.min() 来获取两个值中的较小者。比方说,我们想得到当前K线的收盘价(close)和前一根K线开盘价(open[1])之间的最低值。要获取这个最小值,我们可以像这样使用 math.min()

// 获取当前K线收盘价与上一根K线开盘价的最小值
closeOpenMin = math.min(close, open[1])

我们也可以给 math.min() 传入两个以上的参数。比方说,我们对当前收盘价和最近4个开盘价的最低值感兴趣,那么我们可以这样使用该函数:

// 获取当前K线收盘价与
// 最近四个开盘价的最低值
lowestCloseOpen = math.min(close, open, open[1], open[2], open[3])

math.min()ta.lowest() 的对比

Pine Script 有两个可以返回最小值(最低值)的函数,它们是 math.min()ta.lowest()。它们的区别如下:

  • ta.lowest() 处理的是 单一变量或函数 的历史数据。它找出该数据在 最近指定数量的K线内 达到过的最低值。例如,通过 ta.lowest() 函数,我们可以得到20周期内的最低价,或者过去5根K线内的最低收盘价。
  • math.min() 返回的是 多个不同数值 之间的最小值。这些数值可以都只针对当前K线,也可以包含之前K线上的值。例如,通过 math.min() 函数,我们可以得到当前K线开盘价和收盘价的较低者,或者三条不同移动平均线中的最小值。

在某些情况下,我们可以任选 ta.lowest()math.min()。当 math.min() 用于获取一个变量在过去几根K线上的最小值时,其用法就和 ta.lowest() 的作用相同。

例如,下面这两行代码会返回相同的值:

// 获取最近3根K线的最低收盘价
lowestClose = math.min(close, close[1], close[2])

// 或等效的写法:
lowestClose = ta.lowest(close, 3)

虽然 math.min() 可以获取一个变量在过去K线上的最小值,但为了这个目的,使用 ta.lowest() 函数通常更普遍(也更省事)。

math.min() 的特性

math.min() 从变量或函数结果中返回最小值,但该函数不能用于处理数组。要获取数组中的最小值,我们应该改用 array.min() 函数。

在 Pine Script 版本4及更早的版本中,math.min() 函数的名称是 min() 。除了名称变化外,其行为并未改变。

使用 math.min() 替代 if/else 语句

math.min() 的作用是评估并返回一组数中的最低值。这个功能我们有时会用 if 语句或 if/else 语句来实现。但如果我们选择使用 math.min(),我们的代码会变得更短,而且通常更易于阅读。

让我们来看一个例子。假设我们想获取当前收盘价和前一根K线最低价的较小值。使用 math.min() 的代码是:

minimumValue = math.min(close, low[1])

要用 if 语句实现相同的行为,则需要:

minimumValue = close
if low[1] < minimumValue
    minimumValue := low[1]

而要用 if/else 语句得到相同的结果,代码如下:

float minimumValue = na
if close < low[1]
    minimumValue := close
else
    minimumValue := low[1]

如上所示,math.min() 需要敲的代码少得多,并且也减少了出错的可能性。

脚本范例

让我们看看脚本如何使用 math.min() 函数。下方的指标会绘制两条简单移动平均线(SMA)。我们使用 math.min() 来获取这两条平均线中的较低者。这条最低值线也会被绘制在图表上,举例来说,我们可以将这个值用作止损水平。

该指标的完整代码如下:

//@version=5
indicator(title="math.min() example script", overlay=true)

// 计算两条移动平均线
fastSMA = ta.sma(close, 20)
slowSMA = ta.sma(close, 45)

// 获取两条平均值中的最低值
minAverage = math.min(fastSMA, slowSMA)

// 在图表上绘制两条平均线及其最小值
plot(series=fastSMA, color=color.fuchsia, title="Fast SMA")
plot(series=slowSMA, color=color.teal, title="Slow SMA")

plot(series=minAverage, color=color.orange, linewidth=3,
     title="Lowest Average")

首先,我们使用 indicator() 函数来设置指标的属性,用于命名该脚本,并使其显示在与主图交易品种相同的图表区域内。

然后,ta.sma() 函数计算两条简单移动平均线。第一条的周期长度为20根K线,另一条则使用45为周期。两者都基于交易品种的收盘价(close)进行计算。我们将它们的值分别存储在 fastSMAslowSMA 变量中。

接下来,我们将这两个变量传入 math.min() 函数,这会让我们得到两条平均线中的最低值。我们将这个最小值赋给 minAverage 变量。

之后,Pine Script 的 plot() 函数创建了三个绘图。前两个用于显示 fastSMAslowSMA 变量。第一条以紫红色(color.fuchsia)显示,另一条则以青色(color.teal)出现。由于我们没有明确指定绘图类型,Pine Script 会默认将它们创建为常规的线图。

最后,我们用 plot() 绘制两条平均线的最小值(minAverage)。这同样会创建一个常规的线图,并以橙色(color.orange)显示。因为我们在每一根K线上都获取了最小值,所以最终会得到一条在每根K线上都有对应数值的连续线。

当该脚本在图表上运行时,它会绘制两条移动平均线以及它们的橙色最小值线。在一个苹果公司(Apple)的日线图上,它看起来是这样的:

总结

  • math.min() 函数会从其接收的多个整数或浮点数参数中,返回数值最小的那个。
  • 如果所提供的参数中有一个是 na(不可用),那么 math.min() 的返回值也会是 na

math.max():取多个数值中的最大值

通过 Pine Script 的 math.max() 函数,我们可以从多个数值中获取最大值 。

该函数的标准语法格式如下 :

math.max(number1, number2, ...)

number1number2 是我们希望从中找出最大值的整数或浮点数。

三个点(...)表示:可以有任意数量的参数。因此,number3number4number5 都是可以的,甚至更多也完全没问题。TradingView 并未指明参数的最大数量。在我测试 math.max() 时,我可以给函数传入75个不同的值,脚本依然能快速执行且不报错。

math.max() 会返回两种可能的值之一:

  • 一个整数或浮点值,代表所提供参数中的最大值。
  • 或者是 na 值。当 math.max() 的参数中有一个(或多个)为 na 时,就会返回这个值。

快速范例

我们经常使用 math.max() 来获取两个值中的较大者。比方说,我们想得到当前K线的收盘价(close)和前一根K线开盘价(open[1])之间的最高值。要获取这个最大值,我们可以像这样使用 math.max()

// 获取当前K线收盘价与上一根K线开盘价的最大值
closeOpenMax = math.max(close, open[1])

math.max() 可以处理远不止两个数值。比方说,我们想要当前收盘价和最近4个开盘价的最高值。为此,我们可以这样使用该函数:

// 获取当前K线收盘价与
// 最近四个开盘价的最高值
highestCloseOpen = math.max(close, open, open[1], open[2], open[3])

math.max()ta.highest() 的对比

有两个 Pine Script 函数可以返回最大值(最高值),它们是 math.max()ta.highest()。它们的区别如下:

  • ta.highest() 处理的是 单一变量或函数 的历史数据。它返回该数据在 最近指定数量的K线内 达到过的最高值。例如,通过 ta.highest() 函数,我们可以得到20周期内的最高价,或者过去10根K线内的最大成交量。
  • math.max() 返回的是 多个不同数值 之间的最大值。这些数值可以都只针对当前K线,也可以包含之前K线上的值。例如,通过 math.max() 函数,我们可以得到当前K线开盘价和收盘价的较高者,或者三条不同移动平均线中的最大值。

在某些情况下,我们可以任选 ta.highest()math.max()。当 math.max() 用于获取一个变量在过去几根K线上的最大值时,其用法就和 ta.highest() 的作用相同。

例如,下面这两行代码会返回相同的值:

// 获取最近3根K线的最高收盘价
highestClose = math.max(close, close[1], close[2])

// 或等效的写法:
highestClose = ta.highest(close, 3)

尽管 math.max() 可以获取一个变量在过去K线上的最大值,但使用 ta.highest() 函数来替代通常更普遍,也更省事。

math.max() 的特性

math.max() 从多个变量或函数结果中返回最大值。然而,它不能用于处理 Pine Script 数组。要获取数组中的最大值,我们应该改用 array.max() 函数。

在 Pine Script 版本4及更早的版本中,math.max() 函数的名称是 max() 。除了名称变化外,其行为并未改变。

使用 math.max() 替代 if/else 语句

math.max() 的作用是评估并返回一组数中的最高值。这个功能 if 语句和 if/else 语句也能做到。但如果我们改用 math.max(),我们的代码会更短,而且通常更易于阅读。

让我们来看一个例子。假设我们想获取当前收盘价和前一根K线最高价的较大值。使用 math.max() 来实现的代码是:

maximumValue = math.max(close, high[1])

要用 if 语句实现相同的功能,则需要:

maximumValue = close
if high[1] > maximumValue
    maximumValue := high[1]

而要用 if/else 语句得到相同的结果,代码如下:

float maximumValue = na
if close > high[1]
    maximumValue := close
else
    maximumValue := high[1]

如这些示例所示,math.max() 需要敲的代码少得多,该函数也减少了编程出错的可能性。

脚本范例

让我们来探究一个脚本如何使用 math.max() 函数。下方的指标会绘制两条指数移动平均线(EMA)。我们使用 math.max() 来获取这两条平均线中的较高者。这条最大值线也会被绘制在图表上,举例来说,我们可以将其用作买入触发条件或空头止损水平。

该指标的完整代码如下:

//@version=5
indicator(title="math.max() example script", overlay=true)

// 计算两条移动平均线
fastEMA = ta.ema(close, 12)
slowEMA = ta.ema(close, 24)

// 获取两条平均值中的最大值
maxAverage = math.max(fastEMA, slowEMA)

// 在图表上绘制两条平均线及其最大值
plot(series=fastEMA, color=color.fuchsia, title="Fast EMA")
plot(series=slowEMA, color=color.teal, title="Slow EMA")

plot(series=maxAverage, color=color.orange, linewidth=3,
     title="Highest Average")

首先,我们使用 Pine Script 的 indicator() 函数来配置指标,用于命名该脚本并将其叠加显示在主图的交易品种上。

然后,ta.ema() 函数计算两条指数移动平均线。第一条的周期为12,另一条则使用24根K线。两者都基于交易品种的收盘价(close)进行计算。我们将它们的值分别存储在 fastEMAslowEMA 变量中。

接下来,我们获取这两个变量中的最大值。因此我们将它们传入 math.max() 函数。返回的最大值被我们存储在 maxAverage 变量中。

之后,plot() 函数创建了三个绘图。第一个用于显示快速平均线(fastEMA),颜色为紫红色(color.fuchsia)。第二个显示慢速平均线(slowEMA),颜色为青色(color.teal)。由于我们没有设置绘图类型,Pine Script 会默认将它们创建为常规的线图。

第三个也是最后一个绘图是两条平均线的最大值(maxAverage)。这条线以橙色(color.orange)显示,并且比正常线更粗(linewidth=3)。

当我们将该脚本添加到图表时,它会绘制两条移动平均线以及它们的最大值。在一个苹果公司(Apple)的日线图上,它看起来是这样的:

总结

  • math.max() 函数会从其接收的多个整数或浮点数参数中,返回数值最大的那个。
  • 如果所提供的参数中有一个是 na(不可用),那么 math.max() 的返回值也会是 na

将数值限制在指定范围内

TradingView 的指标和策略通过计算各种数值来分析图表。但我们并非总是希望一个数值可以取任何可能的值。有时,为一个数值设定一个范围限制会达到最佳效果。

在 Pine Script 中,有三种方法可以限制一个数值的范围:

  • 设置一个最小值。
  • 设置一个最大值。
  • 设置一个从最小到最大的范围。

下面我们来看看如何用代码实现这些限制。

设置一个最小值

当我们为一个数值设定一个最小值时,我们实际上是为其值设定了一个下限。这样,该数值将永远等于或高于某个特定水平,而不能低于该最小值。

自定义函数

下面的这个自定义函数 ApplyMin() 可以为一个给定的数值应用一个最小值下限:

// ApplyMin() 为指定的值应用给定的最小值范围。
// 当值高于最小值时,返回原值不变。
ApplyMin(value, minimum) =>
    math.max(minimum, value)

ApplyMin() 函数有两个参数。value 指的是我们希望限制其最小范围的数值,而 minimum 则用于设定可接受的最低值应该是多少。

在函数内部,math.max() 函数会返回 value 和其定义的 minimum 这两者中的较大值。这使得该函数返回的新数值永远不会小于我们所指定的最小值。

快速范例

让我们看看如何使用这个函数。比方说,我们想计算收盘价的变化,但不想得到负值。在这种情况下,我们可以像这样调用 ApplyMin()

// 计算单根K线的收盘价变化,最小值为0
closeChange = ApplyMin(0, close - close[1])

如果我们需要使用策略的净利润进行计算,但不希望使用低于250的值,我们可以这样使用 ApplyMin()

// 计算策略的净利润,最小值为250
netProfit = ApplyMin(250, strategy.netprofit)

我们也可以使用另一个变量或表达式作为最小值。

让我们假设我们使用平均真实波幅(ATR)来计算订单大小,但我们不希望使用小于15个 tick 的 ATR 值。在这种情况下,我们可以像这样调用 ApplyMin()

// 计算14周期的平均真实波幅(用于仓位大小),
// 但使用15个 tick 作为最小值
minimumAtr = ApplyMin(15 * syminfo.mintick, ta.atr(14))

脚本范例

下面的指标展示了我们如何在脚本中使用 ApplyMin() 函数。该代码计算单根K线的成交量变化,并通过自定义函数确保成交量变化值永远不会低于零。当我们绘制这些值时,便可以轻松识别出成交量有较大增长的K线。

该脚本的完整代码如下:

//@version=5
indicator(title="Minimum value example", format=format.volume)

// ApplyMin() 为指定的值应用给定的最小值范围。
// 当值高于最小值时,返回原值不变。
ApplyMin(value, minimum) =>
    math.max(minimum, value)

// 计算单根K线的成交量差值,最小值为
// 0 (这样我们就不会绘制负的成交量变化)
volumeChange = ApplyMin(0, volume - volume[1])

// 用成交量变化值创建一个柱状图
plot(series=volumeChange, style=plot.style_columns, title="Volume change")

首先,我们使用 Pine Script 的 indicator() 函数来配置指标,用于命名该脚本,并将绘图值格式化为成交量数字,用 K 和 M 分别代表千和百万。

下一步,我们引入了上面讨论过的 ApplyMin() 函数。

然后,我们创建 volumeChange 变量,并使用 ApplyMin() 函数来限制其值。我们向该函数传入两个值:第一个是最小值(0),另一个是单根K线的成交量变化。为了得到这个变化值,我们用当前K线的成交量(volume)减去前一根K线的成交量(volume[1])。

plot() 函数随后在图表上显示成交量的变化。我们让 volumeChange 变量以柱状图(plot.style_columns)的形式显示。

当这个脚本在图表上运行时,它的绘图只显示成交量有正向变化的K线柱:

设置一个最大值

当我们为一个数值设定一个最大值时,我们实际上是为其值添加了一个上限。这使得该数值将永远等于或低于某个特定水平,其值再也不能超过给定的最大值。

自定义函数

下面的这个 ApplyMax() 自定义函数可以为一个给定的数值应用一个最大值上限:

// ApplyMax() 为指定的值应用给定的最大值范围。
// 当值低于最大值时,返回原值不变。
ApplyMax(value, maximum) =>
    math.min(maximum, value)

这个 ApplyMax() 函数有两个参数。value 指的是我们希望限制其最大范围的数值,而 maximum 则用于设定可接受的最高值应该是多少。

在函数内部,math.min() 函数会返回 value 和其定义的 maximum 这两者中的较小值。这使得该函数返回的新数值永远不会多于我们所指定的最大值。

快速范例

让我们看看如何使用 ApplyMax() 函数。如果我们对单根K线的收盘价变化感兴趣,但又不要正值,我们可以像这样使用该函数:

// 计算单根K线的收盘价变化,最大值为0
// (这样,当绘制时,只会显示负值)
closeChange = ApplyMax(0, close - close[1])

再比如,我们通过一个输入来为一个动量震荡指标设定交叉水平,但不希望使用高于5的交叉水平。在这种情况下,我们可以这样使用 ApplyMax()

// 从一个输入选项决定动量指标的
// 上穿水平应该是多少,
// 并设置最大值为5
momCrossLevel = ApplyMax(5, crossLevelInput * 2)

当我们在计算仓位大小时,我们可能不希望交易超过500股。要实现交易1.5%的账户权益或不超过该限制,我们可以像这样调用 ApplyMax()

// 计算仓位大小,最大为500股
positionSize = ApplyMax(500, strategy.equity * 0.015)

最大值不必是一个字面值,我们也可以通过计算或使用一个变量来得到最大值。

比方说,我们的止损距离应该是前一根K线与当前K线低点之间的距离,但不能超过20个 tick。要实现这样的最大值限制,我们可以像这样使用 ApplyMax()

// 止损距离当前价的距离,
// 不使用超过20个 tick 的距离
stopDistance = ApplyMax(20 * syminfo.mintick, math.abs(low - low[1]))

脚本范例

让我们来探究一个脚本如何使用 ApplyMax() 函数。下方的指标计算单根K线的成交量变化,并通过 ApplyMax() 函数为其应用一个0的最大值。当绘图在图表上显示这些值时,它会凸显出那些成交量下降的K线。

该脚本的完整代码如下:

//@version=5
indicator(title="Maximum value example", format=format.volume)

// ApplyMax() 为指定的值应用给定的最大值范围。
// 当值低于最大值时,返回原值不变。
ApplyMax(value, maximum) =>
    math.min(maximum, value)

// 计算单根K线的成交量差值,最大值为
// 0 (所以我们只对成交量下降的K线感兴趣)
volumeChange = ApplyMax(0, volume - volume[1])

// 用成交量变化值创建一个柱状图
plot(series=volumeChange, style=plot.style_columns, title="Volume change")

首先,我们调用 indicator() 函数来配置指标。这为脚本提供了一个名称,并使其绘图格式显示为成交量,用 K 代表千、M 代表百万。

然后,我们引入了上面讨论过的 ApplyMax() 函数。

接下来,我们计算单根K线的成交量变化,并使用 ApplyMax() 来限制其值。因此,在该函数内部,我们用当前K线的成交量(volume)减去上一根K线的成交量(volume[1])。我们传入 0 作为最大值,来告诉 ApplyMax() 不要返回高于零的值。我们将返回的值存入 volumeChange 变量。

然后,plot() 函数在图表上显示该变量。我们让单根K线的成交量差异以柱状图(plot.style_columns)的形式显示。

当该脚本在图表上运行时,没有任何成交量变化能够高于0这个最大值:

设置一个范围(从最小到最大值)

当我们为一个数值设定一个范围时,我们实际上是为其值同时添加了一个上限和一个下限。这样,该数值将永远等于或高于指定的最小值,但绝不会超过其最大值。

自定义函数

下面的这个自定义函数 ApplyRange() 可以为一个给定的数值应用一个最小值和最大值范围:

// ApplyRange() 为指定的值应用给定的最小值和最大值范围。
// 当值在最小-最大范围内时,返回原值不变。
ApplyRange(value, minimum, maximum) =>
    math.min(math.max(value, minimum), maximum)

该函数有三个参数。value 是我们希望应用范围的数值。minimummaximum 则定义了其可接受的最低和最高值应该是多少。

该函数的代码将 math.max() 函数嵌套在 math.min() 函数内部。让我们先看内层的 math.max()。这个函数会返回给定的数值或其最小值中的较大者,这就确保了结果不会低于我们设定的最小值。

然后,Pine Script 将 math.max() 的结果用于外层的 math.min() 函数。后者会返回给定的最大值或 math.max() 返回的结果中的较小值。通过这种方式,我们得到的值就不会高于设定的最大值。

由于这两层过滤,ApplyRange() 可能返回三种值:未经改变的原值、设定的最小值或设定的最大值。无论函数最终返回哪个值,这个数现在都被限制在了最小值和最大值之间。其结果是,我们从这个数值中移除了异常的离群值。

快速范例

让我们来探究一些例子。比方说,我们的脚本计算平均真实波幅(可能用于确定订单大小),但我们不希望 ATR 值过高或过低。要使用5的最小值和25的最大值,我们可以像这样调用 ApplyRange() 函数:

// 获取14周期的平均真实波幅 (ATR),最小值为
// 5个点,最大值为25个点。
atrValue = ApplyRange(ta.atr(14), 5, 25)

再假设,我们用账户权益的1%来计算仓位大小,但我们希望至少交易10股,且绝对不超过2,500股。要实现这个取值范围,我们可以这样使用 ApplyRange()

// 计算仓位大小,最小为10股
// 最大为2,500股。
positionSize = ApplyRange(0.01 * strategy.equity, 10, 2500)

我们也可以通过变量或计算来动态设定最小值和最大值。

比方说,我们想用最近的最高高点和最低低点来设定一个止盈目标的距离,但我们不希望目标距离小于20个 tick 或超过100个 tick。要得到该范围内的目标距离,我们可以这样做:

// 计算最近的价格范围
recentRange = ta.highest(high, 20) - ta.lowest(low, 20)

// 决定离当前价的盈利目标距离。但不要
// 将目标放置在超过100个 tick 或少于20个 tick 远的地方。
targetDistance = ApplyRange(recentRange, 20 * syminfo.mintick, 
     100 * syminfo.mintick)

脚本范例

让我们看看脚本如何使用 ApplyRange() 函数。下方的指标计算单根K线的成交量变化。为了不让异常的离群值扭曲图表,该脚本将成交量差异的范围限制在最小-10,000和最大10,000之间。

该脚本的完整代码如下:

//@version=5
indicator(title="Range (minimum-maximum) example", format=format.volume)

// ApplyRange() 为指定的值应用给定的最小值和最大值范围。
// 当值在最小-最大范围内时,返回原值不变。
ApplyRange(value, minimum, maximum) =>
    math.min(math.max(value, minimum), maximum)

// 计算单根K线的成交量差值,但通过使用
// -10k 的最小值和 10k 的最大值来排除离群值
volumeChange = ApplyRange(volume - volume[1], -10000, 10000)

// 用一个柱状图显示有界的成交量变化
plot(series=volumeChange, style=plot.style_columns, title="Volume change")

首先,我们使用 indicator() 函数配置指标。它为脚本命名,并且 format.volume 值将绘图数值格式化为成交量,用 K 和 M 后缀分别代表千和百万。

然后,我们引入了上面讨论过的自定义 ApplyRange() 函数。

接下来,我们创建 volumeChange 变量,并使用 ApplyRange() 函数来限制其值。我们向该函数传入三个参数:第一个是单根K线的成交量变化,为此我们用当前K线的成交量(volume)减去前一根K线的成交量(volume[1]);第二个和第三个参数分别是-10000的最小值和10000的最大值。

然后,我们用 plot() 函数绘制该变量。这使得单根K线的成交量差异以柱状图(plot.style_columns)的形式显示。

当我们将该脚本添加到图表时,其成交量变化被整齐地限制在了-10k到10k的范围内:

赞(0)
未经允许不得转载:图道交易 » Pine Script(122):math.min与math.max,把数值限制在指定范围
分享到

评论 抢沙发

登录

找回密码

注册