理解和转换 ThinkScripts CompoundValue 函数

Understanding & Converting ThinkScripts CompoundValue Function

我目前正在将 ThinkScript 指示器转换为 C#,但是,我已经 运行 到这个 CompoundValue 函数中,但我不确定如何转换它。

文档内容如下:

Calculates a compound value according to following rule: if a bar number is greater than length then the visible data value is returned, otherwise the historical data value is returned. This function is used to initialize studies with recursion.

使用示例:

declare lower;
def x = CompoundValue(2, x[1] + x[2], 1);
plot FibonacciNumbers = x;

我的解读:

基于描述和示例。看来我们正在 x[1] + x[2] 中传递一个计算,它在当前柱和前一个柱上执行此计算(基于 2 的第一个参数)。我不确定参数 1 的用途。

我的问题:

请解释这个函数的实际作用。如果可能,请使用伪代码说明此方法的工作原理。

请让我提供 thinkscript 本身代码的两个等效工作版本。我们使用这种方法通过将等效输出彼此相减来证明等效性 - 结果应为 0.

# The original Fibonacci code with a parameter "length" added.
# That parameter is the first parameter of the CompoundValue function.
declare lower;
def length = 2;
def x = CompoundValue(length, x[1] + x[2], 1);
# plot FibonacciNumbers = x;

# Equivalent code using the `if` statement:
def y;
if(BarNumber() > length){
    # Visible data. This is within the guarded branch of the if statement.
    # Historical data y[1] (1 bar back) and y[2] (2 bars back) is available
    y = y[1] + y[2];
}else{
    # Not enough historical data so we use the special case satisfying the
    # original rule.
    y = 1;
}
plot FibonacciNumbersDiff = y - x;

Thinkscript "recursion" 是一个有点夸张的术语。函数名称 CompoundValue 不是很有用,因此可能会造成混淆。

使用if语句的版本通常更有用,因为在遍历时间序列的柱时,我们经常需要一个包含多个嵌套if语句的程序结构——这是做不到的使用 CompoundValue 函数。请参阅我在扫描上下文中使用它的其他文章。

在Java中,使用相同的结构,看起来是这样的:

int size = 100;
int length = 2;
int[] values = new int[size];
for(int index = 1; index < size; index++){
    if(index > length){
        values[index] = values[index - 1] + values[index - 2];
    }else{
        values[index] = 1;
    }
}

根本区别在于 for 循环,它在 thinkscript 代码中不存在。 thinkscript 以一种控制反转的方式提供循环,它多次执行用户代码,每个柱执行一次。

对于 TLDR; crowd,一些简单的代码,希望可以解释 CompoundValue() 函数正在尝试做什么,这可能有助于转换它的功能:

# from: Chapter 12. Past/Future Offset and Prefetch
# https://tlc.thinkorswim.com/center/reference/thinkScript/tutorials/Advanced/Chapter-12---Past-Offset-and-Prefetch

# According to this tutorial, thinkScript uses the highest offset, overriding
#   all lower offsets in the script - WOW


declare lower;

# recursive addition using x[1] is overridden by 11 in the plot for
#   Average(close, 11) below; SO `x = x[1] + 1` becomes `x = x[11] + 1`
def x = x[1] + 1;

# using CompoundValue, though, we can force the use of the *desired* value
# arguments are:
# - length: the number of bars for this variable's offset (`1` here)
# - "visible data": value to use IF VALUES EXIST for a bar (a calculation here)
# - "historical data": value to use IF NO VALUE EXISTS for a bar (`1` here)
def y = CompoundValue(1, y[1] + 1, 1);

# *plotting* this Average statement will change ALL offsets to 11!
plot Average11 = Average(close, 11); 

# `def`ing the offset DOES NOT change other offsets, so no issue here
#    (if the `def` setup DID change the offsets, then `x[1]` would 
#    become `x[14]`, as 14 is higher than 11. However, `x[1]` doesn't change.
def  Average14 = Average(close, 14);

plot myline = x;
plot myline2 = y;

# add some labels to tell us what thinkScript calculated
def numBars = HighestAll(BarNumber());
AddLabel(yes, "# Bars on Chart: " + numBars, Color.YELLOW);
AddLabel(yes, "x @ bar 1: " + GetValue(x, numBars), Color.ORANGE);
AddLabel(yes, "x @ bar " + numBars + ": " + x, Color.ORANGE);

AddLabel(yes, "y @ bar 1: " + GetValue(y, numBars), Color.LIGHT_ORANGE);
AddLabel(yes, "y @ bar " + numBars + ": " + y, Color.ORANGE);


现在,一些,呃,很多细节...

首先,关于“偏移”值的快速说明

thinkScript 与其他交易相关语言一样,使用内部循环系统。这就像一个 for 循环,遍历图表上的所有“周期”或“柱”(例如,日线图上 1 柱 = 1 天;1 分钟盘中图表上 1 柱 = 1 分钟, ETC)。 thinkScript 中的每一行代码都是 运行,用于图表中的 每个柱 或脚本中指定的时间长度。

如 OP 所述,x[1] 表示循环正在处理的当前柱 之前 一个柱的偏移量。 x[2] 表示当前柱之前的两个柱,依此类推。此外,可以使用负数抵消未来:例如,x[-1] 表示当前柱线 领先 一根柱线。

这些偏移量与 C# 中的 for 循环类似,只是它们是反向的:C# 中的 x[0] 表示当前的 x 值,就像在 thinkScript 中一样;但是,在循环中向前移动,x[1] 将是 下一个 值,而 x[-1] 将不存在,因为在 0 之前没有过去的值.(当然,一般来说!在 C# 中肯定可以使用负数循环。重点是 thinkScript 中的正偏移索引表示 过去 柱,而 thinkScript 中的负偏移索引表示 future bars - 在 C# 中不是这样。)

此处同样重要的是“长度”的概念:在 thinkScript 中,length 参数表示您想要移动的距离 - 就像偏移量一样,但是是一个范围而不是一个特定的柱。在我上面的示例代码中,我使用了语句 plot Average11 = Average(close, 11); 在这种情况下,11 参数表示绘制 11 个柱的收盘价,即偏移 x[0]x[10].

现在,解释 CompoundValue() 函数的用途:

Chapter 12. Past/Future Offset and Prefetch thinkScript 教程解释了 thinkScript 实际上 覆盖 脚本中具有最大值的较小偏移或长度值。这意味着如果你有两个项目定义如下:

def x = x[1] + 1;
plot Average11 = Average(close, 11);

thinkScript 实际上会用 Average 语句中使用的较长长度覆盖 x[1] 偏移量 - 因此导致 x[1] 变为 x[11]!

哎呀!这意味着指定的偏移量(最高偏移量除外)对 thinkScript 没有任何意义!那么,等一下 - 对所有内容都使用相同的偏移量吗?不!这就是 CompoundValue() 发挥作用的地方...

同一章解释了 CompoundValue() 允许为变量指定偏移量 ,即使存在更高的偏移量也不会更改

带有参数标签的 CompoundValue() 函数看起来像这样:

CompoundValue(length, "visible data", "historical data")

正如 OP 指出的那样,这并不是特别清楚。以下是参数代表的内容:

  • length:此变量的偏移柱数。

    在我们的示例中,def x = x[1] + 1,有 1 个小柱偏移量,因此我们的语句以 CompoundValue(length=1, ...) 开头。相反,如果它是一个更大的偏移量,比如 14 个柱,我们将放置 CompoundValue(length=14, ...)

  • “可见数据”:thinkScript 应该执行的值或计算if DATA IS AVAILABLE for the current bar .

    同样,在我们的示例中,我们使用 x[1] + 1 的计算,因此 CompoundValue(length=1, "visible data"=(x[1] + 1), ...)。 (等式周围的括号不是必需的,但可能有助于清楚。)

  • “历史数据”:使用的值如果当前柱没有数据可用。

    在我们的示例中,如果没有可用数据,我们将使用值 1

现在,在 thinkScript 中,如果参数按顺序 and/or 提供默认值,则不需要参数标签。所以,我们可以这样写这个 CompoundValue 语句 没有 标签:

def y = CompoundValue(1, y[1] + 1, 1);

或者像这样 标签:

def y = CompoundValue(length=1, "visible data"=(y[1] + 1), "historical data"=1);

(请注意,包含空格的参数名称必须用双引号括起来。单字参数名称不需要引号。另外,为了清楚起见,我在等式周围放置了括号;这不需要。)

总结:需要 CompoundValue(...) 来确保变量使用系统 (thinkScript) 中实际需要的 offset/number 柱,否则 覆盖指定的偏移量(如果存在)。

如果脚本中的所有偏移量都相同,或者如果使用不同的编程系统,则 CompoundValue() 可以简单地分解为其适当的计算或值,例如 def x = x[1] + 1 或者,或者,一个 if/else 语句,在需要的任何柱或条件下填写所需的值。