1

在 thinkscript 图表和扫描中,任何脚本都会针对每个条形执行多次。此类执行之间的程序状态存储在数组变量中,这些变量可以直接访问,也可以通过[]或偏移量访问GetValue()。许多涉及某种模式识别的脚本需要在第一个小节通过分配初始值来初始化这些变量。在后续柱上执行时,脚本要么复制先前的值,要么创建新值。然后根据其他一些条件,脚本可能会检查当前数组条目是否仍然等于第一个数组条目,以发现数据中是否发生了有趣的事情。

以下测试脚本遵循这个简单的模式。它的唯一目的是使用扫描器来验证单个变量的第一个条目是否保留其值。

扫描在最后一个小节执行单个绘图语句。该脚本的唯一标准是测试变量保持其值并且不会被其他东西改变。在 thinkscript 中更改变量或变量数组条目是非法的,绝不应该发生。但是,此脚本显示变量的第一个条目被覆盖。这是如何发生的,如何避免?

def index;
def myVar;
if (BarNumber() == 1) {
    index = -1;
    myVar = close;
} else {
    if (close > myVar[1]) {
        myVar = close;
        index = index[1];
    } else {
        if (close <= myVar[1]) {
            index = 1;
        } else {
            index = index[1];
        }
        myVar = myVar[1];
    }
}
plot scan = GetValue(index, BarNumber() -1) == -1;
4

3 回答 3

0

这是无法避免的,因为它是一个缺陷,是 2019-06-13 扫描引擎中的一个错误。让我通过几个简单的步骤提供一个证明,所有代码都由扫描引擎为所有符号执行,以获得最大的覆盖率。

def myLowest = LowestAll(BarNumber());
plot scan = myLowest == 1;

这将返回整个集合,并证明在扫描的所有符号中的第一个柱中具有BarNumber() == 1;Always。

同样,我们从所有符号开始

def myHighest = HighestAll(BarNumber());
plot scan = BarNumber() == myHighest;

这将返回整个集合。

它证明在所有扫描中,单个绘图语句仅在最高柱上执行一次,而不管每个符号有多少柱。所以它是HighestAll(BarNumber())自己计算的,我们不需要这样做。

有了上面,我们有了一些工具,可以利用扫描引擎本身来测试一些基本情况。这很重要,因为如果我们想识别错误,我们需要有一种可靠的方法来检查实际值与预期值。之所以如此,是因为我们无法调试扫描引擎——我们必须使用这种间接方法,一种可靠的方法。

现在我们正在使用这些知识来测试扫描引擎是否成功执行了用户编写的“if”语句。

def index;
if (BarNumber() == 1) {
    index = -1;
} else {
   index = 3;
}
    
plot scan = GetValue(index, BarNumber() -1) == -1;

GetValue()函数允许我们根据每个符号的柱数使用可变偏移量进行索引。我们希望将index我们可以验证内容的第一个条目作为 number进行比较-1,并且它按预期工作,因为扫描返回集合中的所有符号。

作为最后一步,我们正在扩展 if 语句的代码以显示扫描引擎故障。我们仍在 plot 语句中执行相同的测试。但是,现在扫描引擎会破坏第一个条目index作为新代码的副作用。现在测试失败了。扫描引擎将indexat的值设置BarNumber() == 10。没有执行此操作的用户代码 - 用户代码将其设置为 -1。

def index;
def myVar;
if (BarNumber() == 1) {
    index = -1;
    myVar = close;
} else {
    if (close > myVar[1]) {
        myVar = close;
        index = index[1];
    } else {
        if (close <= myVar[1]) {
            index = 1;
        } else {
            index = index[1];
        }
        myVar = myVar[1];
    }
}
plot scan = GetValue(index, BarNumber() -1) == -1;

因此,通过少量的小步骤,我们可以逐步显示扫描引擎存在缺陷/错误,因为它无法在变量中保留值。

下面是另一个例子:

def sum;
if (BarNumber() == 1) {
    sum = 1;
} else {
    if (BarNumber() < 5) {
        sum = sum[1] + 1;
    } else {
        sum = sum[1]; # This causes the problem.
        #sum = Double.NaN;# alternative: does not corrupt previous value but useless.
    }
}
plot scan = GetValue(sum, BarNumber() -1) == 1;

让我们从观察到的行为中得出结论,编译器中可能会发生什么: 简而言之,thinkscriptif语句并不能防止超出范围的数组索引。那是一个缺陷。

细节如下:有两个条件要记住,知道图表或扫描窗口有一个左边缘,这是我们作为程序员看到的,数据似乎从这里开始:

  1. 时间序列可能在左侧图表边界的左侧之外有更多历史记录。
  2. 时间序列可能从左侧图表边界开始 - 没有更多数据。

案例2)是我们正在研究的。

因此,如果在第 1 条我们将执行index[1]or close[1],那将因为超出范围的数组索引而失败。

为了防止这种情况,我们使用 if-else 结构,如下所示:

def index
if (BarNumber() == 1) {
    index = -1;
}else{
    index = index[1];
}

我们必须假设if语句的每个分支都在需要时执行有效代码。然而,我们观察到情况并非如此。我们观察到,即使在它必须失败的条件下,分支中的index[1]术语也会被评估。这会导致整个语句在分支中失败,此时不应在分支中执行任何评估。我们可以通过替换术语来证明情况确实如此。else(BarNumber() == 1)ififelseindex[1]GetValue(index, 1)

于 2019-06-13T07:58:40.010 回答
0

另外,对于任何来这里寻找的人,实际上,如何创建一个保留其值的变量- 这意味着值不会跨栏改变,因为你不希望它 - 这里有 3 种方法......

使用递归变量,如下:

方法一if语句

# declare variable without initializing
def myVar;

# set up `if` condition for which variable should be set
if ( BarNumber() == 1 ) {
  
  # set the value you want when the condition is met
  # in this case, the variable is set to the close value at bar 1
  myVar = close;
}
# thinkScript always requires an `else` 
else {

  # now, here, if you want the variable to change, enter the desired value,
  # or, if you want it to always stay the same, then...
  myVar = myVar[1];

}

方法2if表达式

# same as above, really, but more compact; use parens as desired for clarity
def myVar = if BarNumber() == 1 then close else myVar[1];

方法 3CompoundValue()

注:一般CompoundValue()情况与上述示例相同,无需使用;但是,有时必须使用它。有关更多详细信息,请参见下面的代码。

def myVar = CompoundValue(1,
                          if BarNumber() == 2 then close[1] else myVar[1],
                          0);

plot test = myVar;

一些CompoundValue()细节

首先,参数,因为文档中的描述可能会令人困惑:

一个参数length. 它表示柱号,之后 CompoundValue()将使用第二个参数中的表达式。

1 是此参数接受的最小(和默认)值。

第二个参数"visible data"。这里的值必须是一个表达式,通常是一个计算(尽管我if在这个例子中使用了一个表达式)。

这是值超过长度CompoundValue()后将使用的数据操作。例如,对于这个例子,因为我在第一个参数中有一个 1,所以表达式在 thinkScript 处理第二个柱之前不会生效。在柱 0 和 1,第三个参数值将发挥作用。BarNumber()if

请注意,我close在表达式中使用了偏移量。由于我必须在length参数中至少输入 1,因此 thinkScript 在开始使用表达式之前将位于小节 2。因此,我必须指出前一根柱线的收盘价 ( close[1]) 确实是我想要的。

第三个参数,"historical data"是在第一个参数中的长度(条号)值之前用于条号的值,包括长度(条号)值。

在这个例子中,我输入了 0,但我也可以使用Double.NaN. 在我的情况下这并不重要,因为我不关心在计算点之前设置任何值。

CompoundValue()文档给出了一个现在很容易理解的斐波那契示例。它还说明了为什么可能为先前的柱设置一个值:

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

length= 2,所以第二个参数的计算在第三个柱出现之前不会发生。

"visible data"= x[1] + x[2],将在第 2 根柱之后(即从第 3 根柱开始)之后的每根柱进行的计算。

"historical data"= 1,因此对于柱线 1 和 2,将使用常数值 1 代替第二个参数中的计算。这适用于斐波那契计算!


至于为什么要使用CompoundValue()而不是上面的前两种方法的原因,主要是CompoundValue在绘制具有多个长度或“偏移量”的项目时需要这样做。简而言之,thinkScript 会将所有绘制的偏移量更改为等于最大偏移量。CompoundValue,与其他绘制的变量不同,保持其规定的偏移值。

有关详细信息,请参阅 thinkScript 教程的第 12 章。Past/Future Offset and Prefetch,以及我对 SO questionUnderstanding & Converting ThinkScripts CompoundValue Function 的回答

于 2021-03-31T20:31:13.477 回答
-1

我在 2021 年 3 月 30 日对此进行测试,因此 OP 的原始问题可能已修复

首先,为了看看 ThinkOrSwim 对这段代码做了什么,我拿了第一个例子,把它放到了一个图表研究中。使用标签和图表气泡,我可以演示 thinkScript 处理每个条形时发生的情况。

  • 测试代码:
# OP's example code
def index;
def myVar;
if (BarNumber() == 1) {
    index = -1;
    myVar = close;
} else {
    if (close > myVar[1]) {
        myVar = close;
        index = index[1];
    } else {
        if (close <= myVar[1]) {
            index = 1;
        } else {
            index = index[1];
        }
        myVar = myVar[1];
    }
}
#plot scan = GetValue(index, BarNumber() -1) == -1;


# labels; do non-variable offset values show correct values based on code?
def numBars = HighestAll( BarNumber() );
AddLabel(yes, " numBars: " + numBars + " ", Color.CYAN);

def barNum = if BarNumber() == 0 then 0 
             else if BarNumber() == 1 then 1 else -6;
AddLabel(yes,
 " Bar 1: " + "index[5]: " + index[5] + ", GetValue(index, 5): " + GetValue(index, 5) + " ",
 Color.LIGHT_ORANGE );
AddLabel(yes,
 " Bar 2: " + "index[4]: " + index[4] + ", GetValue(index, 4): " + GetValue(index, 4) + " ",
 Color.LIGHT_ORANGE );
AddLabel(yes,
 " Bar 3: " + "index[3]: " + index[3] + ", GetValue(index, 3): " + GetValue(index, 3) + " ",
 Color.LIGHT_ORANGE );
AddLabel(yes,
 " Bar 4: " + "index[2]: " + index[2] + ", GetValue(index, 2): " + GetValue(index, 2) + " ",
 Color.LIGHT_ORANGE );
AddLabel(yes,
 " Bar 5: " + "index[1]: " + index[1] + ", GetValue(index, 1): " + GetValue(index, 1) + " ",
 Color.LIGHT_ORANGE );
AddLabel(yes,
 " Bar 6: " + "index[0]: " + index[0] + ", GetValue(index, 0): " + GetValue(index, 0) + " ",
 Color.LIGHT_ORANGE );


# chart bubbles; displaying variable values - are they what we expect?
AddChartBubble(yes, high,
  "Bar Number: " + BarNumber() + "\nclose: " + close + "\nmyVar: " + myVar + "\nindex: " + index, 
  Color.YELLOW, if BarNumber() % 2 == 0 then no else yes);

# yes! the first entry of both variables actually remain the same
AddChartBubble(yes, low,
  "BarNumber() -1 == " + (BarNumber() -1) + "\nGetValue(index, " + (BarNumber() -1) + ") == " + GetValue(index, BarNumber() -1) + "\nGetValue(myVar, " + (BarNumber() -1) + ") == " + GetValue(myVar, BarNumber() -1), 
  Color.YELLOW,  if BarNumber() % 2 == 0 then yes else no);


我将图表设置为显示 6 个(每日)柱,因此结果很容易看到。

  • 以下是 2021 年 3 月 19 日至 2021 年 3 月 26 日期间 TRCH 图表上的结果:

TRCH 图表,2021 年 3 月 19 日至 2021 年 3 月 26 日,带有标签和图表气泡

可以看到两者 index的值和myVar第一个柱的值确实保持了它们的值。在每个条的处理过程中,它们也确实发生了变化。


现在来测试扫描:扫描函数是一个过滤器,它返回任何满足编码条件的股票。对于扫描,我们可以创建一个显示指定值的列,而不是基于返回的结果数量进行测试。

  • 所以,首先我创建了一个只包含示例代码的研究:
def index;
def myVar;
if (BarNumber() == 1) {
    index = -1;
    myVar = close;
} else {
    if (close > myVar[1]) {
        myVar = close;
        index = index[1];
    } else {
        if (close <= myVar[1]) {
            index = 1;
        } else {
            index = index[1];
        }
        myVar = myVar[1];
    }
}
plot scan = GetValue(index, BarNumber() -1);


  • 然后,我根据该研究创建了一个自定义列。当我添加指向自定义研究的绘图时,thinkScript 将拉入当前代码并设置列代码。在这种情况下,完成的列代码如下所示:
script SO_ScanProofOfConcept_Column {
def index;
def myVar;
if (BarNumber() == 1) {
    index = -1;
    myVar = close;
} else {
    if (close > myVar[1]) {
        myVar = close;
        index = index[1];
    } else {
        if (close <= myVar[1]) {
            index = 1;
        } else {
            index = index[1];
        }
        myVar = myVar[1];
    }
}
plot scan = GetValue(index, BarNumber() -1);

}
plot scan = SO_ScanProofOfConcept_Column().scan;

现在,我可以看到自定义列中的所有内容实际上都是 -1.0。浏览从左起第 4 列,从“Custom04-...”开始:

ThinkOrSwim 扫描结果显示自定义列


所以,至少在这个日期,扫描引擎会像我们预期的那样保留变量的值。数据数组中的数据似乎没有损坏。

于 2021-03-30T22:10:12.637 回答