1. 介绍
每个智能交易程序都包含一段用于控制建仓的代码。它会在所有订单中持续搜索,根据相关信息筛选仓位,并进行修改和关闭操作。这段代码的样式往往大同小异,功能也较为相似。正是因为这段代码常被重复使用,我们可以将其从程序中提取出来,封装成函数,从而使程序编写更高效、结构更清晰。首先,我们根据功能将任务划分为三个步骤——这三个步骤对应着三种智能交易程序类型:
[*]同一时间只能新建一个仓位的智能交易程序[*]同一时间可以为每种类型新建一个仓位的智能交易程序(例如,多头和空头仓位)[*]可以同时新建多个仓位的智能交易程序
2. 一个仓位
只新建一个仓位有许多种策略。虽然这种控制代码块相对简单,但编写起来也需要花费一些时间和精力。
举一个简单的例子,该例子通过MACD信号线交叉(信号线与基础线)来生成建仓信号。为了简化控制代码块,程序如下所示:复制代码
extern int _MagicNumber = 1122;
int start()
{
//—- 存储指标值用于数据分析
double MACD_1 = iMACD(Symbol(), 0, 12, 26, 9, PRICE_CLOSE,
MODE_MAIN, 1 );
double MACD_2 = iMACD(Symbol(), 0, 12, 26, 9, PRICE_CLOSE,
MODE_MAIN, 2 );
int _GetLastError = 0, _OrdersTotal = OrdersTotal();
//—- 在开仓位置搜索
for ( int z = _OrdersTotal – 1; z = 0; z — )
{
// 如果在搜索中生成错误,转至下一个仓位
if ( !OrderSelect( z, SELECT_BY_POS ) )
{
_GetLastError = GetLastError();
Print(/”OrderSelect( /”, z, /”, SELECT_BY_POS ) – 错误 #/”,
_GetLastError );
continue;
}
// 如果当前货币对没有开仓,
// 忽略过
if ( OrderSymbol() != Symbol() ) continue;
// 如果MagicNumber不等于_MagicNumber,
// 忽略这个仓位
if ( OrderMagicNumber() != _MagicNumber ) continue;
//—- 如果BUY舱位开仓,
if ( OrderType() == OP_BUY )
{
//—- 如果MACD 遇到下降的零线,
if(NormalizeDouble(MACD_1, Digits + 1) < 0.0 &&
NormalizeDouble( MACD_2, Digits + 1) = 0.0)
{
//—- 平仓
if(!OrderClose( OrderTicket(), OrderLots(),
Bid, 5, Green))
{
_GetLastError = GetLastError();
Alert(/”错误OrderClose 鈩?/”, _GetLastError);
return(-1);
}
}
// 如果信号线没有改变,退出:
// 开新仓位过早
else
{ return(0); }
}
//—- 如果 SELL 仓位开仓,
if ( OrderType() == OP_SELL )
{
//—- 如果 MACD 遇到上升的零线
if(NormalizeDouble(MACD_1, Digits + 1) 0.0 &&
NormalizeDouble(MACD_2, Digits + 1 ) <= 0.0)
{
//—- 平仓
if(!OrderClose( OrderTicket(), OrderLots(),
Ask, 5, Red))
{
_GetLastError = GetLastError();
Alert( /”错误 OrderClose 鈩?/”, _GetLastError );
return(-1);
}
}
// 如果信号没有给便,退出:
// 开新仓位过早
else return(0);
}
}
//+——————————————————————+
//|如果达到此点,说明没有开仓仓位 |
//| 检测可能开仓 |
//+——————————————————————+
//—- 如果 MACD 遇到上升的零线,
if ( NormalizeDouble( MACD_1, Digits + 1 ) 0.0 &&
NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0 )
{
//—- 打开 BUY 仓位
if(OrderSend( Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0,
/”MACD_test/”, _MagicNumber, 0, Green ) < 0)
{
_GetLastError = GetLastError();
Alert( /”错误 OrderSend 鈩?/”, _GetLastError );
return(-1);
}
return(0);
}
//—- 如果MACD 遇到下降的零线,
if(NormalizeDouble( MACD_1, Digits + 1 ) < 0.0 &&
NormalizeDouble( MACD_2, Digits + 1 ) = 0.0)
{
//—- open a SELL position
if(OrderSend( Symbol(), OP_SELL, 0.1, Bid, 5, 0.0, 0.0,
/”MACD_test/”,
_MagicNumber, 0, Red ) < 0 )
{
_GetLastError = GetLastError();
Alert( /”错误 OrderSend 鈩?/”, _GetLastError );
return(-1);
}
return(0);
}
return(0);
}
现在我们将代码块封装成函数。这个函数能够在所有订单中搜索出所需目标,并将其信息记录在全局变量中,程序如下:复制代码
int _Ticket = 0, _Type = 0; double _Lots = 0.0,
_OpenPrice = 0.0, _StopLoss = 0.0;
double _TakeProfit = 0.0; datetime _OpenTime = -1;
double _Profit = 0.0, _Swap = 0.0;
double _Commission = 0.0; string _Comment = /”/”;
datetime _Expiration = -1;
void OneOrderInit( int magic )
{
int _GetLastError, _OrdersTotal = OrdersTotal();
_Ticket = 0; _Type = 0; _Lots = 0.0; _OpenPrice = 0.0;
_StopLoss = 0.0;
_TakeProfit = 0.0; _OpenTime = -1; _Profit = 0.0;
_Swap = 0.0;
_Commission = 0.0; _Comment = /”/”; _Expiration = -1;
for ( int z = _OrdersTotal – 1; z = 0; z — )
{
if ( !OrderSelect( z, SELECT_BY_POS ) )
{
_GetLastError = GetLastError();
Print(/”OrderSelect( /”, z, /”, SELECT_BY_POS ) -错误#/”,
_GetLastError );
continue;
}
if(OrderMagicNumber() == magic && OrderSymbol() ==
Symbol())
{
_Ticket = OrderTicket();
_Type = OrderType();
_Lots = NormalizeDouble( OrderLots(), 1 );
_OpenPrice = NormalizeDouble( OrderOpenPrice(), Digits);
_StopLoss = NormalizeDouble( OrderStopLoss(), Digits);
_TakeProfit = NormalizeDouble( OrderTakeProfit(), Digits);
_OpenTime = OrderOpenTime();
_Profit = NormalizeDouble( OrderProfit(), 2 );
_Swap = NormalizeDouble( OrderSwap(), 2 );
_Commission = NormalizeDouble( OrderCommission(), 2 );
_Comment = OrderComment();
_Expiration = OrderExpiration();
return;
}
}
}
如你所见,这个过程非常简单: 一共使用11个变量,每个都用于储存仓位的相关信息(票据号、类型、手数等). 当函数开始运行时,这些变量会被清零。作为全局变量,这一步是必要的。函数被调用时,变量也可以不清零,但我们需要的是最新近的信息而非旧数据。随后,所有仓位将以标准方式被搜索,一旦获得所需的信号和MagicNumber值,信息将被存储到相应的变量中。
现在我们将这个函数应用到智能交易程序中:复制代码
extern int _MagicNumber = 1122;
#include <OneOrderControl.mq4
int start()
{
int _GetLastError = 0;
// 记录开仓的参数(如果可用)
OneOrderInit( _MagicNumber );
//—- 记录指标值用作分析
double MACD_1 = iMACD(Symbol(), 0, 12, 26, 9, PRICE_CLOSE,
MODE_MAIN, 1 );
double MACD_2 = iMACD(Symbol(), 0, 12, 26, 9, PRICE_CLOSE,
MODE_MAIN, 2 );
// 现在,取代在仓位中的搜索
// 存在开仓:
if ( _Ticket 0 )
{
//—-如果BUY 仓位开仓,
if ( _Type == OP_BUY )
{
//—- 如果MACD 遇到下降的零线,
if(NormalizeDouble( MACD_1, Digits + 1 ) < 0.0 &&
NormalizeDouble( MACD_2, Digits + 1 ) = 0.0)
{
//—- 平仓
if(!OrderClose( _Ticket, _Lots, Bid, 5, Green))
{
_GetLastError = GetLastError();
Alert( /”错误 OrderClose 鈩?/”, _GetLastError);
return(-1);
}
}
// 如果信号没有改变,退出:
// 开新仓位过早 else return(0);
}
//—-如果 SELL 仓位开仓,
if ( _Type == OP_SELL )
{
//—- 如果MACD 遇到上升的零线
if(NormalizeDouble( MACD_1, Digits + 1 ) 0.0 &&
NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0)
{
//—- 平仓
if(!OrderClose( _Ticket, _Lots, Ask, 5, Red))
{
_GetLastError = GetLastError();
Alert( /”错误 OrderClose 鈩?/”, _GetLastError);
return(-1);
}
}
// 如果信号没有改变,退出:
// 开新仓位过早
else return(0);
}
}
// 如果智能交易没有开仓
// ( _Ticket == 0 )
// 如果MACD 遇到上升的零线
if(NormalizeDouble( MACD_1, Digits + 1 ) 0.0 &&
NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0)
{
//—- 开BUY 仓位
if(OrderSend(Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0,
/”CrossMACD/”, _MagicNumber, 0, Green ) < 0)
{
_GetLastError = GetLastError();
Alert( /”错误 OrderSend 鈩?/”, _GetLastError );
return(-1);
}
return(0);
}
//—- 如果MACD 遇到下降的零线
if ( NormalizeDouble( MACD_1, Digits + 1 ) < 0.0 &&
NormalizeDouble( MACD_2, Digits + 1 ) = 0.0 )
{
//—- 开SELL仓位
if(OrderSend(Symbol(), OP_SELL, 0.1, Bid, 5, 0.0, 0.0,
/”CrossMACD/”,
_MagicNumber, 0, Red ) < 0 )
{
_GetLastError = GetLastError();
Alert( /”错误 OrderSend 鈩?/”, _GetLastError );
return(-1);
}
return(0);
}
return(0);
}
如你所见,这段智能交易的程序显得更加紧凑且易于理解。这是一个简单的示例。如果你想了解更多关于提升交易效率的思路,也可以访问Decode官网获取更多资源。
现在让我们解决下一个任务。
3. 每个类型的一个仓位
为了实现其他功能,我们需要一个更复杂的智能交易程序。此程序能够新建多种类型的仓位并进行管理。以下是这种程序遵循的规则:
[*]该程序在运行时将设置两个挂单:在卖出价+20点处设置买入止损,在买入价+20点处设置卖出止损;[*]当其中一个挂单被触发后,另一个必须被取消;[*]建仓后必须附带追踪止损;[*]当仓位因止损或盈利被关闭后,程序将重新启动,即再次设置两个挂单。
程序代码如下:复制代码
extern int _MagicNumber = 1123;
extern double Lot = 0.1;
extern int StopLoss = 60;
// 止损点的间距 (0 – 无)
extern int TakeProfit = 100;
// 赢利点的间距 (0 – 无)
extern int TrailingStop = 50;
// 追踪止损点 (0 – 无)
extern int Luft = 20;
// 挂单交易放置水平的间距
int start()
{
// 记录订单中每个票据
int BuyStopOrder = 0, SellStopOrder = 0, BuyOrder = 0,
SellOrder = 0;
int _GetLastError = 0, _OrdersTotal = OrdersTotal();
// 在所有开仓仓位中搜索并记录
// 已存在的仓位类型:
for ( int z = _OrdersTotal – 1; z = 0; z — )
{
// 如果在搜索中生成错误,
// 转至下一个仓位
if ( !OrderSelect( z, SELECT_BY_POS ) )
{
_GetLastError = GetLastError();
Print(/”OrderSelect(/”, z, /”, SELECT_BY_POS) – Error #/”,
_GetLastError );
continue;
}
// 如果当前货币对没有开仓仓位,忽略它
if ( OrderSymbol() != Symbol() ) continue;
// 如果MagicNumber 不等于 _MagicNumber,
// 忽略这个仓位
if ( OrderMagicNumber() != _MagicNumber ) continue;
// 取决于仓位类型,
// 改变变量值:
switch ( OrderType() )
{
case OP_BUY: BuyOrder = OrderTicket(); break;
case OP_SELL: SellOrder = OrderTicket(); break;
case OP_BUYSTOP: BuyStopOrder = OrderTicket(); break;
case OP_SELLSTOP: SellStopOrder = OrderTicket(); break;
}
}
//—- 如果我们有两个挂单交易,退出
//—- 等待他们开启
if ( BuyStopOrder 0 && SellStopOrder 0 ) return(0);
// 在全部订单中第二次搜索
// 现在执行操作:
_OrdersTotal = OrdersTotal();
for ( z = _OrdersTotal – 1; z = 0; z — )
{
// 如果在仓位搜索中生成错误,
// 转至下一个仓位
if ( !OrderSelect( z, SELECT_BY_POS ) )
{
_GetLastError = GetLastError();
Print(/”OrderSelect(/”, z, /”, SELECT_BY_POS) – 错误 #/”,
_GetLastError );
continue;
}
// 如果对于当前的货币对没有开仓
// 忽略它
if ( OrderSymbol() != Symbol() ) continue;
// 如果 MagicNumber 不等于 _MagicNumber,
// 忽略这个仓位
if ( OrderMagicNumber() != _MagicNumber ) continue;
// 取决于仓位的类型,
// 改变变量值:
switch ( OrderType() )
{
//—-如果BUY仓位开仓,
case OP_BUY:
{
// 如果 SellStop挂单还没有删除,
// 删除:
if ( SellStopOrder 0 )
{
if ( !OrderDelete( SellStopOrder ) )
{
Alert(OrderDelete Error #/”, GetLastError());
return(-1);
}
}
// 检测止损是否被移动:
// 如果追踪止损的大小不小于当前水平,
if(TrailingStop MarketInfo( Symbol(),
MODE_STOPLEVEL ) )
{
// 如果利润点超过追踪止损点,
if(NormalizeDouble( Bid – OrderOpenPrice(),
Digits )
NormalizeDouble(TrailingStop*Point,
Digits ) )
{
// 如果新的止损水平超过当前仓位的水平
// (或者如果仓位没有止损),
if(NormalizeDouble(Bid –
TrailingStop*Point, Digits )
OrderStopLoss() || OrderStopLoss() <=
0.0 )
{
//—- 修改订单
if(!OrderModify( OrderTicket(),
OrderOpenPrice(),
NormalizeDouble(Bid –
TrailingStop*Point, Digits ),
OrderTakeProfit(),
OrderExpiration()))
{
Alert(/”OrderModify 错误 #/”,
GetLastError());
return(-1);
}
}
}
}
// 如果没有开仓仓位,退出
// 无事可做
return(0);
}
// 下一个单元格与BUY 仓位的单元格一样
// 这就是我们不能在单元格上标注的原因…
case OP_SELL:
{
if ( BuyStopOrder 0 )
{
if ( !OrderDelete( BuyStopOrder ) )
{
Alert(/”OrderDelete 错误 #/”,
GetLastError());
return(-1);
}
}
if(TrailingStop MarketInfo( Symbol(),
MODE_STOPLEVEL ) )
{
if(NormalizeDouble(OrderOpenPrice() – Ask,
Digits) NormalizeDouble(TrailingStop*Point,
Digits ) )
{
if(NormalizeDouble(Ask + TrailingStop*Point,
Digits ) < OrderStopLoss() ||
OrderStopLoss() <= 0.0 )
{
if(!OrderModify( OrderTicket(),
OrderOpenPrice(),
NormalizeDouble(Ask + TrailingStop*Point,
Digits), OrderTakeProfit(),
OrderExpiration() ) )
{
Alert(/”OrderModify Error #/”,
GetLastError());
return(-1);
}
}
}
}
return(0);
}
}
}
//+——————————————————————+
//| 如果执行达到此点, |
//| 说明没有挂单和开仓。 |
//+——————————————————————+
//—- 放置BuyStop 和 SellStop:
double _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel;
_OpenPriceLevel = NormalizeDouble( Ask + Luft*Point, Digits );
if ( StopLoss 0 )
{ _StopLossLevel = NormalizeDouble( _OpenPriceLevel –
StopLoss*Point, Digits ); }
else
{ _StopLossLevel = 0.0; }
if ( TakeProfit 0 )
{ _TakeProfitLevel = NormalizeDouble( _OpenPriceLevel +
TakeProfit*Point, Digits ); }
else
{ _TakeProfitLevel = 0.0; }
if ( OrderSend ( Symbol(), OP_BUYSTOP, Lot, _OpenPriceLevel,
5, _StopLossLevel, _TakeProfitLevel, /”/”,
_MagicNumber ) < 0 )
{
Alert( /”OrderSend Error #/”, GetLastError() );
return(-1);
}
_OpenPriceLevel = NormalizeDouble(Bid – Luft*Point, Digits);
if ( StopLoss 0 )
{ _StopLossLevel = NormalizeDouble( _OpenPriceLevel +
StopLoss*Point, Digits ); }
else
{ _StopLossLevel = 0.0; }
if ( TakeProfit 0 )
{ _TakeProfitLevel = NormalizeDouble( _OpenPriceLevel –
TakeProfit*Point, Digits ); }
else
{ _TakeProfitLevel = 0.0; }
if ( OrderSend ( Symbol(), OP_SELLSTOP, Lot, _OpenPriceLevel,
5, _StopLossLevel,
_TakeProfitLevel, /”/”, _MagicNumber ) < 0 )
{
Alert( /”OrderSend Error #/”, GetLastError() );
return(-1);
}
return(0);
}
