If/else-like behaviour - if-statement

I'm actually having a huge problem debugging one of a script i'm programming on tradingview.
To make it simple, on the image below you'll find that at line 54, i'm calculating the value of "a" which firstly return 22.19.
After that i'm trying an if / else on which i'm basically doing the same calculation whether if my if/else is true or false and i'm not understanding why this return me a different result then the first time i'm calculating the "a" value.
Is there something obvious i'm missing there because this is actually driving me crazy ^^"
Edit 1 (with the value of Price and Price 1 ) :
Complete code :
Pastebin link : Nw2dWMi7

Let's simplify your example:
//#version=5
indicator("Dominant Cycle Tuned Rsi - V5")
var globalScope = 0.0
var localScope1 = 0.0
var localScope2 = 0.0
globalScope := ta.change(close)
if close > close[1]
// this ta.change has it's own set of values for cases when close > close[1]
localScope1 := ta.change(close)
localScope2 := na
else
localScope1 := na
// this ta.change has it's own set of values for other cases
localScope2 := ta.change(close)
plot (globalScope, color=color.red)
plot (localScope1, color=color.black)
plot (localScope2, color=color.black)
Firs of all, you're getting warning
The function 'ta.change' should be called on each calculation for consistency. It is recommended to extract the call from this scope.
From the manual: (https://www.tradingview.com/pine-script-docs/en/v4/language/Functions_and_annotations.html#execution-of-pine-functions-and-historical-context-inside-function-blocks)
The history of series variables used inside Pine functions is created
through each successive call to the function. If the function is not
called on each bar the script runs on, this will result in disparities
between the historic values of series inside vs outside the function’s
local block. Hence, series referenced inside and outside the function
using the same index value will not refer to the same point in history
if the function is not called on each bar.
The reason why you're getting different results because that when you're calling ta.change in a local scope.
if close > close[1]
// this ta.change has it's own set of values for cases when close > close[1]
localScope1 := ta.change(close)
here we comparing close and close[1] ONLY FOR cases when close > close[1].
And here
else
localScope1 := na
// this ta.change has it's own set of values for other cases
localScope2 := ta.change(close)
for the cases ONLY when close < close[1].
So results of two conditional statements and global scope will be different.

Related

Equation in if branch is not executed

I have a question that confused me for a long time. As you know, when we use an if condition in Modelica, that means if the expression is true, then Modelica will do the corresponding equation.
But when i test the following code, I am confused:
model Model134
Real a(start = 0);
equation
if not sample(0, 2) then
a = 1;
else
a = 3;
end if;
end Model134;
I think a will be changed every 2s (start time=0), but when I simulate this model, it dose not change and a is equal to 1 all the time.
Dose anybody know the root cause?
a does change its value, but depending on your simulation tool you might not see it in the plot.
sample(0, 2) creates a time event every 2 seconds. The return value of sample() is only true during the event. So the value of a changes, but after the event it immediately changes back.
In this answer to a similar question, it is mentioned that Dymola stores the value before and after the event in result file. Intermediate values are skipped for efficiency reasons (there can be many for every event, which would bloat up your result file). Hence you can not plot this change in Dymola. For OpenModelica see the answer by
Akhil Nandan.
To proof that a really does change its value you can use this code for example:
model Model134
import Modelica.Utilities.Streams.print;
Real a;
equation
if sample(0, 2) then
a = 1;
else
a = 0;
end if;
when a > 0.5 then
print("a is " + String(a) + " at t=" + String(time) + "s");
end when;
annotation (experiment(StopTime=10));
end Model134;
You should see something like this in the simulation log:
a is 1 at t=2s
a is 1 at t=4s
a is 1 at t=6s
a is 1 at t=8s
a is 1 at t=10s
This is the plot simulated when trying your above code in OpenModelica with settings shown in the second figure.
A time event is triggered when sample(startTime,interval) evaluates true at every multiple of 2 seconds and based on your code logic this should activate else
block and assign value of variable a to be 3.

thinkscript if statement failure

The thinkscript if statement fails to branch as expected in some cases. The following test case can be used to reproduce this bug / defect.
It is shared via Grid containing chart and script
To cut the long story short, a possible workaround in some cases is to use the if-expression which is a function, which may be slower, potentially leading to Script execution timeout in scans.
This fairly nasty bug in thinkscript prevents me from writing some scans and studies the way I need to.
Following is some sample code that shows the problem on a chart.
input price = close;
input smoothPeriods = 20;
def output = Average(price, smoothPeriods);
# Get the current offset from the right edge from BarNumber()
# BarNumber(): The current bar number. On a chart, we can see that the number increases
# from left 1 to number of bars e.g. 140 at the right edge.
def barNumber = BarNumber();
def barCount = HighestAll(barNumber);
# rightOffset: 0 at the right edge, i.e. at the rightmost bar,
# increasing from right to left.
def rightOffset = barCount - barNumber;
# Prepare a lookup table:
def lookup;
if (barNumber == 1) {
lookup = -1;
} else {
lookup = 53;
}
# This script gets the minimum value from data in the offset range between startIndex
# and endIndex. It serves as a functional but not direct replacement for the
# GetMinValueOffset function where a dynamic range is required. Expect it to be slow.
script getMinValueBetween {
input data = low;
input startIndex = 0;
input endIndex = 0;
plot minValue = fold index = startIndex to endIndex with minRunning = Double.POSITIVE_INFINITY do Min(GetValue(data, index), minRunning);
}
# Call this only once at the last bar.
script buildValue {
input lookup = close;
input offsetLast = 0;
# Do an indirect lookup
def lookupPosn = 23;
def indirectLookupPosn = GetValue(lookup, lookupPosn);
# lowAtIndirectLookupPosn is assigned incorrectly. The if statement APPEARS to be executed
# as if indirectLookupPosn was 0 but indirectLookupPosn is NOT 0 so the condition
# for the first branch should be met!
def lowAtIndirectLookupPosn;
if (indirectLookupPosn > offsetLast) {
lowAtIndirectLookupPosn = getMinValueBetween(low, offsetLast, indirectLookupPosn);
} else {
lowAtIndirectLookupPosn = close[offsetLast];
}
plot testResult = lowAtIndirectLookupPosn;
}
plot debugLower;
if (rightOffset == 0) {
debugLower = buildValue(lookup);
} else {
debugLower = 0;
}
declare lower;
To prepare the chart for the stock ADT, please set custom time frame:
10/09/18 to 10/09/19, aggregation period 1 day.
The aim of the script is to find the low value of 4.25 on 08/14/2019.
I DO know that there are various methods to do this in thinkscript such as GetMinValueOffset().
Let us please not discuss alternative methods of achieving the objective to find the low, alternatives for the attached script.
Because I am not asking for help achieving the objective. I am reporting a bug, and I want to know what goes wrong and perhaps how to fix it. In other words, finding the low here is just an example to make the script easier to follow. It could be anything else that one wants a script to compute.
Please let me describe the script.
First it does some smoothing with a moving average. The result is:
def output;
Then the script defines the distance from the right edge so we can work with offsets:
def rightOffset;
Then the script builds a lookup table:
def lookup;
script getMinValueBetween {} is a little function that finds the low between two offset positions, in a dynamic way. It is needed because GetMinValueOffset() does not accept dynamic parameters.
Then we have script buildValue {}
This is where the error occurs. This script is executed at the right edge.
buildValue {} does an indirect lookup as follows:
First it goes into lookup where it finds the value 53 at lookupPosn = 23.
With 53, if finds the low between offset 53 and 0, by calling the script function getMinValueBetween().
It stores the value in def lowAtIndirectLookupPosn;
As you can see, this is very simple indeed - only 38 lines of code!
The problem is, that lowAtIndirectLookupPosn contains the wrong value, as if the wrong branch of the if statement was executed.
plot testResult should put out the low 4.25. Instead it puts out close[offsetLast] which is 6.26.
Quite honestly, this is a disaster because it is impossible to predict which of any if statement in your program will fail or not.
In a limited number of cases, the if-expression can be used instead of the if statement. However the if-expression covers only a subset of use cases and it may execute with lower performance in scans. More importantly,
it defeats the purpose of the if statement in an important case because it supports conditional assignment but not conditional execution. In other words, it executes both branches before assigning one of two values.

XQuery get Random Text from a List

Suppose I have a list of 100 String Element and I want to get 50 of these random Text string to be returned Randomly.
I try to do this:
let $list := ("a","b",..."element number 100")
return xdmp:random(100)
This query return one string, I want to return back 50 strings that are distinct from each other.
Easiest to order by xdmp:random() and limit to the first 50:
(for $x in (1 to 100)
order by xdmp:random()
return $x
)[1 to 50]
xdmp:random() (as well as xdmp:elapsed-time()) return different values at each call. It would be rather impractical if it wouldn't. This opposed to for instance fn:current-dateTime() which gives the same value throughout one execution run.
Ghislain is making a good first attempt, but as also pointed out by BenW, even though xdmp:random() does return different results each time, it is not said they are unique throughout one execution run. Collisions on its 64-bit max scale are rare (though still possible), but on small scale like 10's or 100's it is likely to have some accidental repetition. It is wise to eliminate the texts from the list once chosen.
BenW beat me to posting an alternative to Ghislain, and it looks similar, but uses less lines. Posting it anyhow, in the hope someone finds it useful:
declare function local:getRandomTexts($list, $count) {
if ($count > 0 and exists($list)) then
let $random := xdmp:random(count($list) - 1) + 1
let $text := $list[$random]
return ($text, local:getRandomTexts($list[. != $text], $count - 1))
else ()
};
let $list :=
for $i in (1 to 26)
return fn:codepoints-to-string(64 + $i)
for $t in local:getRandomTexts($list, 100)
order by $t
return $t
HTH!
If you are saying the 50 strings must be distinct from one another, as in, no repeats allowed, then even if xdmp:random() does return different values when called repeatedly in the same query, getting 50 random positions in the same list is not sufficient, because there may be repeats. You need to get random positions from 50 different lists.
declare function local:pickSomeFromList($some as xs:integer, $listIn as xs:string*, $listOut as xs:string*) as xs:string* {
if($some = 0 or not($listIn)) then $listOut
else
let $random := xdmp:random(count($listIn) - 1) + 1
return local:pickSomeFromList(
$some - 1,
($listIn[fn:position() lt $random],$listIn[fn:position() gt $random]),
($listOut, $listIn[$random])
)
};
let $list := ("a","b","c","d","e","f","g","h","i","element number 10")
return local:pickSomeFromList(5, $list, ())
Assuming it returns a different result at every call (but I cannot tell from the documentation of xdmp:random whether it is the case), the following code returns 50 strings from the list picked at random (but not necessarily distinct):
let $list := ("a","b",..."element number 100")
for $i in 1 to 50
let $position = 1 + xdmp:random(99)
return $list[$position]
However, the exact behavior of xdmp:random, that is, whether it returns identical results across calls, depends on how the MarkLogic engine supports or treats nondeterministic behavior, which is outside of the scope of the XQuery specification. Strict adherence to the specification would actually return 50 times the same result with the above query.
XQuery 3.1 provides a random number generator with which you can control the seed. This allows you to generate as many numbers as you want by chaining calls, while only using interoperable behavior and staying within a fully deterministic realm.
Edit: here is a query (still assuming calls to xdmp:random are made each time) that should make sure that 50 distinct strings from the list are taken following grtjn's remark. It uses a group by clause and relies on a lazy evaluation for taking the first 50.
let $list := ("a","b",..."element number 100")
let $positions := (
for $i in 1 to 100000 (: can be adjusted to make sure we get 50 distinct :)
group by $position = 1 + xdmp:random(count($list) - 1)
return $position
)[position() le 50]
return $list[position() = $positions]
I think hunterhacker's proposal for computing the $positions is even better though.

Impute missing covariates at random in Stata

I am trying to randomly impute missing data for several covariates using Stata. I have never done this, and I am trying to use this code from a former employee:
local covarall calc_age educcat ipovcat_bl US_born alc_yn2 drug_yn lnlpcbsum tot_iod
local num = 0
foreach j of local covarall {
gen iflag_`j'=0
replace iflag_`j'=1 if `j'==.
local num = `num'+1000
forvalues i = 1/476 {
sort `j'
count if `j'==.
di r(N)
local num2 = `num'+`i'
set seed `num2'
replace `j' in `i'=`j'[1+int((400-r(N))*runiform())] if iflag_`j'[`i']==1
}
}
When I run this, Stata just gives me this over and over forever:
(0 real changes made)
0
0
What am I doing wrong?
The three messages seem interpretable as follows:
replace iflag_`j' = 1 if `j' == .
will lead to a message (0 real changes made) whenever that is so, meaning that the variable in question is never equal to system missing, the requirement for replacement.
count if `j' == .
will lead to the display of 0 in the same circumstance.
di r(N)
ditto. count shows a result by default and then the code insists that it be shown again. Strange style, but not a bug.
All that said the line
replace `j' in `i'=`j'[1+int((400-r(N))*runiform())] if iflag_`j'[`i'] == 1
is quite illegal. My best guess is that you have copied it incorrectly somehow and that it should have been
replace `j' =`j'[1+int((400-r(N))*runiform())] in `i' if iflag_`j'[`i'] == 1
but this too should produce the same message as the first if a value is not missing.
I add that it is utterly pointless to enter the innermost loop if there are no missing values in a variable: there is then nothing to impute.
Changing the seed every time a change is made is strange, but that is partly a matter of taste.

stata - variable operations conditional to existent vars and to a list of varnames

I have this problem.
My dataset has variables like:
sec20_var1 sec22_var1 sec30_var1
sec20_var2 sec22_var2 sec30_var2 sec31_var2
(~102 sectors, ~60 variables, not all of the cominations are complete or even existent)
My intention is to build an indicator that do an average of variables within sector. So it is an "aggregated sector" that contains sectors belonging to a class in a high-med-low technology fashion. I already have the definitions of what sectors should include in each category. Let's say, in high technology I should put sec20 and sec31.
The problem: the list of sectors belonging to a class and the actual sectors available for each variable doesn't match. So I'm stucked with this problem and started to do it manually. My best approach was:
set more off
foreach v in _var02 {
ds *`v'
di "`r(varlist)'"
local sects`v' `r(varlist)'
foreach s in sec26 sec28 sec37 {
capture confirm local sects`v'
if !_rc {
egen oecd_medhigh_avg_`v'=rowmean(`s'`v' sec28`v' sec37`v' sec40`v' sec59`v' sec92`v' sec54`v' sec55`v' sec48`v' sec50`v' sec53`v' sec4`v' sec5`v' sec6`v')
else {
di "`v' didnt existed"
}
}
}
}
I got it work only with those variables that has all the sectors present in the totalrow (which is simpler since I dont have to store the varlist in a macro). I would like to do an average of the AVAILABLE sectors, even if they are only two per variable.
I also noticed that the macro storage could be helpful but I don't know how to put it into my code. I'm totally stucked in here.
Thanks for your help! :)
Thank you #SOConnell. As I said in my comment, I went to the same direction, but I'm still searching for the solution I expected (that I don't how to program it or even if it's possible).
I used this code, that goes in the same direction that the one made by #SOConnell, but I found this one more clear. The trick is the _rc==111 that catches the missing combinations of sector_X_variable and complete them, with the objective of beeing used in the second part. Everything worked. It's not elegant, but it has some practical use. :) The third part erases the missing variables created.
*COMPLETING THE LIST OF COMBINATIONS
set more off
foreach v in _var02 _var03 _var08 _var13 _... {
foreach s in sec27 sec35 sec42 sec43 sec45 sec46 sec39 sec52 sec67 {
capture confirm variable s'v'
if _rc==111 {
gen s'v'=.
}
}
}
*GENERATING THE INDICATOR WITH ALL POSSIBLE COMBINATIONS
set more off
foreach v in _var02 _var03 _var08 _var13 ... {
egen oecd_high_avg_v'=rowmean(sec27v' sec35v' sec42v' sec43v' sec45v' sec46v' sec39v' sec52v' sec67v')
}
*DROPPING MISSING VARIABLES CREATED TO DO THE INDICATOR.
set more off
foreach v of varlist * {
gen TEMP=.
replace TEMP=1 if !missing(v')
egen TEMPSUM=sum(TEMP)
if TEMPSUM==0 {
di " >>> Dropping empty variable:v'"
drop `v'
}
drop TEMP TEMPSUM
}
Note that I cutted the list of variables.
I will call what you are referring to as variables as "accounts".
The workaround would be to create empty variables in the dataset for all sectorXaccount combinations. From a point where you already have your dataset loaded into memory:
forval sec = 1/102 {
forval account = 1/60 {
cap gen sec`sec'_var`account'=. /*this will skip over generating the secXaccount combination if it already exists in the dataset */
}
}
Then apply the rowmean operation to the full definition of each indicator. The missings won't be calculated into your rowmean, so it will effectively be an average of available cells without you having to do the selection manually. You could then probably automate deleting the empty variables you created if you do something like:
g start=.
forval sec = 1/102 {
forval account = 1/60 {
cap gen sec`sec'_var`account'=. /*this will skip over generating the secXaccount combination if it already exists in the dataset */
}
}
g end=.
[indicator calculations go here]
drop start-end
However, it seems like you would be creating averages that might not be comparable (some will have 2 underlying values, some 3, some 4, etc.) so you need to be careful there (but you are probably already aware of that).