Sorting in Pine Script - sorting

Looks like this basic functionality is missing from v4 but is there a way to hack a solution?
I have several moving averages and would like to sort them by how far they are from the close value.

I ended up doing this. Not sure if the first for loop is needed but it's there in case the order of the cases affects the outcome.
get_diff(up, src, series1, series2, series3) =>
diff = src * 999
for c = 1 to 2
for i = 1 to 3
val = 0.0
if i == 1
val := series1
if i == 2
val := series2
if i == 3
val := series3
diff := up and val > src and val - src < diff ? val - src : not up and val < src and src - val < diff ? src - val : diff
if diff == src * 999
diff := 0
diff
support1 = close - get_diff(false, close, ma1, ma2, ma3)
support2 = support1 - get_diff(false, support1, ma1, ma2, ma3)
support3 = support2 - get_diff(false, support2, ma1, ma2, ma3)
resistance1 = close + get_diff(true, close, ma1, ma2, ma3)
resistance2 = resistance1 + get_diff(true, resistance1, ma1, ma2, ma3)
resistance3 = resistance2 + get_diff(true, resistance2, ma1, ma2, ma3)

Related

How to draw a fan in Pine v5? Lines connecting open/close in a range?

Hello I'm new to pine and i have a question.
I would like to write a script that helps me drawing lines connecting the close of the first candle in a selected range and the close of every other candle in the range.
I think I have some problem understanding pine runtime because using for loops or conditional structures seems bad but I can't find a solution around this.
I tried with if but had no succes, the idea was that
after i select the start/end point, the code should be something like this:
if bar_index > bar_index[barStart] and bar_index < bar_index[barEnd]
line.new(bar_index[barStart], close[barStart], bar_index, close)
else na
After this I tried with a for loop, again with no success:
for i = bar_index[barStart]+1 to bar_index[barEnd]
line.new(bar_index[barStart], close[barStart], bar_index[i], close[i])
The code I use to select the range and count the candles inside it is this one:
//#version=5
indicator("Close", overlay=true)
// Range Start
t0 = input.time(timestamp("20 Jul 2021 00:00 +0300"), confirm = true)
p0 = input.price(defval = 0, confirm = true)
// Range End
t1 = input.time(timestamp("20 Jul 2021 00:00 +0300"), confirm = true)
p1 = input.price(defval = 0, confirm = true)
///////////////////////////////////////////////////////////////////////////////////
// Bar counting
t_bar(_t) =>
var int _bar = na
if time_close[1] <= _t and time >= _t
_bar := bar_index
_bar
start = int(t_bar(t0))
end = int(t_bar(t1))
//Counting bars in the selected range
barStart = bar_index - start
barEnd = bar_index - end
barDelta = end - start
//Print results
plot(barStart, "Range start")
plot(barEnd, "Range end")
plot(barDelta, "Candles in range")
But from here on I don't know how to proceed. This should be pretty easy but I'm stuck.
What I'm trying to draw
Thank you to anyone willing to help!!
You don't need the loop or the input price variables. The lines can be drawn bar by bar as the script's execution enters your time range and the price variables can also be obtained at the same time.
//#version=5
indicator("Close", overlay=true)
// Range Start
t0 = input.time(timestamp("20 Jul 2021 00:00 +0300"), confirm = true)
// Range End
t1 = input.time(timestamp("20 Jul 2021 00:00 +0300"), confirm = true)
///////////////////////////////////////////////////////////////////////////////////
first_bar = time >= t0 and time[1] < t0
in_range = time > t0 and time <= t1
post_bar = time > t1 and time[1] <= t1
var float start_close = na
var int start_index = na
if first_bar
start_close := close
start_index := bar_index
if in_range and not first_bar
line.new(x1 = start_index, y1 = start_close, x2 = bar_index, y2 = close)
if post_bar
num_bars = bar_index[1] - start_index
delta = close[1] - start_close
info_text = "Start Bar : " + str.tostring(start_index) + "\nEnd Bar : " + str.tostring(bar_index[1]) + "\nNumber of bars : " + str.tostring(num_bars) + "\nPrice delta : " + str.tostring(delta)
label.new(x = bar_index[1], y = high[1], style = label.style_label_lower_left, size = size.small, text = info_text)
Follow up question :
To draw the lines on a higher timeframe and have them "persist" once you move to a lower timeframe is a bit trickier. You will have to use an input to manually set the higher timeframe as the script has no way to determine the previous timeframe that it was applied to.
When you set t0 and t1 on the higher timeframe, the timestamp values will correspond to the opening time for those higher time frame bars. This isn't ideal as the lower timeframe candle that starts at this same time isn't the close value we are after.
By using request.security() we can then get the actual closing time of the higher timeframe bar which has the closing value we do want.
So we can use time to determine when we've started the correct higher time frame bars and then use time_close to determine when we are on the lower time frame bar that coincides with the higher timeframe close.
//#version=5
indicator("MTF Close", overlay=true)
// Range Start
t0 = input.time(timestamp("20 Jul 2021 00:00 +0300"), confirm = true)
// Range End
t1 = input.time(timestamp("20 Jul 2021 00:00 +0300"), confirm = true)
///////////////////////////////////////////////////////////////////////////////////
tf = input.timeframe("240", title = "higher timeframe")
htf_close = request.security(syminfo.tickerid, tf, time_close)
is_htf_closing_bar = time_close == htf_close
new_htf = ta.change(time(tf)) != 0
var bool started_first_htf_bar = false
var float start_close = na
var int start_index = na
var bool started_last_htf_bar = false
if time >= t0 and time[1] < t0 and new_htf
started_first_htf_bar := true
else if new_htf
started_first_htf_bar := false
if started_first_htf_bar and is_htf_closing_bar and na(start_close)
start_close := close
start_index := bar_index
else if not started_first_htf_bar and is_htf_closing_bar and time > t0 and time < t1
line.new(x1 = start_index, y1 = start_close, x2 = bar_index, y2 = close)
if time >= t1 and time[1] < t1 and new_htf
started_last_htf_bar := true
else if new_htf
started_last_htf_bar := false
if started_last_htf_bar and is_htf_closing_bar
line.new(x1 = start_index, y1 = start_close, x2 = bar_index, y2 = close)
post_bar = new_htf and started_last_htf_bar[1]
if post_bar
num_bars = bar_index[1] - start_index
delta = close[1] - start_close
info_text = "Start Bar : " + str.tostring(start_index) + "\nEnd Bar : " + str.tostring(bar_index[1]) + "\nNumber of bars : " + str.tostring(num_bars) + "\nPrice delta : " + str.tostring(delta)
label.new(x = bar_index[1], y = high[1], style = label.style_label_lower_left, size = size.small, text = info_text)

Negative index caused by undeclared identifier in 'for' statement

I'm trying to test my script, and I wanted to plot some of the variables I have on it. The problem is that everything that is being ploted before the 'for' statement works, but nothing comes to work beyond that. I made a test to plot the 'bar' variable, and when I do so, it pops an error: Undeclared identifier 'bar'. This happens when I try to plot the 'bar' variable.
The 'SwingIndex' variable contains the result that I finally want to plot in the indicator, but when I try to plot it, it comes an error on the indicator window that says 'Study Error Negative index -1'. Something is causing a problem somewhere inside the 'for' statement.
Personally, I see that since the 'bar' variable is not working (declared in line 39 of my code), nothing else is. But I don't know why is not working.
Can anyone help me to find where is my mistake? I'll leave the code down here.
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © TD_DaFen
//#version=5
// Indicator name
indicator("DAF_Swing_Index", shorttitle= 'DAF_SwInd', overlay=false)
// Input
T = input.int(30000, title = 'Ratio de escala', minval = 1000, maxval = 150000)
Shift = input.int(0, title = 'Desplazamiento horizontal', minval = 0, maxval = 100)
// Other variable
var float SwingIndex = 0
var int StartBars = 1
Prev_calculated = bar_index
Rates_total = bar_index + 1
var float SH1 = 0
var float SI = 0
var float R = 0
// Array
Open = array.new_float(Rates_total)
Open1 = array.new_float(Rates_total)
Close = array.new_float(Rates_total)
Close1 = array.new_float(Rates_total)
High = array.new_float(Rates_total)
Low = array.new_float(Rates_total)
// Initial bar verification
if Rates_total < StartBars
SwingIndex := 0
Primero = 1
if Prev_calculated > Rates_total or Prev_calculated <= 0
Primero := 1
else
Primero := Prev_calculated -1
// Main process
for bar= Primero to Rates_total-1
array.push(Open, high[bar])
array.push(Open1, open[bar-1])
array.push(Close, close[bar])
array.push(Close1, close[bar-1])
array.push(High, high[bar])
array.push(Low, low[bar])
K = math.max(math.abs(array.get(High, bar)- array.get(Close1, bar)), math.abs(array.get(Low, bar) - array.get(Close1, bar)))
TR = math.max(math.max(math.abs(array.get(High, bar) - array.get(Close1, bar)), math.abs(array.get(Low, bar) - array.get(Close1, bar))), math.abs(array.get(High, bar) - array.get(Low, bar)))
ER = 0.0
if array.get(Close1, bar) > array.get(High, bar)
ER := math.abs(array.get(High, bar) - array.get(Close1, bar))
if array.get(Close1, bar) < array.get(Low, bar)
ER := math.abs(array.get(Low, bar) - array.get(Close1, bar))
SH1 := math.abs(array.get(Close1, bar) - array.get(Open1, bar))
R := TR - 0.5 * ER + 0.25 * SH1
SI := 0.0
if R != 0
SI := 50 * ((array.get(Close, bar) - array.get(Close1, bar)) + 0.5 * (array.get(Close, bar) - array.get(Open1, bar))) * (K / T) / R
SwingIndex := SI
// ploting partial results to see if its working
plot(SwingIndex, title = 'Swing Index', style = plot.style_line, color = color.rgb(193, 150, 51, 10))
Your problem is in your for loop. Error message tells you that you are trying to access an element at negative index -1.
Following two array.push calls are causing the problem:
for bar= Primero to Rates_total-1
array.push(Open1, open[bar-1])
array.push(Close1, close[bar-1])
That is because at the second bar of the chart, Primero becomes zero, making your for loop start from zero (for bar = Primero). Then you subtract 1 from zero and therefore try to access an element at negative index -1.
How do we know that?
Remember your script will be executed for every bar. Let's analyze the following segments of your code for the very first and second bars.
Prev_calculated = bar_index
Rates_total = bar_index + 1
Primero = 1
if Prev_calculated > Rates_total or Prev_calculated <= 0
Primero := 1
else
Primero := Prev_calculated -1
Very first bar (bar_index = 0)
Prev_calculated = 0
Rates_total = 1
Primero = 1 (if branch -> Prev_calculated <= 0 is true)
Second bar (bar_index = 1)
Prev_calculated = 1
Rates_total = 2
Primero = 0 (else branch)
Also, this Prev_calculated > Rates_total check will always be false.

Copying a circular buffer to a larger buffer while preserving contents and modulus indices

I am keeping an efficient circular buffer buf as an array and two total read and write counts bufRead and bufWrite such that bufRead % buf.length and bufWrite % buf.length are correct indices into buffer for current operations.
Now I might need to "grow" the array at some point because the buffer size expands. So I want to replace buf with a new, larger array, but preserve all the previous contents of the buffer while preserving the above modulus properties. So if at bufRead % buf.length in the old buffer we find element X, then I want this element X to be found again at the same index bufRead % buf.length after buf has been updated.
Example:
trait Algorithm {
var buf: Array[Double]
var bufRead : Long // this won't be needed in `resize`
var bufWrite: Long // this won't be needed in `resize`
def resize(newLength: Int): Unit = {
val newBuf = new Array[Double](newLength)
???
buf = newBuf
}
}
A test procedure:
def test(in: Algorithm): Unit = {
import in._
import math.{min, random}
val avail = buf.length // (bufWrite - bufRead).toInt
val data0 = Array.fill(avail)(random)
val off0 = (bufRead % buf.length).toInt
val chunk0 = min(avail, buf.length - off0)
val off1 = (off0 + chunk0) % buf.length
val chunk1 = avail - chunk0
System.arraycopy(data0, 0 , buf, off0, chunk0)
System.arraycopy(data0, chunk0, buf, off1, chunk1)
resize(avail * 2) // arbitrary growth
val data1 = new Array[Double](avail)
val off2 = (bufRead % buf.length).toInt
val chunk2 = min(avail, buf.length - off2)
val off3 = (off2 + chunk2) % buf.length
val chunk3 = avail - chunk2
System.arraycopy(buf, off2, data1, 0 , chunk2)
System.arraycopy(buf, off3, data1, chunk2, chunk3)
assert(data0 sameElements data1)
}
There are two possible approaches:
re-ordering the contents so they fit into the new modulus
for (i <- bufRead until bufWrite) {
newBuf(i % newBuf.length) = buf(i % buf.length)
}
resetting read and write pointers to fit to the new array
var j = 0
for (i <- bufRead until bufWrite) {
newBuf(j) = buf(i % buf.length)
j += 1
}
bufWrite -= bufRead
bufRead = 0
I'm not sure whether you want to keep track of the number of all elements the buffer has ever stored, if yes then the second approach doesn't work of course. The first approach, re-ordering, shouldn't be too much of a hazzle as you need to copy the contents from the old into the new array anyway.
I believe the following is correct:
class Impl(size0: Int) extends Algorithm {
var buf = new Array[Double](size0)
var bufRead = 0L
var bufWrite = 0L // unused here
override def resize(newLength: Int): Unit = {
import math.{min, max}
val newBuf = new Array[Double](newLength)
val oldLength = buf.length
val off0 = (bufRead % oldLength).toInt
val off1 = (bufRead % newLength).toInt
val chunk0 = min(oldLength - off0, newLength - off1)
System.arraycopy(buf, off0, newBuf, off1, chunk0)
val off2 = (off0 + chunk0) % oldLength
val off3 = (off1 + chunk0) % newLength
val chunk1 = min(oldLength - max(chunk0, off2), newLength - off3)
System.arraycopy(buf, off2, newBuf, off3, chunk1)
val off4 = (off2 + chunk1) % oldLength
val off5 = (off3 + chunk1) % newLength
val chunk2 = oldLength - (chunk0 + chunk1)
System.arraycopy(buf, off4, newBuf, off5, chunk2)
buf = newBuf
}
}
Test:
for (r <- 0 until 200) {
val a = new Impl(100)
a.bufRead = r // test all possible offsets
test(a)
}

Project Euler 2

I am working on problem two of Euler.
I wanted to solve it this way, to compare the time after .
//Find the sum of all the even-valued terms in the Fibonacci sequence which do not exceed four million
I should get
//A: 4613732
but I am getting a huge number :
177112424089630957537
Can someone explain why ?
def Fibonaccu(max: Int) : BigInt = {
var a:BigInt = 0
var b:BigInt = 1
var sum:BigInt= 0
var i:BigInt = 0;
while(i < max){
i+=1
b = a + b
a = b - a
if (b % 2 == 0) sum += b
}
//Return
println(sum)
sum
}
}
Here it is:
scala> val fib: Stream[Int] = 0 #:: fib.scanLeft(1)(_+_)
fib: Stream[Int] = Stream(0, ?)
scala> fib.takeWhile(4000000>).filter(_%2 == 0).sum
res0: Int = 4613732
And here is based on your code:
scala> def Fibonaccu(max: Int) : BigInt = {
| var a:BigInt = 0
| var b:BigInt = 1
| var sum:BigInt= 0
| while(b < max) {
| if(b % 2 == 0) sum += b
| b = a + b
| a = b - a
| }
| sum
| }
Fibonaccu: (max: Int)BigInt
scala> Fibonaccu(4000000)
res1: BigInt = 4613732

Give each object in scene a unique wirecolor?

Know of any scripts, or code, to do this?
I've seen several which gives random colors, but I need to make sure no two objects have the same wirecolor..
Thanks! =)
I don't know any but it's not so hard to write one.
Here you go, hope it will do what you expected it to:
fn shuffle &arr =
(
local temp, swapIndex, counter = arr.count + 1
while counter > 1 do
(
swapIndex = random 1 (counter -= 1)
temp = arr[counter]
arr[counter] = arr[swapIndex]
arr[swapIndex] = temp
)
OK
)
fn incrementCounters &r &g &b step =
(
if (b += step) > 256 do
(
b = 1
if (g += step) > 256 do
(
g = 1
if (r += step) > 256 do r = 1
)
)
)
fn assignRandomWirecolor objs simple:true =
(
local stepCount = objs.count^(double 1/3) + 1
local step = 255./stepCount
local redArr = #(0) + #{1..255}
local greenArr = copy redArr #noMap
local blueArr = copy redArr #noMap
local r = local g = local b = 1
if simple then
(
shuffle &redArr
shuffle &greenArr
shuffle &blueArr
)
else shuffle &sel -- slower with many objects
for obj in objs do
(
obj.wirecolor = [redArr[int(r)], greenArr[int(g)], blueArr[int(b)]]
incrementCounters &r &g &b step
)
)
sel = selection as array
clearSelection()
assignRandomWirecolor sel --simple:false --> if simple is not so cool, try the other option
select sel
Of course it all also depends on the purpose you want to use it for, this is just a general approach and as such it might not be suitable for that exact task. If that's the case, you can give more details and I'll make some adjustments.

Resources