了解和转换ThinkScripts CompoundValue函数

时间:2019-11-23 01:11:33

标签: technical-indicator thinkscript

我目前正在将ThinkScript指示器转换为C#,但是,我遇到了这个CompoundValue函数,并且不确定如何隐蔽它。

文档显示为:

  

根据以下规则计算复合值:   如果数字大于长度,则返回可见数据值,   否则,返回历史数据值。使用此功能   用递归初始化研究。

示例用法:

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

我的解释:

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

我的问题:

请解释该功能的实际作用。如有可能,请使用伪代码说明此方法的工作原理。

2 个答案:

答案 0 :(得分: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;

思维脚本“递归”是一个有点夸张的术语。函数名称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提供了一种控制反转形式的循环,其中它多次执行用户代码,每条执行一次。

答案 1 :(得分:1)

对于 TLDR;人群,一些简单的代码,希望能解释 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 分钟,等等)。 thinkScript 中的每一行代码都针对每个柱形或脚本中指定的时间长度运行。

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

这些偏移的工作方式与 C# 中的 for 循环类似,只是它们是向后的:C# 中的 x[0] 表示当前的 x 值,就像在 thinkScript 中一样;但是,在循环中向前移动,x[1] 将是 next 值,而 x[-1] 将不存在,因为在 0 之前没有过去的值。(在一般,当然!在 C# 中绝对可以使用负数循环。重点是 thinkScript 中的正偏移索引代表 过去 柱,而 thinkScript 中的负偏移索引代表 未来条形 - 在 C# 中不是这种情况。)

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

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

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

x[10]

thinkScript 实际上会用在 Average 语句中使用的更长的长度覆盖 def x = x[1] + 1; plot Average11 = Average(close, 11); 偏移量 - 因此导致 x[1] 变成 x[1]

哎呀!这意味着指定的偏移量,除了最高偏移量,对 thinkScript 没有任何意义!那么,等一下 - 是否有人 必须 对所有内容使用所有相同的偏移量,然后呢?不!这就是 CompoundValue() 的用武之地...

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

带有参数标签的 CompoundValue() 函数如下所示

x[11]

正如 OP 所指出的,这并不是特别清楚。以下是参数代表的含义:

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

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

  • “可见数据”:thinkScript 应该执行的值或计算如果当前柱的数据可用

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

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

    在我们的示例中,如果没有可用数据,我们将使用值 CompoundValue(length=1, "visible data"=(x[1] + 1), ...)

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

1

或者像这样带有标签:

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

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

总结:需要 CompoundValue(...) 以确保变量使用系统 (thinkScript) 中实际所需的偏移量/柱线数量,否则覆盖指定的偏移量(如果存在)具有更高的数字。

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