量化投资_EasyLanguage/PowerLanguage教学课程__【第一篇基础】__【第六章函数】

第六章:函数

第一节:新建函数

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/

原文地址:https://www.cnblogs.com/noah0532/p/13682369.html