在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;
答案 0 :(得分:0)
无法避免这种情况,因为它是缺陷,即截至2019-06-13的扫描引擎中的错误。让我通过几个简单的步骤提供证明,即扫描引擎为“所有符号”执行的所有代码,以获得最大的覆盖范围。
def myLowest = LowestAll(BarNumber());
plot scan = myLowest == 1;
这将返回整个集合,并证明在扫描的所有符号的第一个栏中始终为BarNumber() == 1;
。
同样,我们从
开始所有符号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
的第一项,在这里我们可以将内容验证为数字-1
,并且它按预期方式工作,因为扫描会返回集合中的所有符号。
作为最后一步,我们正在扩展if语句的代码以显示扫描引擎故障。我们仍在plot语句中执行相同的测试。但是,现在扫描引擎破坏了index
的第一个条目,这是新代码的副作用。现在测试失败。扫描引擎将index
处BarNumber() == 1
的值设置为0
。没有执行此操作的用户代码-用户代码将其设置为-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;
答案 1 :(得分:0)
另外,对于来这里寻找的任何人来说,实际上如何创建一个保留其值的变量 - 这意味着该值不会跨条变化,因为您不想 - 这里有 3 种方法...
使用递归变量,如下:
方法 1:if statement
# 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];
}
方法 2:if expression
# same as above, really, but more compact; use parens as desired for clarity
def myVar = if BarNumber() == 1 then close else myVar[1];
方法 3:CompoundValue()
注意: 一般情况下,CompoundValue()
的作用与上述示例相同,因此无需使用它;然而,它有时必须被使用。有关更多详细信息,请参阅下面的代码。
def myVar = CompoundValue(1,
if BarNumber() == 2 then close[1] else myVar[1],
0);
plot test = myVar;
一些CompoundValue()
细节
首先是参数,因为 docs 中的描述可能会令人困惑:
第一个参数是 length
。它表示柱号之后 CompoundValue()
将使用第二个参数中的表达式。
1 是此参数接受的最小(和默认)值。
第二个参数是 "visible data"
。这里的值必须是一个表达式,通常是一个计算(虽然我在这个例子中使用了一个 if
表达式)。
这是一旦 CompoundValue()
值超过长度时BarNumber()
将使用的数据操作。例如,对于这个例子,因为我在第一个参数中有一个 1,所以 if
表达式在 thinkScript 处理 2nd 条之前不会生效。在第 0 和第 1 小节,第三个参数值将起作用。
请注意,我在表达式中为 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]
,将在 第二个柱之后(即从第三个柱向前)的每个柱进行计算。
"historical data"
= 1,因此对于柱 1 和柱 2,将使用常量值 1 代替第二个参数中的计算。这适用于斐波那契计算!
至于为什么要使用CompoundValue()
而不是上面的前两种方法,主要是在绘制具有多个长度或“抵消”。简而言之,thinkScript 会将所有绘制的偏移量更改为等于最大偏移量。 CompoundValue
与其他绘制的变量不同,保持其规定的偏移值。
有关详细信息,请参阅 thinkScript 教程的 Chapter 12. Past/Future Offset and Prefetch,以及我对 SO 问题 Understanding & Converting ThinkScripts CompoundValue Function
的回答答案 2 :(得分:-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 个(每日)柱状图,因此结果很容易看到。
可以看到 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);
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-...”开始:
所以,至少在这个日期,扫描引擎会按照我们的预期保留变量的值。数据数组中的数据似乎没有损坏。 >