用mt4提供的mql4语言实现 比尔威廉姆斯 混沌证券交易方法的编码笔记(一)

时间:2024-02-18 15:14:19
在证券交易界,比尔威廉姆斯运用混沌科学的理论来交易的方法非常出名。国内的译本《证券交易新空间》详细得描述了他这一思想方法的具体操作细则,非常详细。但书编辑排版的质量实在是不敢恭维,插图的质量和摆放位置已经不能用粗制滥造来形容了,那简直是以折磨读者为乐,小小抱怨一下。

 

此篇博客是我使用mt4交易软件自带的职能交易系统mql4语言实现混沌交易方法的开发笔记,不保证正确,只为了给自己留一些日记,如果有朋友也关注这一个领域的,欢迎交流。这篇博客只是笔记,不是教程,不负责解释所有事情,只为给日后的自己一个交待,如是而已。 是故,涉及到的一些名词概念不会有解释,中文的翻译以《证券交易新空间》一书中的翻译为准。

 

首先,一个新的智能交易系统有三个默认的函系统函数,分别是 init() 函数,deinit() 函数,以及 start() 函数。三个函数中的第一init()函数会在智能交易系统程序被加载后执行一次,可以理解为初始化函数,而第二个deinit() 函数则是在程序退出时会被执行一次,类似于解构函数。最后一个函数 start() 函数则是在系统每收到一次新的报价后执行一次,因为系统总是收到新的报价(交易时段内),所以这个函数会被反复执行,相当于主循环函数。

 

比如目前黄金的报价是1650美元一盎司,我们打开智能交易程序,init()函数会被执行一次,假设是交易时段,start()函数会在下一个报价时被触发执行(一般新的报价每秒都有),而只有当我们结束这个智能交易程序时,deinit()函数才有可能被执行。所以start()函数要考虑到性能问题,一般这样的智能交易系统不能涉及到太大规模的计算,否则几乎每秒都要执行的start()函数一次还没有完成就要执行下一次,执行程序的计算机可能会出现问题。这是必须要考虑的。

 

值得庆幸的是mql4可以调用Win32 API 和其他外部程序获得沟通和协作,如果要涉及到非常大规模的计算的话,也不是没有可能形成一个分布式的架构。而我目标实现的系统的运算量很小,完全每有必要和外部程序协作,mql4可以胜任。

 

鳄鱼组线 

 

第一个要解决的问题是鳄鱼组线,如何在mql4环境里获得鳄鱼组线的数据。包括鳄鱼的牙齿、唇和下颚。这个是比尔威廉姆斯混沌交易方法里贯穿始终要用到的。

 

因为该方法在西方投资界的盛名,mt4本身的技术分析里就有鳄鱼组线,mql4也已经有预定义的函数,不需要自己写了,填写几个实参就可以直接获得结果了。

 

具体的方法是,先在代码的最上方引入外部变量,并赋值,我这里使用书里面的标准值。13根平均线平移8位作为下颚,8根平均线平移5位做牙齿,5根平均线平移3位做唇。 

 

extern int JawsPeriod   =13;
extern int JawsShift    =8;
extern int TeethPeriod  =8;
extern int TeethShift   =5;
extern int LipsPeriod   =5;
extern int LipsShift    =3;

 

然后在需要的地方就可以直接调用,获取当前这几根线的y轴值了(横轴x轴是时间,纵轴y轴是价格)

1 double jaw   = iAlligator(NULL, 0, JawsPeriod, JawsShift, TeethPeriod, TeethShift, LipsPeriod, LipsShift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORJAW, 1);
2 double teeth = iAlligator(NULL, 0, JawsPeriod, JawsShift, TeethPeriod, TeethShift, LipsPeriod, LipsShift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORTEETH, 1);
3 double lips  = iAlligator(NULL, 0, JawsPeriod, JawsShift, TeethPeriod, TeethShift, LipsPeriod, LipsShift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORLIPS, 1);

 

有了鳄鱼组线后,判断趋势产生的另外一个要素就是分形的形成和突破了。

 

分形几何信号 

 

 mql4并每有直接提供分形相关的函数,但分形的技术定义并不复杂。过去5根bar价格柱线里,中间的那根价格柱线的最高值是所有5根柱线里最高的,则是一个向上分形,如果中间的那根的最低价是所有5根里最低的,那就是一个向下分形。

 

mql4 提供价格柱线的获取方法,不需要函数,用内置的数组就可以了。

 

当前价格柱线的最高价格、最低价格、开盘价和收盘价可以用数组获取。

 

double high = High[0];

double low  = Low[0];

double open = Open[0];

double close= Close[0];  

 

 

在mql4里,一旦输入High或者Low这样的数组名,颜色会发生变化,这表示是系统保留的内置数组名。这里是high low open close就是当前的最高价,最低价,开盘和收盘价。

 

如果要获取上一个价格柱线的最高价或者最低价

 

 

double last_highest = High[1];
double last_lowest = Low[1];

 

 

向前的话,以此类推。 high[2]是前面第二根线的最高,low[2]是前面第二根线的最低。而非 high[-2] low[-2]

 

1 bool IsUpFractal     = true;
2 bool IsDownFractal   = true;
3    
4 for(int i=1;i<=5;i++)
5 {
6    if(High[3]<High[i])  IsUpFractal    = false;
7    if(Low[3]>Low[i])    IsDownFractal  = false;   
8 }

 

这里的算法比较简单

 

第1,2行定义两个变量,是否是向上分形或者向下分形的标志,先假设是形成了分形的。然后查看前5根价格柱线,如果有任何柱线最高价比中间第三根最高价高的话,也就是打擂法成功的话,没有分形突破形成,否则就确实是一个有效的向上分形,向下分形一样道理。

 

这个函数的执行时机是个问题,如果按照一小时线图来交易的话,则只需要在每个小时开始时执行一下就可以了,没有必要浪费资源在每一个新报价时执行。

 

这里要用到系统的时间函数,获取当前时间的小时数,比如现在是下午5点,则Hour()函数返回17,表示是17点。

 

假设我们现在把上面分形突破的判断方法写在一个叫CatchFractal()函数里,则我们要保证每个小时执行一次的话,我用的方法如下:

 

 1 int last_hour=-1;
 2 int start()
 3 {
 4    if(Hour()!=last_hour)
 5    {
 6       CatchFractal();
 7       last_hour=Hour();
 8    }
 9    return(0);
10 }

 

第一行先定义了一个变量,叫做last_hour函数,这个函数用来记录上一个小时数,初始为-1,这样就不可能和任何一个时间一样。

 

然后在start()函数里只需要判断当前小时数和上一个是否一致,如果一致,证明还没有过一个小时,则不触发任何计算,如果触发了,一定是这个小时的第一个时间点触发的,则我们判断有没有新的分形出现,更新一下last_hour 变量为现在的小时数。这样就实现了一个小时执行一次CatchFractal()函数,这个函数一定是每个小时刚刚开始时执行的。

 

初始情况,值为-1,然后执行start()函数,假设是7点30分开始执行的程序,则Hour()=7不等于-1,则执行一次,检查有没有分形的情况发生,last_hour函数变成7。然后7点31分,7点46分这样的时间点获取的Hour()=7,于last_hour变量一直,不会执行任何操作,直到8点00分00秒,Hour()=8,不再等于last_hour=7,则再次触发分形判断,last_hour更新为8,直到9点再开始检测,期间不会浪费任何系统资源。

 

在4小时图和日图周图都可以以此类推进行改进,4小时图如果不修改的话,也能正常运行,只不过每4次检查中有3次是浪费的。日图的话,Day()函数取代Hour()函数即可。周线图也是可以扩展的。如果做更加短时间的,30分钟、15分钟、5分钟、1分钟图的交易,也是这个思路。

 

至此,我们得到了一个每小时监测有无分形形成的程序,并没有做任何交易,也没有对分形信息做处理。已有的代码整理如下:

 

 

 1 extern int JawsPeriod   =13;
 2 extern int JawsShift    =8;
 3 extern int TeethPeriod  =8;
 4 extern int TeethShift   =5;
 5 extern int LipsPeriod   =5;
 6 extern int LipsShift    =3;
 7 
 8 int last_hour=-1;
 9 int start()
10 {
11    if(Hour()!=last_hour)
12    {
13       CatchFractal();
14       last_hour=Hour();
15    }
16    return(0);
17 }
18 
19 int CatchFractal()
20 {
21    double jaw = iAlligator(NULL, 0, JawsPeriod, JawsShift, TeethPeriod, TeethShift, LipsPeriod, LipsShift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORJAW, 1);
22    double teeth = iAlligator(NULL, 0, JawsPeriod, JawsShift, TeethPeriod, TeethShift, LipsPeriod, LipsShift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORTEETH, 1);
23    double lips = iAlligator(NULL, 0, JawsPeriod, JawsShift, TeethPeriod, TeethShift, LipsPeriod, LipsShift, MODE_SMMA, PRICE_MEDIAN, MODE_GATORLIPS, 1);
24    
25    bool IsUpFractal     = true;
26    bool IsDownFractal   = true;
27    
28    for(int i=1;i<=5;i++)
29    {
30       if(High[3]<High[i])  IsUpFractal    = false;
31       if(Low[3]>Low[i])    IsDownFractal  = false;   
32    }
33 }

 

1~6 行引入外部函数,是鳄鱼组线的参数。

8~17 行用来确保每个小时开始的时候触发一次CatchFractal()函数

19~33 是获取分形的代码

其中

21~23行是鳄鱼组线值得获取

25~32行则计算出是否有向上或者向下分形

 

下一篇,判断如何用鳄鱼组线和分形信号来判断如何形成一个买入和卖出信号。