外汇EA简易订单管理入门指南

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);

}