第六章:函数
第一节:新建函数
1.1 函数
所谓函数,就是把一些程序存储成特定的脚本,在使用时,无须重复编写,直接调用,使代码的复用率提高的一种办法,从本质上来说也是一种“脚本”,因此函数需要分建立和调用两个部分。另外,在公式编辑器的导航器可以看到有MC预先编写好的函数、指标和信号,这些预先编写好的脚本我们叫做内置(Bult-in)。一个完整的函数是需要有:输入→函数体→返回值,其中输入我们也叫做参数。
1.2 新建函数
再说新建函数的时候,我们首先说一下,在新建脚本的时候会有三种公式类型可以选择,其中有函数、指标和信号。MC与其他软件最大的不同是区分指标和信号的分别建立。也就是说我们看到的一些技术指标,而且想建立这些技术指标的话,我们选择新建指标。如果想利用编写好的指标添加交易逻辑的话,可以选择信号。当然,在MC当中,指标编写中是不允许出现信号买卖的逻辑,但是允许有画图等显示代码,另外,信号编写时,是不允许出现画图这样的指标显示代码的。MC区分开指标和信号的原因是提高代码运行的效率,如果在信号当中添加画图代码势必会造成代码运行的开销,降低下单买卖指令的运算。因此这种做法是好的。
在新建函数选择框中,可以看到返回类型和函数存储形式。
所谓返回值可以选择数值型、TrueFalse(布尔型)、字符串型,也就是我们通过函数的运算,最终想要得到一个什么结果。默认状态下,数值型的返回值是0,布尔型的返回值是False,字符串的返回值是空。函数的存储是因为数列类型(序列型)函数会将函数返回值绑定在当根bar上,后续进行回溯数值,而数值类型函数值只有一个值,不能进行回溯值,对于自动类型函数,默认情况下自动类型函数是数值类型,但是自动类型函数会在某种情况下是数列类型函数,这个是在函数编译时就确定了。这里牵扯到一个在函数体内部自动变换的问题,后面会说到,在此我们默认为“自动”,后面方便观察这个自动变换过程。
第二节:函数的参数
1.1 声明:
声明就是在写函数、指标和信号的时候,需要实现对其需要的数组、参数、变量进行的“预定义”,之前见到过的Inputs:、var:、array:分别表示声明参数、变量和数组。这是并不是必须的,但是如果需要更多复杂的逻辑,也是必须使用的。
另外,在MC当中,对于变量有一些默认的全局变量,分别是:
数值型:初始值0:有99个,value1~value99
逻辑性:初始值False,有99个,condition1~condition99
参数与变量的区别是,变量可以在公式内部改变,而一般的参数只能在程序外部修改(当指标或者信号插入到图表时,可以修改参数是定的初始值);参数可以在外部进行优化,变量不可以;只要声明引用型参数时,才允许程序内部改变参数,且会带有Ref这样的关键字。
函数的参数也叫函数的输入,在函数当中,声明部分与其他指标和信号一样,在Inputs:定义好想要传入函数参数的类型。
1.2 IntraBarPersist函数
这个函数是比较特别的,他是添加在定义变量之前的。
# 语法:
Declaration:[IntraBarPersist]Name(InitialValue1)
# 说明:
用在变量和数组的声明语句中,在变量或数组名称之前,用来指定变量或数组元素的值依据每根tick更新。如果没有指定的话,变量或数组值将会在每根Bar的close更新。
# 示例:
声明一个数值型变量 Max,按照每 tick 更新,初始值为 100: Variable:IntraBarPersist Max(100); 声明一个含 24 个元素的一维数值型数组 Max_Price,元素值按照每 tick 更新,元素初始值为 0: Array:IntraBarPersist Max_Price[23](0);
1.3 定义函数参数、变量类型
与变写指标和信号最大的不同。在指标和信号中,声明可以设定默认值,或者不写。例如:var:var0(0);,括号内的值我们是可以填写具体的值(数值、布尔、字符串)。但是函数做为一个需要接收这些值的代码。对于括号内的具体值一般是不知道的。因此我们需要在这个括号内定义需要接收某个值的类型。根据数值的类型:数值型、字符串型、布尔型(基本数据类型);序列型、数组型、引用型(特殊数据类型),可以如下表:
数值:
Numeric
NumericSimple
NumericSeries
NumericRef
NumericArray
NumericArrayRef
布尔型:
TrueFalse
TrueFalseSimple
TrueFalseSeries
TrueFalseRef
TrueFalseArray
TrueFalseArrayRef
字符串型:
String
StringSimple
StringSeries
StringRef
StringArray
StringArrayRef
为了防止重复解释,我们只观察后缀:
1、不带后缀的:表示为数值型。它可以是简单数值(Simple)或者序列数值(Series)。简单数值不能进行回溯(也就是在当前位置不能去前面某个位置的K线数值),序列数值是从Bar到Bar的变化,可以回溯历史。
2、Simple:设定好为简单数值
3、Series:设定好为序列数值
4、Array:声明一个数组类型;在这里提示一下语法应用:
Input:InputName[M1,M2,M3,etc.](NumericArray)
参数:
InputName——表达式,指定 input 参数的名称。名称可以包
括英文字母、下划线、数字和英文句号。字母不区分大小写,名称不能用数字或下划线开头。
M——变量,表示传递到函数的数组每个维度的最大索引值,一个变量指定的是一维数组,两个变量指定的是二维数组(M1,M2),三个变量指定的是三维数组(M1,M2,M3)以此
类推。
一个 input 声明只能是一个指定维度的数组。
要说明的是一般声明的时候,用X变量来指定数组的最大索引值
# 示例:
声明 Length 作为函数的一维数值数组参数:
Input:Length[X](NumericArray);
数组的最大索引值由变量 X 来指定。
声明 Table 作为函数的一个三维数值数组参数:
Input:Table[X,Y,Z](NumericArray);
数组的最大索引值由变量 X,Y,Z 来指定。
5、Ref:引用型;所谓引用型是运行在函数脚本中对参数进行变更,并且在外部可以直接调用这个修改后的值。换句话说,也就是给函数传递这个变量,不光可以传递值,而且在外部可以接收这个值。一般在声明成布尔类型的函数经常采用这种方式,后面会举例说明。
第三节:序列参数与简单参数的区别
这个部分可能是函数这块儿的一个小难点。我们发现在前面有不带后缀的声明,诸如:Numeric,再就是带有Simple和Series后缀的声明。后面两个根据前面的定义来说一个是简单型,一个是序列型。
现在,我们先分别定义好这两个参数,观察一下,他们是否可以进行回溯操作:
3.1 区别1:定义成Simple只是简单变量,而定义成Series是可以回溯
# 示例1:观察Simple和Series,是否可以回溯
//func input: simple(numericsimple), series(numericseries); print("func ,","simple=",simple,",simple[1]=",simple[1],",series=",series,",series[1]=",series[1]); //indicator var: var0(0), var1(0); var0=currentbar; var1=currentbar; print("sign ,","currentbar=",currentbar); func(var0,var1); //print输出结果 sign ,currentbar= 3.00 func ,simple= 3.00,simple[1]= 3.00,series= 3.00,series[1]= 2.00 sign ,currentbar= 4.00 func ,simple= 4.00,simple[1]= 4.00,series= 4.00,series[1]= 3.00
# 说明:通过返回值我们可以观察到。在定义成简单型Simple的情况下,它仅仅是一个变量值,没有产生回溯的效果,回溯前一个值并不起作用。也就是说:定义成Simple只是简单变量,而定义成Series是可以回溯。
3.2 区别2:函数内部自动变化成序列类型。
# 示例2:
//func input: num1(numeric), num2(numeric); print("func ,","num1=",num1,",num2[0]=",num2[0]); //indicator var: var0(0), var1(0); var0=currentbar; var1=currentbar; print("sign ,","currentbar=",currentbar); func(var0,var1);
# 说明:在这个例子中,并不特别指明是Simple还是Series。通过返回值观察到,num2被强制变换成序列类型了。这是因为在没有特别声明具体是哪一种类型的情况下,在函数体内部如果使得变量具有序列性质(也即是带方括号[0],这个样子),MC系统会自动的帮我们转换成序列参数。
3.3 区别3:外部传入序列类型变量至函数内,使simple类别变为series类型
# 示例3:
//func input: simple(numericsimple), series(numericseries); print("func ,","simple=",simple,",simple[1]=",simple[1],",series=",series,",series[1]=",series[1]); //indicator var: var0(0), var1(0); var0=currentbar; var0[0]; var1=currentbar; print("sign ,","currentbar=",currentbar); func(var0,var1); //print输出 sign ,currentbar= 3.00 func ,simple= 3.00,simple[1]= 2.00,series= 3.00,series[1]= 2.00 sign ,currentbar= 4.00 func ,simple= 4.00,simple[1]= 3.00,series= 4.00,series[1]= 3.00
# 说明:同案例1,当时我们回溯simple值的时候,发现定义成numericsimple类型,并不能起到回溯作用,但是在这里也能回搠,原因是在函数外部调用这个值,传入一个带有回溯性质的变量时,simple类型会自动变换成序列类型。
【小结】:函数的调用时,传值与bar之间是绑定关系。不论是否定义为simple或者series类型,如果传值性质改变,函数接收时的参数定义也会变化。为方便起见,最好明确是simple类型或series,这样会更加清晰。
第四节:Array后缀参数类型
# 示例1:
//func input: num1[x,y](numericarray), num2[w](numericarray); print("func ,","num1[1,3]=",num1[1,3],",num1[1,3][1]=",num1[1,3][1],",num2[2]=",num2[2],",num2[w]=",num2[w]); //sign array: arr0[3,4](0), arr1[5](0); arr0[1,3]=currentbar; arr1[2]=currentbar; print("sign ,","currentbar=",currentbar); func(arr0,arr1); //print输出 sign ,currentbar= 3.00 func ,num1[1,3]= 3.00,num1[1,3][1]= 2.00,num2[2]= 3.00,num2[w]= 0.00 sign ,currentbar= 4.00 func ,num1[1,3]= 4.00,num1[1,3][1]= 3.00,num2[2]= 4.00,num2[w]= 0.00
# 说明:array类型参数也是非常简单的,略有一些不同是,最好在声明时预留出可以表示参数尺寸的位置。
第五节:引用型参数
5.1 通过传值改变传入参数的值
# 示例1:
//func input: num0(numericref); num0=num0+1; //indicator var: var0(0); var0=var0+1; print("3 sign ,","currentbar=",currentbar,",var0=",var0); func(var0); print("5 sign ,","currentbar=",currentbar,",var0=",var0); //print输出结果 3 sign ,currentbar= 1.00,var0= 1.00 5 sign ,currentbar= 1.00,var0= 2.00 3 sign ,currentbar= 2.00,var0= 3.00 5 sign ,currentbar= 2.00,var0= 4.00 3 sign ,currentbar= 3.00,var0= 5.00 5 sign ,currentbar= 3.00,var0= 6.00
# 说明:在这个例子中,打印了两遍var0的值,发现在调用函数后,在函数内部又改变了一次var0的值,我们知道,可以通过变量来计算某某值,在这里传入的参数也起到了和变量相同的作用,也改变了其传入的值。
第六节:单一返回值与多个返回值
如果记得之前学过想这样的函数,比如:value1 = Xaverage(close,5);通过Xaverage这个函数,传入close价格和周期5,然后用变量value1接收这个函数计算的值。这叫输入N个参数,返回一个计算好的值。
现在有这么一个问题,我们能不能通过传入N个参数,返回N个返回的值呢?这里就用到了第五节中讲到的引用参数的用法。
# 示例:
//函数:TrueFalse返回值 Input:var0(NumericSimple),var1(Numericref),var2(Numericref); if currentbar = 1 then begin var1 = 0; var2 = 0; end else begin var1 = var1 + var0; var2 = var1 - var0; end; //指标: var:var0(100),var1(0),var2(0); funbool(var0,var1,var2); print(var1," ",var2); //返回值: 0.00 0.00 100.00 0.00 200.00 100.00 300.00 200.00 400.00 300.00 500.00 400.00 600.00 500.00 700.00 600.00 800.00 700.00 900.00 800.00 1000.00 900.00 .... ..略
# 说明:在这里我们建立一个TrueFalse的函数,默认返回值是False,当然我们不需要接收这个返回值,我们在这里通过把传参var1和var2当做变量传递给我们的函数。我们直接调用这两个“参数”,此时这两个变量在函数体内部经过运算,当做变量值反向传递给函数。这就实现了多个返回值的效果。
=================================================
之前的文章感谢大家的转载,希望转载时请注明出处,本人转自其它网站的图表一并感谢,谢谢~!
https://www.cnblogs.com/noah0532/