程序化交易软件策略——期货股票mt5海龟出来的升级改良版

信号生成函数

根据这个系统,在两种所需的条件下会产生一个买入信号:

1. 距离前一个20天低点至少已经过了3个交易日

2a. 交易品种的价格低于20天低点 (海龟汤)

2b. 每日柱的收盘价不高于20天的低点 (海龟汤升级版)

添加图片注释,不超过 140 字(可选)

所有以上描述的其他交易策略规则都属于交易订单参数和仓位的管理,所以我们不在信号模块包含它们。

在同一个模块中,赫兹量化交易软件将根据这两种交易系统 (海龟汤和海龟汤升级版)进行信号的编程,将在EA交易的参数中加入选项,可以选择规则的相应版本。让我们把这对应的自定义变量称为 Strategy_Type。在我们的实例中,策略选项只有两个,所以就使用 true/false ( bool 类型的变量) 将更简单一些。但是我们需要可以把系列文章中的所有策略都加到其中,所以让我们使用数字列表:

 
 

enum ENUM_STRATEGY { // 策略列表 TS_TURTLE_SOUP, // 海龟汤 TS_TURTLE_SOUP_PLUS_1 // 海龟汤升级版 }; input ENUM_STRATEGY Strategy_Type = TS_TURTLE_SOUP; // 交易策略:

策略类型将会传到主程序的信号侦测函数中,也就是说它需要知道是否需要等到柱(日柱)关闭 — bool 类型的 b_Wait_For_Bar_Close 变量。第二个需要的变量是在前面的 i_Extremum_Bars 柱数处暂停,该函数返回信号状态: 是否买入/卖出条件已经符合还是继续等待. 在主EA交易文件中要加入对应的数字列表:

 
 

enum ENUM_ENTRY_SIGNAL { // 进场信号的列表 ENTRY_BUY, // 买入信号 ENTRY_SELL, // 卖出信号 ENTRY_NONE, // 没有信号 ENTRY_UNKNOWN // 未定义状态 };

另外一个信号模块和主程序函数都要用到的结构是 go_Tick 全局对象,它包含最新订单时刻的信息。这是一个标准的 MqlTick 类型的结构, 应该在主文件中声明。晚些时候我们将会编程使它在主程序体中更新 (OnTick 函数)。

 
 

MqlTick go_Tick; // 最后所知订单时刻的信息

现在,最终我们可以继续到模块的主函数了。

添加图片注释,不超过 140 字(可选)

 
 

ENUM_ENTRY_SIGNAL fe_Get_Entry_Signal( bool b_Wait_For_Bar_Close = false, int i_Extremum_Bars = 3 ) {}

赫兹量化交易软件从检查卖出信号条件开始; 距离前一最高价(第一个条件)是否过去了足够的天数(柱数), 以及价格是否突破了范围的上方边界 (第二个条件):

 
 

if(go_Channel.i_Highest_Offset > i_Extremum_Bars) // 第一个条件 if(go_Channel.d_High < d_Actual_Price) // 第二个条件 return(ENTRY_SELL); // 两个卖出条件都已达到

检查买入信号条件是类似的:

 
 

if(go_Channel.i_Lowest_Offset > i_Extremum_Bars) // 第一个条件 if(go_Channel.d_Low > d_Actual_Price) { // 第二个条件 return(ENTRY_BUY); // 两个买入条件都已满足

赫兹量化交易软件已经使用d_Actual_Price变量,它包含用于此交易策略的当前价格。对于海龟汤,它的意思是最后所知的卖出价格,对于海龟汤升级版,它就是前一天(柱)的收盘价格:

 
 

double d_Actual_Price = go_Tick.bid; // 默认价格 - 用于海龟汤版本 if(b_Wait_For_Bar_Close) { // 用于海龟汤升级版版本 double da_Price_Array[1]; // 辅助数组 CopyClose(_Symbol, PERIOD_CURRENT, 1, 1, da_Price_Array)); d_Actual_Price = da_Price_Array[0]; }

包含了最少所需处理的函数看起来是这样的:

 
 

ENUM_ENTRY_SIGNAL fe_Get_Entry_Signal(bool b_Wait_For_Bar_Close = false, int i_Extremum_Bars = 3) { double d_Actual_Price = go_Tick.bid; // 默认价格 - 用于海龟汤版本 if(b_Wait_For_Bar_Close) { // 用于海龟汤升级版版本 double da_Price_Array[1]; CopyClose(_Symbol, PERIOD_CURRENT, 1, 1, da_Price_Array)); d_Actual_Price = da_Price_Array[0]; } // 上方边界: if(go_Channel.i_Highest_Offset > i_Extremum_Bars) // 第一个条件 if(go_Channel.d_High < d_Actual_Price) { // 2nd condition // 价格突破了上方边界 return(ENTRY_SELL); } // 下方边界: if(go_Channel.i_Lowest_Offset > i_Extremum_Bars) // 第一个条件 if(go_Channel.d_Low > d_Actual_Price) { // 第二个条件 // 价格突破了下方边界 return(ENTRY_BUY); } return(ENTRY_NONE); }

请记住,通道对象可能在从中读取数据时还没有准备好(标志 go_Channel.b_Ready = false),所以,我们需要增加对这个标志的检查。在这个函数中,我们使用用于从时间序列中复制数据的标准函数之一(CopyClose), 所以让我们加上可能的错误处理。不要忘记记录详细数据,它们可能用于调试:

 
 

ENUM_ENTRY_SIGNAL fe_Get_Entry_Signal(bool b_Wait_For_Bar_Close = false, int i_Extremum_Bars = 3) { if(!go_Channel.b_Ready) { // 通道数据还没有准备好 if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: 通道参数没有准备好", __FUNCTION__); return(ENTRY_UNKNOWN); } double d_Actual_Price = go_Tick.bid; // 默认价格 - 用于海龟汤版本 if(b_Wait_For_Bar_Close) { // 用于海龟汤升级版版本 double da_Price_Array[1]; if(WRONG_VALUE == CopyClose(_Symbol, PERIOD_CURRENT, 1, 1, da_Price_Array)) { // 处理 CopyClose 函数的错误 if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyClose: 错误 #%u", __FUNCSIG__, _LastError); return(ENTRY_NONE); } d_Actual_Price = da_Price_Array[0]; } // 上方边界: if(go_Channel.i_Highest_Offset > i_Extremum_Bars) // 第一个条件 if(go_Channel.d_High < d_Actual_Price) { // 2nd condition // 价格突破了上方边界 if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: 价格 (%s) 已经突破了上方边界 (%s)", __FUNCTION__, DoubleToString(d_Actual_Price, _Digits), DoubleToString(go_Channel.d_High, _Digits)); return(ENTRY_SELL); } // 下方边界: if(go_Channel.i_Lowest_Offset > i_Extremum_Bars) // 第一个条件 if(go_Channel.d_Low > d_Actual_Price) { // 第二个条件 // 价格突破了下方边界 if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: 价格 (%s) 已经突破了下方边界 (%s)", __FUNCTION__, DoubleToString(d_Actual_Price, _Digits), DoubleToString(go_Channel.d_Low, _Digits)); return(ENTRY_BUY); } // 如果程序达到了这一行,价格就在范围之内,也就是第二个条件不满足 return(ENTRY_NONE); }

此函数将在每个订单时刻中调用,也就是每天成百上千次。但是,如果第一个条件(距离上一个极值点至少三天)不满足,后面的动作就是没有意义的。根据编程的规则,我们应该尽量减少资源的消耗,所以让我们的函数一直沉睡直到下一个柱(天),也就是说,直到通道参数更新:

 
 

ENUM_ENTRY_SIGNAL fe_Get_Entry_Signal(bool b_Wait_For_Bar_Close = false, int i_Extremum_Bars = 3) { static datetime st_Pause_End = 0; // 下一次检查的时间 if(st_Pause_End > go_Tick.time) return(ENTRY_NONE); st_Pause_End = 0; if(go_Channel.b_In_Process) { // 通道数据还没有准备好 if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: 通道参数没有准备好", __FUNCTION__); return(ENTRY_UNKNOWN); } if(go_Channel.i_Lowest_Offset < i_Extremum_Bars && go_Channel.i_Highest_Offset < i_Extremum_Bars) { // 第一个条件不满足 if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: 第一个条件不满足", __FUNCTION__); // 暂停,直到通道更新 st_Pause_End = go_Tick.time + PeriodSeconds() - go_Tick.time % PeriodSeconds(); return(ENTRY_NONE); } double d_Actual_Price = go_Tick.bid; // 默认价格 - 用于海龟汤版本 if(b_Wait_For_Bar_Close) { // 用于海龟汤升级版版本 double da_Price_Array[1]; if(WRONG_VALUE == CopyClose(_Symbol, PERIOD_CURRENT, 1, 1, da_Price_Array)) { // 处理 CopyClose 函数的错误 if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyClose: 错误 #%u", __FUNCSIG__, _LastError); return(ENTRY_NONE); } d_Actual_Price = da_Price_Array[0]; } // 上方边界: if(go_Channel.i_Highest_Offset > i_Extremum_Bars) // 第一个条件 if(go_Channel.d_High < d_Actual_Price) { // 2nd condition // 价格突破了上方边界 if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: 价格 (%s) 已经突破了上方边界 (%s)", __FUNCTION__, DoubleToString(d_Actual_Price, _Digits), DoubleToString(go_Channel.d_High, _Digits)); return(ENTRY_SELL); } // 下方边界: if(go_Channel.i_Lowest_Offset > i_Extremum_Bars) // 第一个条件 if(go_Channel.d_Low > d_Actual_Price) { // 第二个条件 // 价格突破了下方边界 if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: 价格 (%s) 已经突破了下方边界 (%s)", __FUNCTION__, DoubleToString(d_Actual_Price, _Digits), DoubleToString(go_Channel.d_Low, _Digits)); return(ENTRY_BUY); } // 如果程序达到了这一行,价格就在范围之内,也就是第二个条件不满足 if(b_Wait_For_Bar_Close) // 对于海龟汤升级版 // 暂停,直到当前柱关闭 st_Pause_End = go_Tick.time + PeriodSeconds() - go_Tick.time % PeriodSeconds(); return(ENTRY_NONE); }

这是这个函数的最终代码,让我们把信号模块文件称为Signal_Turtle_Soup.mqh, 把有关通道和信号的代码加到其中; 在文件的开头,我们将加入用于自定义策略的栏位:

 
 

enum ENUM_STRATEGY { // 策略版本 TS_TURTLE_SOUP, // 海龟汤 TS_TURTLE_SOUP_PLIS_1 // 海龟汤升级版 }; // 自定义设置 input ENUM_STRATEGY Turtle_Soup_Type = TS_TURTLE_SOUP; // 海龟汤: 策略版本 input uint Turtle_Soup_Period_Length = 20; // 海龟汤: 极值点搜索深度 (柱数) input uint Turtle_Soup_Extremum_Offset = 3; // 海龟汤: 距离上一个极值点的暂停(柱数) input double Turtle_Soup_Entry_Offset = 10; // 海龟汤: 进场: 与极值水平的偏移 (点数) input double Turtle_Soup_Exit_Offset = 1; // 海龟汤: 推出: 与相反极值点的偏移 (点数)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值