商品期货量化交易-TradingviewPine语言基础课程(十五):数组

Author: ianzeng123, Created: 2023-03-09 17:04:17, Updated:

大家好,今天我们来学习Pine语言array数组的操作。数组作为具有某一长度的元素集合,你可以添加,删除,修改你需要的元素,并进行相关的计算展示工作。尽管内置函数可以帮助我们进行某些数组集合的操作,但是很多时候,内置函数并不能满足我们所有的需要,因此我们需要创建数组进行我们所需要的操作。

数组创建

在前面的章节,我们学习过数组可由以下两种方式进行创建:

a = array.from(ele1,ele2...)
b = array.new(n,ele)

array.from不限制元素的类型,布尔值,字符型,数字都可以添加;array.from第一个值填写重复的个数,第二个值添加重复的元素(也可以不写,这样创建一个数量为n,元素为null的数组)。Pine语言中还有很多和类型相关的与array.new类似的函数:array.new_int()、array.new_bool()、array.new_color()、array.new_string()等。

数组可以在脚本的全局范围内声明,也可以在函数或if分支的本地范围内声明。var关键字也可以作用与数组的声明模式,使用var关键字声明的数组在每个策略周期执行完成后,会保留在这个策略周期内array的变动,而没有添加关键词var的array在每次策略周期内,都会重新更新一次。

var a = array.from(0)
b = array.from(0)

if bar_index == 1
    array.push(a, bar_index)
    array.push(b, bar_index)
else if bar_index == 2 
    array.push(a, bar_index)
    array.push(b, bar_index)
else ibar_index == 3
    runtime.log("a:", a)
    runtime.log("b:", b)
    runtime.error("stop")

可以看到a数组的变动都持续确定了下来,没有被重置过。b数组则在每个BAR上都被初始化。最终打印的时候仍然只有一个元素,数值0。

数组的增加和删除

数组的增加操作相关函数:

array.unshift():数组起始位置增加
array.insert():数组设定位置增加
array.push():数组末尾增加
a = array.from(1, 2, 3)
array.unshift(a, "X")
runtime.log("数组a:", a)

a2 = array.from(1, 2, 3)
array.insert(a2, 1, "Y")
runtime.log("数组a2:", a2)

a3 = array.from(1, 2, 3)
array.push(a3, "D")
runtime.log("数组a3:", a3)

runtime.error("stop")

数组的删除操作相关函数:

array.shift():删除数组起始位置
array.remove():删除数组设定位置元素
array.pop():删除数组末尾位置元素
array.clear():删除数组全部元素
a = array.from(1, 2, 3)
array.shift(a)
runtime.log("数组a:", a)

a2 = array.from(1, 2, 3)
array.remove(a2, 1)
runtime.log("数组a2:", a2)

a3 = array.from(1, 2, 3)
array.pop(a3)
runtime.log("数组a3:", a3)

a4 = array.from(1, 2, 3)
array.clear(a4)
runtime.log("数组a4:", a4)

runtime.error("stop")

数组的查询和改动

使用array.get获取数组中指定索引位置的元素,使用array.set修改数组中指定索引位置的元素。

array.get的第一个参数为要处理的数组,第二个参数为指定的索引。
array.set的第一个参数为要处理的数组,第二个参数为指定的索引,第三个参数为要写入的元素。
a = array.from(1,2,3,4,5)
runtime.log('元素1', array.get(a,0))

array.set(a,0,100)
runtime.log('元素1', array.get(a,0))
runtime.error('stop')

通过举例证明,array.get帮助我们获取所需位置的元素,array.set修改所需位置的元素。

队列应用

使用数组,以及数组的一些增加、删除函数我们可以构造出「队列」数据结构。队列是一种在编程领域经常使用的结构,队列的特点就是:先进先出。这样就可以确保队列中存在的数据都是最新的数据。

移动平均值sma大家都很熟悉,sma以固定周期为窗口,计算窗口内的平均值。使用array数组,我们可以构建sma计算的原始代码:

var a = array.new_float(0)
length = 10

if not barstate.ishistory
    array.push(a, close)
    if array.size(a) > length
        array.shift(a)

sum = 0.0
for [index, ele] in a 
    sum += ele

avgPrice = array.size(a) == length ? sum / length : na

plot(avgPrice, title="avgPrice")
plot(ta.sma(close, length), title="ta.sma")

通过将10设为固定周期,在策略开始,首先使用push往array里添加元素,直到数组的长度大于10,使用shift删除数组第一个元素,确保窗口内的数字都是最新10个周期内的收盘价。接着利用一个for循环,计算最近10个周期内的收盘价总和。最后计算平均值,这里我们使用了一个三元操作符,确保收集够10个周期的收盘价时,才返回平均值。请注意,我们array创建需要声明是var,确保每个周期内数组的变动会记录下来。

对比于内置函数ta.sma,可以发现两个值是一致的。只是我们构建的函数是从10个周期后才开始呈现。

历史数据引用

数组同样可以使用历史引用操作符进行历史的引用。

a = array.new_float(1,close)
runtime.log(a[1])
runtime.log(close[1])

代码发现,数组a和close使用历史引用后,两个值是一致的。

数组计算

以数组为集合,可以进行统计特征平均值,最小值,最大值,标准差等的获取。

array.avg()求数组中所有元素的平均值array.min()求数组中最小的元素array.max()求数组中最大的元素array.stdev()求数组中所有元素的标准差
array.sum()求数组中所有元素的和
array.median()求数组中元素的中位数
array.mode()求数组中元素的众数
a = array.from(3, 2, 1, 4, 5, 6, 7, 8, 9)

runtime.log("数组a的算数平均:", array.avg(a))
runtime.log("数组a中的最小元素:", array.min(a))
runtime.log("数组a中的最大元素:", array.max(a))
runtime.log("数组a中的标准差:", array.stdev(a))
runtime.log("数组a的所有元素总和:", array.sum(a))
runtime.error("stop")

数组操作

array.concat(array1,array2): 合并或连接两个数组。
array.copy(): 复制数组。
array.join(array,"连接符号"): 将数组中的所有元素连接成一个字符串。
array.sort(array, order.ascending/order.descending): 按升序或降序排序。
array.reverse(): 反转数组(会修改原始数组)。
array.slice(数组,n1,n2): 对数组进行切片,以n1为开始,以n2为结束,遵循左闭右开的原则进行数据切片。
array.includes(数组,元素): 判断元素。
array.indexof(数组,元素): 返回参数传入的值首次出现的索引。如果找不到该值,则返回 -1。
array.lastindexof(数组,元素): 找到最后一次出现的值。
a = array.from(1, 2, 3, 4, 5, 6)
b = array.from(11, 2, 13, 4, 15, 6)

runtime.log("数组a:", a, ", 数组b:", b)
runtime.log("数组a,数组b连接在一起:", array.concat(a, b))

c = array.copy(b)

runtime.log("复制一个数组b,赋值给变量c,变量c:", c)
runtime.log("使用array.join处理数组c,给每个元素中间增加符号+,连接所有元素结果为字符串:", array.join(c, "+"))
runtime.log("排序数组b,按从小到大顺序,使用参数order.ascending:", array.sort(b, order.ascending))     // array.sort函数修改原数组
runtime.log("排序数组b,按从大到小顺序,使用参数order.descending:", array.sort(b, order.descending))   // array.sort函数修改原数组
runtime.log("数组a:", a, ", 数组b:", b)

array.reverse(a)   // 此函数修改原数组
runtime.log("反转数组a中的所有元素顺序,反转之后数组a为:", a)    

runtime.log("截取数组a,索引0 ~ 索引3,遵循左闭右开区间规则:", array.slice(a, 0, 3))
runtime.log("在数组b中搜索元素11:", array.includes(b, 11))
runtime.log("在数组a中搜索元素100:", array.includes(a, 100))
runtime.log("将数组a和数组b连接,搜索其中第一次出现元素2的索引位置:", array.indexof(array.concat(a, b), 2), " , 参考观察 array.concat(a, b):", array.concat(a, b))
runtime.log("将数组a和数组b连接,搜索其中最后一次出现元素6的索引位置:", array.lastindexof(array.concat(a, b), 6), " , 参考观察 array.concat(a, b):", array.concat(a, b))
runtime.error("stop")

array函数中还有一些其他的内置函数的用法,大家可以在FMZ的Pine语言帮助文档中查询获得。


更多内容