外汇EA:简单的订单管理(一)
2012-05-28 11:52:24 来源: 作者:
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 个变量,每个都储存仓位的相关信息(ticket #, type, lot size, 等等). 当函数开始运行时,这些变量被归零。作为全局变量这是必需的。函数被调用时变量也可以不归零,但我们需要的不是先前的信息,我们需要的是最近的。然后所有的仓位会以标准的方式被搜索,一旦获得需要的信号和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);
}
如你所见,这段智能交易的程序显得更紧凑更易读。这是一个简单例子。
现在让我们解决下一个任务。
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);
}