0

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

文件内容如下:

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

使用示例:

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

我的解释:

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

我的问题:

请解释一下这个函数实际上在做什么。如果可能,请说明此方法如何使用伪代码工作。

4

2 回答 2

2

请让我在 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“递归”是一个有点夸张的术语。函数名称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;
    }
}

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

于 2019-11-28T15:52:53.800 回答
2

对于 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]

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

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

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

12 章过去/未来偏移和预取thinkScript 教程解释了 thinkScript 实际上会覆盖脚本中具有最高值的较小偏移或长度值。这意味着如果您有两个项目定义如下:

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

thinkScript 实际上将覆盖x[1]在 Average 语句中使用的具有更高长度的偏移量 - 因此导致x[1]变为x[11]!

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

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

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

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

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

  • 长度:此变量的柱偏移数。

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

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

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

  • “历史数据”如果当前柱没有可用数据,则使用该值。

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

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

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

或者像这样标签:

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

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

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

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

于 2021-03-25T21:40:24.383 回答