编写"EA 交易"时,MQL5 标准交易类库的使用

Samuel Olowoyo | 27 十二月, 2013

简介

新款 MQL5 程序中带有大量内置标准类库,旨在让交易者和开发人员都尽可能简单地完成 MQL5 “EA 交易”、指标和脚本的开发。

这些类库都位于 MetaTrader 5 客户端文件夹中 MQL5 文件夹里的 \Include\ 文件夹。类库被划分为多个子分类 – ArraysChartObjectsChartsFilesIndicatorsStringsTrade 类。

我们会在本文中详细讲解,如何利用内置交易类编写“EA 交易”。“EA 交易”会以某种策略为基础:如果某规定条件被满足,则会涉及到已建仓位的平仓和修改。

如果您对类的概念和用法已有了解,那么,欢迎您进入 MQL5 新语言为您提供的另一个充满机遇的世界。

另一方面,如果您完全没接触过 MQL5,我建议您先阅读《针对初学者以 MQL5 编写“EA 交易”的分步指南》《利用 MQL5 面向对象编程法编写 EA 交易》这两篇文章,或是任何其它能够为您提供 MQL5 这一新语言相关指导的文章预热。人们已经撰写了大量的相关文章,能够提供您所需要的知识。


1. 交易类

交易类文件中由多个不同的类构成,而这些类的主旨,是要让那些为个人用途开发 EA 的交易者、以及那些身为程序员却又不想在其“EA 交易” (EA) 开发过程中从零开始的程序员,提供更多的便捷和舒适。

在使用一个的过程中,您无需知晓该类的内部机制(即其实现开发人员要求的方式),您需要关注的,只有如何利用这个类来解决您的问题。所以,对于任何想要使用它们的人而言,利用内置类库都会便捷许多。本文中,我们着重讨论那些在开发“EA 交易”过程中需要用到的主要的类。

在讨论类的过程中,我们不会纠结于各个类的内部详情,而是详细分析这个类有什么用,我们怎样才能用它来完成开发一个极具盈利能力的 EA 的使命。让我们逐个讨论。

1.1 СAccountInfo 类

CAccountInfo是一个方便用户于客户端中访问所有账户属性或当前已建交易账户信息的类。

为了更好地理解,我们看一看本类中很可能用于 EA 的主要成员函数。在使用一个类之前,我们必须首先创建该类的一个对象;所以,要使用 CAccountInfo 类,我们必须为该类创建一个对象。

我们称其为 myaccount

//--- The AccountInfo Class Object
CAccountInfo myaccount;

记住:想创建一个类对象,您要使用类名称,后接想要赋予该对象的名称。

现在,我们可以利用 myaccount 对象访问 CAccountInfo 类的公共成员函数。

方法
描述
应用示例
myaccount.Login()

如果您想获取终端中当前建仓交易的账户号,则使用此函数。

// returns account number, example 7770
long accountno = myaccount.Login()
myaccount.TradeModeDescription()此函数用于获取终端当前活动账户的交易模式描述。
// returns Demo trading account, 
// or Real trading account or Contest trading account
string  acc_trading_mode = myaccount.TradeModeDescription();
myaccount.Leverage()

此函数用于获取终端当前活动账户的交易模式描述。

// returns leverage given to the active account
long acc_leverage = myaccount.Leverage(); 
myaccount.TradeAllowed() 

此函数用于检查终端活动账户的交易是否被允许。如交易不被允许,则该账户不能交易。

if (myaccount.TradeAllowed())
{
    // trade is allowed
}
else
{
  // trade is not allowed
}
myaccount.TradeExpert()此函数用于检查“EA 交易”在终端当前活动账户的交易是否被允许。
if (myaccount.TradeExpert())
{
   // Expert Advisor trade is allowed
}
else
{
   // Expert Advisor trade is not allowed
}
myaccount.Balance()此函数会给出终端活动账户的账户余额。
// returns account balance in the deposit currency
double acс_balance =  myaccount.Balance(); 
myaccount.Profit() 

此函数用于获取终端活动账户的当前利润。

// returns account profit in deposit currency
double acс_profit =  myaccount.Profit();
myaccount.FreeMargin() 

此函数用于获取 终端 活动账户的 可用预付款。

// returns free margin for active account
double acс_free_margin = myaccount.FreeMargin();
myaccount.Currency() 

此函数用于获取终端活动账户的存款货币。

string acс_currency = myaccount.Currency();
myaccount.OrderProfitCheck(const string symbol, ENUM_ORDER_TYPE  trade_operation, double volume, double price_open, double price_close)此函数会根据被传递的参数获取估算利润。输入参数分别为:交易品种、交易操作类型、交易量和开盘/收盘价。
double op_profit=myaccount.OrderProfitCheck(_Symbol,ORDER_TYPE_BUY,
1.0,1.2950,1.3235);
Print("The amount of Profit for deal buy EURUSD",
      "at 1.2950 and sell at 1.3235 is: ",op_profit);
myaccount.MarginCheck(const string symbol,ENUM_ORDER_TYPE trade_operation,double  volume,double price)此函数用于获取建立订单所需的预付款。此函数有四个输入参数,分别为:交易品种(货币对)、订单类型、要交易的手数(或交易量)以及订单价格。此函数在进行交易时非常重要。
// depending on the type of position to open - in our case buy
double price=SymbolInfoDouble(_Symbol,SYMBOL_ASK); 
double margin_req=myaccount.MarginCheck(_Symbol,ORDER_TYPE_BUY,LOT,price);
myaccount.FreeMarginCheck(const string symbol,ENUM_ORDER_TYPE trade_operation,double volume,double price)此函数用于获取订单下达时活动账户中 剩余可用预付款的额度。其四个输入参数分别为:交易品种(货币对)、订单类型、要交易的手数(或交易量)以及订单价格。
double acс_fm=myaccount.FreeMarginCheck(_Symbol,ORDER_TYPE_BUY,LOT,price);
myaccount.MaxLotCheck(const string symbol,ENUM_ORDER_TYPE trade_operation,double price) 

此函数用于获取为终端活动账户下达订单时可能的最大手数。其三个输入参数分别为:交易品种、订单类型及订单开盘价。

double max_lot=myaccount.MaxLotCheck(_Symbol,ORDER_TYPE_BUY,price);

1.2 СSymbolInfo 类

CSymbolInfo类会让用户快速访问当前交易品种所有属性变得非常简单。

要使用此类,我们必须创建此类的一个对象,本例中,我们称其为 mysymbol

// the CSymbolInfo Class object CSymbolInfo mysymbol;

 我们看一看该类在编写“EA 交易”过程中可能用到的大多数函数:

方法描述应用示例
mysymbol.Name(string  name)

此函数用于设置类对象的交易品种。它会将交易品种名称作为输入参数。

// set the symbol name for our CSymbolInfo class Object
mysymbol.Name(_Symbol);
mysymbol.Refresh()此函数用于刷新所有的交易品种数据。它也会在您为类设定一个新的交易品种名称时被自动调用。
mysymbol.Refresh();
mysmbol.RefreshRates()此函数用于检查最新的报价数据。成功则返回 true,失败则返回 false。这是一个不可或缺的有用函数。
//--- Get the last price quote using the CSymbolInfo 
// class object function
   if (!mysymbol.RefreshRates())
   {
      // error getting latest price quotes 
   }
mysymbol.IsSynchronized()

此函数用于检查终端上所设定交易品种的当前数据,是否与服务器上对应数据同步。如已同步,则返回 true,如果没有,则返回 false。

// check if symbol data are synchronized with server
  if (!mysymbol.IsSynchronized())
   {
     // error! Symbol data aren't synchronized with server
   }
mysymbol.VolumeHigh() 

此函数用于获取所设定交易品种的日最大交易量。

long max_vol = mysymbol.VolumeHigh();
mysymbol.VolumeLow() 

此函数用于获取设定交易品种的日最小交易量。

long min_vol = mysymbol.VolumeLow();
mysymbol.Time() 

此函数用于获取所设定交易品种的最后一次报价的时间。

datetime qtime = mysymbol.Time();
mysymbol.Spread()此函数用于获取所设定交易品种的当前点差值(点数)。
int spread = mysymbol.Spread();
mysymbol.StopsLevel()此函数用于获取当前收盘价的最低水平(点数),可在该位置为所设定交易品种下达止损指令。只要您考虑使用追踪止损或订单/持仓修改,则该函数会非常有用。
int stp_level = mysymbol.StopsLevel();
mysymbol.FreezeLevel()此函数用于获取所设定交易品种冻结交易操作的距离(点数)。
int frz_level = mysymbol.FreezeLevel();
mysymbol.Bid()

此函数用于获取所设定交易品种的当前卖价。

double bid =  mysymbol.Bid();
mysymbol.BidHigh()此函数用于获取当天的最大/最高卖价。
double max_bid = mysymbol.BidHigh();
mysymbol.BidLow()此函数用于获取所设定交易品种的日最小/最低卖价。
double min_bid = mysymbol.BidLow();
msymbol.Ask()此函数用于获取所设定交易品种的当前买价。
double ask = mysymbol.Ask();
mysymbol.AskHigh()此函数用于获取所设定交易品种的日最大/最高买价。
double max_ask = mysymbol.AskHigh();
mysymbol.AskLow()此函数用于获取当天的最小/最低买价。
double min_ask = mysymbol.AskLow();
mysymbol.CurrencyBase()此函数用于获取所设定交易品种的基础货币。
// returns "USD" for USDJPY or USDCAD
string base_currency = mysymbol.CurrencyBase();
mysymbol.ContractSize()此函数用于获取所设定交易品种进行交易的合约大小额度。
double cont_size =  mysymbol.ContractSize();
mysymbol.Digits() 此函数用于获取所设定交易品种的小数点后的位数。
int s_digits = mysymbol.Digits();
mysymbol.Point()此函数用于获取所设定交易品种的一个点的值。
double s_point =  mysymbol.Point();
mysymbol.LotsMin()此函数用于获取交易品种成交所需的最小交易量。
double min_lot =  mysymbol.LotsMin();
mysymbol.LotsMax()此函数用于获取交易品种成交所需的最大交易量。
double max_lot =  mysymbol.LotsMax();
mysymbol.LotsStep()此函数用于获取交易品种成交所需的交易量更改的最少步骤。
double lot_step = mysymbol.LotsStep();
mysymbol.NormalizePrice(double price) 此函数用于获取所设定交易品种的正确位数的标准化价格。
// A normalized current Ask price
double n_price = mysymbol.NormalizePrice(mysymbol.Ask()); 
mysymbol.Select()此函数用于确定市场报价窗口中是否已选择了一个交易品种。如果交易品种已选定,则其返回 true,反之,则返回 false。
if (mysymbol.Select())
{
  //Symbol successfully selected
}
else
{
  // Symbol could not be selected
}
mysymbol.Select(bool select)此函数用于在市场报价窗口中选择一个交易品种,或是从其中移除某个交易品种。要注意的是:如果图表已打开,则从市场报价窗口中移除某交易品种;如果已建仓位,则返回 false。
if (!mysymbol.Select())
{
   //Symbol not selected, Select the symbol
    mysymbol.Select(true);
}
else
{
 // Symbol already selected, 
 // remove Symbol from market watch window
    mysymbol.Select(false);
}
mysymbol.MarginInitial()此函数用于获取以预付款货币建立一个一手仓位所需的金额。
double init_margin = mysymbol.MarginInitial() ; 
mysymbol.TradeMode()此函数用于获取交易品种允许的订单执行类型。
if (mysymbol.TradeMode() == SYMBOL_TRADE_MODE_FULL)
{
 // Full trade allowed for this symbol,
 // no trade restrictions 
}
mysymbol.TradeModeDescription()此函数用于获取交易品种允许的订单执行类型的描述。
Print("The trade mode for this symbol is",
       mysymbol.TradeModeDescription());

1.3 СHistoryOrderInfo 类

CHistoryOrderInfo 又是一个会让订单历史属性处理变得非常简单的类。

一旦利用此类创建一个对象之后,我们可以利用该对象访问我们需要用其解决某迫切问题的重要的公共成员函数。

我们将此类的该对象命名为 myhistory

// The CHistoryOrderInfo Class object
CHistoryOrderInfo myhistory;

我们看一看此类的一些重要函数。

在利用此类获取历史订单详情的过程中,我们需要首先获取历史总订单,然后将订单号传递给我们的类对象 myhistory

//Select all history orders within a time period
if (HistorySelect(0,TimeCurrent()))  // get all history orders
{
// Get total orders in history
int tot_hist_orders = HistoryOrdersTotal(); 

我们现在迭代可用的总历史订单,并利用类对象获取每个历史订单的详情。

ulong h_ticket; // Order ticket

for (int j=0; j<tot_hist_orders; j++)
{
  h_ticket = HistoryOrderGetTicket(j));

  if (h_ticket>0)
  {
    // First thing is to now set the order Ticket to work with by our class object 
方法
描述应用示例
myhistory.Ticket(ulong ticket)此函数用于选择我们想要获取其属性或详情的订单号。
myhistory.Ticket(h_ticket);
myhistory.Ticket() 此函数用于获取某订单的订单号。
ulong o_ticket = myhistory.Ticket();
myhistory.TimeSetup()此函数用于获取订单执行或设置的时间。
datetime os_time = myhistory.TimeSetup();
myhistory.OrderType()此函数用于获取订单类型(ORDER_TYPE_BUY 等)。
if (myhistory.OrderType() == ORDER_TYPE_BUY)
{
// This is a buy order
}
myhistory.State()此函数用于获取订单的当前状态。
订单是否已取消、接受、拒绝或下达等。
if(myhistory.State() == ORDER_STATE_REJECTED)
{
// order was rejected, not placed.
}
myhistory.TimeDone()此函数用于获取订单下达、被取消或被拒绝的时间。
datetime ot_done =  myhistory.TimeDone();
myhistory.Magic()此函数用于获取发起订单的“EA 交易”ID。
long o_magic = myhistory.Magic();
myhistory.PositionId()此函数用于获取订单下达时被纳入的仓位 ID。
long o_posid = myhistory.PositionId();
myhistory.PriceOpen()此函数用于获取订单开盘价。
double o_price =  myhistory.PriceOpen();
myhistory.Symbol()此函数用于获取订单的交易品种属性(货币对)。
string o_symbol =  myhistory.Symbol();
      

不要忘了,我们是在一个历史总订单的圈内使用这些函数。

1.4 СOrderInfo 类

COrderInfo 是一个方便访问所有挂单属性的类。此类的对象一旦被创建,就可用于此类的公共成员函数。

此类的用法,在某种程度上同上述讲过的 CHistoryOrderInfo 类相似。

我们创建该类的一个对象,并将其命名为 myorder

// The OrderInfo Class object
COrderInfo myorder;

为了能够利用该类获取挂单详情,我们需要首先获取可用总订单,再按订单号选择它们。

// Select all history orders within a time period
if (HistorySelect(0,TimeCurrent()))   // get all history orders
   {    
     // get total orders
     int o_total = OrdersTotal();

我们会依次通过总订单,并利用创建的对象获取其对应属性。

for (int j=0; j<o_total; j++)
{
 // we must confirm if the order is available for us to get its details using the Select function of the COrderInfo Class
方法
描述
应用示例
myorder.Select(ulong ticket) 此函数用于按订单号选择订单,从而方便订单处理。
if (myorder.Select(OrderGetTicket(j)) 
   { // order has been selected and can now be manipulated.
   }
myorder.Ticket() 此函数用于获取选中订单的订单号。
ulong o_ticket = myorder.Ticket();
myorder.TimeSetup()此函数用于获取订单的设置时间。
datetime o_setup = myorder.TimeSetup();
myorder.Type()此函数用于获取订单类型(比如 ORDER_TYPE_BUY_STOP 等)。
if (myorder.Type() == ORDER_TYPE_BUY_LIMIT)
{
// This is a Buy Limit order, etc
}
myorder.State()此函数用于获取订单状态。
订单是否已取消、接受、拒绝或下达等。
if (myorder.State() ==ORDER_STATE_STARTED)
{
// order has been checked 
// and may soon be treated by the broker
}
myorder.TimeDone()此函数用于获取订单被下达、被拒绝  或被取消的时间。
datetime ot_done = myorder.TimeDone();
myorder.Magic()此函数用于获取发起订单的“EA 交易”ID。
long o_magic =  myorder.Magic();
myorder.PositionId()此函数用于获取订单下达时被纳入的仓位 ID。
long o_posid = myorder.PositionId();
myorder.PriceOpen()此函数用于获取订单开盘价。
double o_price = myorder.PriceOpen();
myorder.StopLoss()此函数用于获取订单的止 损价。
double  s_loss = myorder.StopLoss();
myorder.TakeProfit()此函数用于获取订单的获利价。
double t_profit = myorder.TakeProfit();
myorder.PriceCurrent()此函数用于获取下达订单的交易品种的时价。
double cur_price =  myorder.PriceCurrent();
myorder.Symbol()此函数用于获取下达订单的交易品种的名称。
string o_symbol = myorder.Symbol();
myorder.StoreState()此函数用于保存或存储订单的当前详情,这样一来,我们就能够对比之后是否有任何变化。
myorder.StoreState();
myorder.CheckState()此函数用于检查保存或存储的订单详情是否有变化。
if (myorder.CheckState() == true)
{
// Our order status or details have changed
}

1.5 CDealInfo 类

CDealInfo 类提供访问所有交易属性或信息记录的权限。此类的对象一旦被创建,我们就可以利用它来获取每一条历史交易相关信息,方式与 CHistoryOrderInfo 类相似。

所以,我们想做的第一件事,就是创建一个此类的对象,并将其命名为 mydeal。

// The DealInfo Class object
CDealInfo myinfo; 

我们从获取历史总交易开始。

if (HistorySelect(0,TimeCurrent()))
   {    
    // Get total deals in history
    int tot_deals = HistoryDealsTotal(); 

我们现在迭代可用的总历史订单,并利用类对象获取每个历史订单的详情。

ulong d_ticket; // deal ticket
for (int j=0; j<tot_deals; j++)
    {
     d_ticket = HistoryDealGetTicket(j);
     if (d_ticket>0)  
     {
      // First thing is to now set the deal Ticket to work with by our class object 
方法
描述应用示例
mydeal.Ticket(ulong ticket)此函数用于设置交易订单号,以备我们创建的对象将来使用。
mydeal.Ticket(d_ticket);
mydeal.Ticket() 此函数用于获取交易订单号。
ulong deal_ticket = mydeal.Ticket();
mydeal.Order()此函数用于获取执行交易的订单的订单号。
long deal_order_no =  mydeal.Order();
mydeal.Time()此函数用于获取交易执行的时间。
datetime d_time = mydeal.Time();
mydeal.Type()此函数用于获取交易类型,是否为 DEAL_TYPE_SELL 等等。
if (mydeal.Type() == DEAL_TYPE_BUY)
{
// This deal was executed as a buy deal type
}
mydeal.Entry() 此函数用于获取交易方向,是否为 DEAL_ENTRY_INDEAL_ENTRY_OUT 等等。
if (mydeal.Entry() == DEAL_ENTRY_IN)
{
// This was an IN entry deal
}
mydeal.Magic() 此函数用于获取执行交易的“EA 交易”ID。
long d_magic = mydeal.Magic();
mydeal.PositionId() 此函数用于获取交易所归属仓位的专有仓位 ID。
long d_post_id = mydeal.PositionId();
mydeal.Price() 此函数用于获取交易执行的价格。
double d_price = mydeal.Price();
mydeal.Volume() 此函数用于获取交易量(手数)。
double d_vol = mydeal.Volume();
mydeal.Symbol() 此函数用于获取交易执行的交易品种(货币对)。
string d_symbol = mydeal.Symbol();

1.6 CPositionInfo

CPositionInfo 类可方便对于当前仓位属性的访问。我们必须创建一个此类的对象,才能用它获取仓位属性。

我们创建该类的一个对象,并将其命名为 myposition。

// The object of the CPositionInfo class
CPositionInfo myposition;

现在我们利用该对象获取敞口仓位详情。我们从获取所有敞口仓位开始:

int pos_total = PositionsTotal();

现在,我们从头到尾看一看所有敞口仓位以获取其详情。

for (int j=0; j<pos_total; j++)
    {
方法
描述应用示例
myposition.Select(const string symbol)此函数用于选择对应当前敞口仓位的交易品种,以使其可以操作。
if (myposition.Select(PositionGetSymbol(j)))
{
 // symbol successfully selected, we can now work 
 // on the current open position for this symbol 
}
OR
// when dealing with the current symbol/chart only
if (myposition.Select(_Symbol)) 
{
 // symbol successfully selected, we can now work 
 // on the current open position for this symbol 
}
myposition.Time()此函数用于获取建仓的时间。
datetime pos_time = myposition.Time();
myposition.Type()此函数用于获取建仓的类型。
if (myposition.Type() == POSITION_TYPE_BUY)
{
// This is a buy position
}
myposition.Magic()此函数用于获取建仓的“EA 交易”的 ID。
long pos_magic = myposition.Magic();
myposition.Volume()此函数用于获取敞口仓位的交易量(手数)。
double pos_vol = myposition.Volume(); // Lots
myposition.PriceOpen()此函数用于获取建仓价格 - 仓位开盘价。
double pos_op_price = myposition.PriceOpen();
myposition.StopLoss()此函数用于获取敞口仓位的止损价。
double pos_stoploss = myposition.StopLoss();
myposition.TakeProfit()此函数用于获取敞口仓位的获利价。
double pos_takeprofit = myposition.TakeProfit();
myposition.StoreState()此函数用于存储仓位的当前状态。
// stores the current state of the position
myposition.StoreState();
myposition.CheckState()此函数用于检查敞口仓位的状态有无变化。
if (!myposition.CheckState())
{
  // position status has not changed yet
}
 myposition.Symbol()此函数用于获取以其建仓的交易品种的名称。
string pos_symbol = myposition.Symbol();

1.7 СTrade 类

CTrade 类会方便对于 MQL5 中交易操作的访问。要使用此类,我们必须创建一个此类的对象,然后再用它来执行必要的交易操作。

我们创建该类的一个对象,并将其命名为 mytrade:

//An object of the CTrade class
CTrade mytrade;

第一步,是对该对象要在交易操作进行过程中用到的大多数参数进行设置。

方法描述
应用示例
mytrade.SetExpertMagicNumber(ulong magic)此函数用于设置该类要在交易操作中使用的“EA 交易” ID (幻数)。
ulong Magic_No=12345;
mytrade.SetExpertMagicNumber(Magic_No);
mytrade.SetDeviationInPoints(ulong deviation)此函数用于设置将在交易进行时使用的偏离值(点数)。
ulong Deviation=20;
mytrade.SetDeviationInPoints(Deviation); 
mytrade.OrderOpen(conststring symbol,
ENUM_ORDER_TYPE
order_type,double volume,

double
limit_price,double price,doublesl,
double
tp,ENUM_ORDER_TYPE_TIME type_time,

datetime
expiration,const stringcomment="")
此函数用于下达挂单。要 使用此函数,必须首先准备好参数,然后将其传递给此函数。
// define the input parameters
double Lots = 0.1;
double SL = 0;
double TP = 0;
// latest Bid price using CSymbolInfo class object
double Oprice = mysymbol.Bid()-_Point*550;
// place (BuyStop) pending order
mytrade.OrderOpen(_Symbol,ORDER_TYPE_SELLSTOP,Lots,0.0,Oprice,
                  SL,TP,ORDER_TIME_GTC,0);
mytrade.OrderModify(ulong ticket,double price,
double
sl,double tp,
ENUM_ORDER_TYPE_TIME type_time,datetime expiration)
此函数用于修改现有挂单。
// Select total orders in history and get total pending orders 
// (as shown within the COrderInfo class section). 
// Use the CSymbolInfo class object to get the current ASK/BID price
int Stoploss = 400;
int Takeprofit = 550;
for(int j=0; j<OrdersTotal(); j++)
{
  ulong o_ticket = OrderGetTicket(j);
  if(o_ticket != 0)
  {
   // Stoploss must have been defined
   double SL = mysymbol.Bid() + Stoploss*_Point;   
   // Takeprofit must have been defined  
   double TP = mysymbol.Bid() - Takeprofit*_Point; 
   // lastest ask price using CSymbolInfo class object
   double Oprice = mysymbol.Bid();                 
   // modify pending BuyStop order
   mytrade.OrderModify(o_ticket,Oprice,SL,TP,ORDER_TIME_GTC,0);
  }
}
mytrade.OrderDelete(ulong ticket)此函数用于删除挂单。
// Select total orders in history and get total pending orders
// (as shown within the COrderInfo class section). 

int o_total=OrdersTotal();
for(int j=o_total-1; j>=0; j--)
{
   ulong o_ticket = OrderGetTicket(j);
   if(o_ticket != 0)
   {
    // delete the pending Sell Stop order
    mytrade.OrderDelete(o_ticket);
   }
}
mytrade.PositionOpen(const  string symbol,ENUM_ORDER_TYPE order_type,double volume,double price,double sl,double tp,const string comment="")此函数用于建立一个买入或卖出仓位。要使用此函数,必须首先准备好所有必要参数,然后将其传递给此函数。
// define the input parameters and use the CSymbolInfo class
// object to get the current ASK/BID price
double Lots = 0.1;
// Stoploss must have been defined 
double SL = mysymbol.Ask() – Stoploss*_Point;   
//Takeprofit must have been defined 
double TP = mysymbol.Ask() + Takeprofit*_Point; 
// latest ask price using CSymbolInfo class object
double Oprice = mysymbol.Ask();
// open a buy trade
mytrade.PositionOpen(_Symbol,ORDER_TYPE_BUY,Lots,
                     Oprice,SL,TP,"Test Buy");
mytrade.PositionModify(const string symbol,
double
sl,double tp)
此函数用于修改某现有敞口仓位的止损与(或)获利价。要使用此函数,首先必须利用 CPositionInfo 类对象选择待修改的仓位,用 CSymbolInfo 类对象获取当前的卖/买价。
if (myposition.Select(_Symbol))
{
  int newStoploss = 250;
  int newTakeprofit = 500;
  double SL = mysymbol.Ask() – newStoploss*_Point;    
  double TP = mysymbol.Ask() + newTakeprofit*_Point;  
  //modify the open position for this symbol
 mytrade.PositionModify(_Symbol,SL,TP);
}
mytrade.PositionClose(const string symbol,
ulong
deviation=ULONG_MAX)
此函数用于某现有敞口仓位的平仓。
if (myposition.Select(_Symbol))
{
 //close the open position for this symbol
 mytrade.PositionClose(_Symbol);  
}
mytrade.Buy(double volume,const string symbol=NULL,double price=0.0,double sl=0.0,doubletp=0.0,const string comment="")   此函数用于建立一个买入交易。使用此函数时,建议您设定交易的交易量(或手数)。而 tp (获利价)和 sl (止损价)可通过修改已建仓稍后设置,它会利用当前的 Ask(买) 价格建立交易。
double Lots = 0.1;
// Stoploss must have been defined 
double SL = mysymbol.Ask() – Stoploss*_Point; 
//Takeprofit must have been defined
double TP = mysymbol.Ask() +Takeprofit*_Point;
// latest ask price using CSymbolInfo class object
double Oprice = mysymbol.Ask();
// open a buy trade
mytrade.Buy(Lots,NULL,Oprice,SL,TP,“Buy Trade”);

//OR
mytrade.Buy(Lots,NULL,0.0,0.0,0.0,“Buy Trade”);
// modify position later
mytrade.Sell(double volume,const string symbol=NULL,double price=0.0,double sl=0.0,doubletp=0.0,const string comment="") 此函数用于建立一个卖出交易。使用此函数时,建议您设定交易的交易量(或手数)。而 tp (获利价)和 sl (止损价)可通过修改已建仓稍后设置,它会利用当前的 Bid(卖价 价格建立交易。
double Lots = 0.1;
// Stoploss must have been defined 
double SL = mysymbol.Bid() + Stoploss*_Point;
//Takeprofit must have been defined
double TP = mysymbol.Bid() - Takeprofit*_Point; 
// latest bid price using CSymbolInfo class object
double Oprice = mysymbol.Bid();
// open a Sell trade
mytrade.Sell(Lots,NULL,Oprice,SL,TP,“Sell Trade”); 

//OR
mytrade.Sell(Lots,NULL,0.0,0.0,0.0,“Sell Trade”); 
//(modify position later)
mytrade.BuyStop(double volume,double price,const string symbol=NULL,double sl=0.0,double tp=0.0,
ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,datetime expiration=0,const string comment="")
此函数用于下达 BuyStop 挂单。缺省的订单类型时间为 ORDER_TIME_GTC,到期时间为 0。如果您心中的订单类型时间与之相同,则无需指定上述两个变量。
 double Lot = 0.1;
//Buy price = bar 1 High + 2 pip + spread
 int sprd=mysymbol.Spread();
 double bprice =mrate[1].high + 2*_Point + sprd*_Point;
//--- Buy price
 double mprice=NormalizeDouble(bprice,_Digits); 
//--- Stop Loss
 double stloss = NormalizeDouble(bprice - STP*_Point,_Digits);
//--- Take Profit
 double tprofit = NormalizeDouble(bprice+ TKP*_Point,_Digits);
//--- open BuyStop order
 mytrade.BuyStop(Lot,mprice,_Symbol,stloss,tprofit); 
mytrade.SellStop(double volume,double price,const string symbol=NULL,double sl=0.0,double tp=0.0, ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,datetime expiration=0,const string  comment="")此函数用于下达带有设定参数的 SellStop 挂单。 缺省订单类型时间为 ORDER_TIME_GTC,到期时间为 0。如果您心中的订单类型时间与之相同,则无需指定上述两个变量。
 double Lot = 0.1;
//--- Sell price = bar 1 Low - 2 pip 
//--- MqlRates mrate already declared
 double sprice=mrate[1].low-2*_Point;
//--- SellStop price
 double slprice=NormalizeDouble(sprice,_Digits);
//--- Stop Loss
 double ssloss=NormalizeDouble(sprice+STP*_Point,_Digits);
//--- Take Profit
 double stprofit=NormalizeDouble(sprice-TKP*_Point,_Digits);
//--- Open SellStop Order
 mytrade.SellStop(Lot,slprice,_Symbol,ssloss,stprofit);
mytrade.BuyLimit(double volume,double price,const string symbol=NULL,double sl=0.0,double tp=0.0, ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,datetime expiration=0,const string  comment="") 此函数用于下达带有设定参数的 BuyLimit 订单。 
Usage:
//--- Buy price = bar 1 Open  - 5 pip + spread
double Lot = 0.1;
int sprd=mysymbol.Spread();
//--- symbol spread
double bprice = mrate[1].open - 5*_Point + sprd*_Point;
//--- MqlRates mrate already declared
double mprice=NormalizeDouble(bprice,_Digits);
//--- BuyLimit price
//--- place buyLimit order, modify stoploss and takeprofit later
mytrade.BuyLimit(Lot,mprice,_Symbol); 
mytrade.SellLimit (double volume,double price,const string symbol=NULL,double sl=0.0,double tp=0.0, ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC,datetime expiration=0,const string comment="")此函数用于下达带有设定参数的 Sell Limit 订单。 
//--- Sell Limit price = bar 1 Open  + 5 pip
double Lot = 0.1;
//--- MqlRates mrate already declared
double sprice = mrate[1].open + 5*_Point;
//--- SellLimit
double slprice=NormalizeDouble(sprice,_Digits);
//place SellLimit order, modify stoploss and takeprofit later
mytrade.SellLimit(Lot,slprice,_Symbol);

   交易结果函数

  
mytrade.ResultRetcode()此函数用于获取交易操作的结果代码。
// a trade operation has just been carried out
int return_code = mytrade.ResultRetcode();
mytrade.ResultRetcodeDescription()此函数用于获取交易操作返回代码的完整描述或阐释。
string ret_message =  ResultRetcodeDescription();
// display it
Alert("Error code - " , mytrade.ResultRetcode() ,
      "Error message - ", ret_message);
mytrade.ResultDeal()此函数用于获取敞口仓位的交易订单号。
long dl_ticket = mytrade.ResultDeal();
mytrade.ResultOrder()此函数用于获取所建仓的订单号。
long o_ticket = mytrade.ResultOrder();
mytrade.ResultVolume()此函数用于获取所建仓的订单的交易量(手数)。
double o_volume = mytrade.ResultVolume();
mytrade.ResultPrice()此函数用于获取所建仓的交易价格。
double r_price = mytrade.ResultPrice();
mytrade.ResultBid()此函数用于获取当前的市场卖价(重新报价价格)。
double rq_bid = mytrade.ResultBid;
mytrade.ResultAsk()此函数用于获取当前的市场买价(重新报价价格)。
double rq_ask = mytrade.ResultAsk;
mytrade.PrintRequest() / mytrade.PrintResult() 这两个函数可用于将交易请求参数和结果参数分别呈现于 Journal 选项卡。
// after a trade operation
// prints the trade request parameters
mytrade.PrintRequest(); 
//prints the trade results
mytrade.PrintResult();  

 交易请求函数

  
 mytrade.RequestAction()此函数用于获取刚刚发送的最后一个交易请求的交易操作类型。
//determine the Trade operation type for the last Trade request
if (mytrade.RequestAction() == TRADE_ACTION_DEAL)
{
  // this is a market order for an immediate execution
}
else if (mytrade.RequestAction() == TRADE_ACTION_PENDING)
{
  // this is a pending order.
}  
 mytrade.RequestMagic()此函数用于获取最后一个请求中使用的“EA 交易”幻数。
ulong mag_no = mytrade. RequestMagic();
 mytrade.RequestOrder()
 此函数用于获取最后一个请求中使用的订单号。它主要是与挂单的修改相关。
ulong po_ticket =  mytrade.RequestOrder();
mytrade.RequestSymbol()此函数用于获取最后一个请求中使用的交易品种或货币对。
string symb = mytrade.RequestSymbol(); 
mytrade.RequestVolume()此函数用于获取最后一个请求中进行交易的交易量(手数)。 
double Lot = mytrade.RequestVolume();  
mytrade.RequestPrice()此函数用于获取最后一个请求中使用的 订单价格。
double oprice = mytrade.RequestPrice();   
mytrade.RequestStopLimit()此函数用于获取最后一个请求中使用的 止损价。
double limitprice = mytrade.RequestStopLimit(); 
mytrade.RequestSL()此函数用于获取最后一个请求中使用的 止损价。
double sloss = mytrade.RequestSL();  
mytrade.RequestTP()此函数用于获取最后一个请求中使用的 获利价。
double tprofit = mytrade.RequestTP();   
mytrade.RequestDeviation()此函数用于获取最后一个请求中使用的 偏离。
ulong dev = mytrade.RequestDeviation();  
mytrade.RequestType()此函数用于获取最后一个请求中下达的订单类型。
if (mytrade.RequestType() == ORDER_TYPE_BUY)
 {
  // market order Buy was placed in the last request.
mytrade.RequestTypeDescription()此函数用于获取最后一个请求中下达的订单类型的描述。
Print("The type of order placed in the last request is :",
      mytrade.RequestTypeDescription());  
mytrade.RequestActionDescription()此函数用于获取最后一个请求中使用的请求操作的描述。
Print("The request action used in the last request is :", 
      mytrade.RequestTypeDescription());
mytrade.RequestTypeFillingDescription()此函数用于获取最后一个请求中使用的订单平仓规则类型。
Print("The type of order filling policy used",
      " in the last request is :",
      RequestTypeFillingDescription()); 
 

Trade Class  Request (交易类请求)函数在识别有关下达订单的错误时非常有用。有时候,我们会在下达订单时收到一些错误信息,如果不能马上找出哪里出错,就会让人感觉有点混乱。利用交易类请求函数,我们就能够通过打印发往交易服务器的一些请求参数,找出我们做错的地方。该用法大致如下述代码所示:

 //--- open Buy position and check the result
         if(mytrade.Buy(Lot,_Symbol,mprice,stloss,tprofit))
         //if(mytrade.PositionOpen(_Symbol,ORDER_TYPE_BUY,Lot,mprice,stloss,tprofit)) 
           { //--- Request is completed or order placed           
             Alert("A Buy order at price:", mytrade.ResultPrice() , ", vol:",mytrade.ResultVolume(),
                  " has been successfully placed with deal Ticket#:",mytrade.ResultDeal(),"!!");
            mytrade.PrintResult();
           }
         else
           {
            Alert("The Buy order request at vol:",mytrade.RequestVolume(), ", sl:", mytrade.RequestSL(),
                 ", tp:",mytrade.RequestTP(), ", price:", mytrade.RequestPrice(),
                 " could not be completed -error:",mytrade.ResultRetcodeDescription());
            mytrade.PrintRequest();
            return;
           }

在上述代码中,我们试图在存在错误的情况下,可以识别请求中发送的一些参数。比如说,如果我们未能指定正确的止损价,可能就会收到 Invalid Stops (无效止损价)错误。而利用 mytrade.RequestSL() 显示出止损值,我们就能知道指定的止损价存在什么问题。

讲完每一种类可以如何使用之后,现在,我们该将描述过的一些功能落到实处了。

请注意:我们要在“EA 交易”中使用的所有功能,在上文均有描述。如果您在将要编写的代码中看到任何函数,都建议您参照之前的相关描述。


2. 交易类各项功能的使用

为了演示上述交易类的功能如何使用,我们会编写一个履行下述任务的“EA 交易”。

2.1. 编写“EA 交易”

首先,启动一个新的 MQL5 文档,选择 Expert Advisor (template) ,并点击 Next 按钮:

 启动新的 MQL5 文档


图 1. 启动新的 MQL5 文档

键入“EA 交易”的名称,并点击 Finish 按钮。稍后我们再手动定义输入参数。

赋予新文档一个名称

图 2. 为“EA 交易”命名

创建的新文档大致如下。

EA 代码框架

紧跟 #property 版本行之后,我们纳入要使用的所有交易类

//+------------------------------------------------------------------+
//|  Include ALL classes that will be used                           |
//+------------------------------------------------------------------+
//--- The Trade Class
#include <Trade\Trade.mqh>
//--- The PositionInfo Class
#include <Trade\PositionInfo.mqh>
//--- The AccountInfo Class
#include <Trade\AccountInfo.mqh>
//--- The SymbolInfo Class
#include <Trade\SymbolInfo.mqh>

接下来,我们定义输入参数:

//+------------------------------------------------------------------+
//|  INPUT PARAMETERS                                              |
//+------------------------------------------------------------------+
input int      StopLoss=100;     // Stop Loss
input int      TakeProfit=240;   // Take Profit
input int      ADX_Period=15;    // ADX Period
input int      MA_Period=15;     // Moving Average Period
input ulong    EA_Magic=99977;   // EA Magic Number
input double   Adx_Min=24.0;     // Minimum ADX Value
input double   Lot=0.1;          // Lots to Trade
input ulong    dev=100;          // Deviation 
input long     Trail_point=32;   // Points to increase TP/SL
input int      Min_Bars = 20;    // Minimum bars required for Expert Advisor to trade
input double   TradePct = 25;    // Percentage of Account Free Margin to trade

再来指定将在本“EA 交易”代码中使用的其它参数:

//+------------------------------------------------------------------+
//|  OTHER USEFUL PARAMETERS                                         |
//+------------------------------------------------------------------+
int adxHandle;                     // handle for our ADX indicator
int maHandle;                    // handle for our Moving Average indicator
double plsDI[],minDI[],adxVal[]; // Dynamic arrays to hold the values of +DI, -DI and ADX values for each bars
double maVal[];                  // Dynamic array to hold the values of Moving Average for each bars
double p_close;                    // Variable to store the close value of a bar
int STP, TKP;                   // To be used for Stop Loss, Take Profit 
double TPC;                        // To be used for Trade percent

现在,我们为已经纳入的每一个类创建一个对象:

//+------------------------------------------------------------------+
//|  CREATE CLASS OBJECTS                                            |
//+------------------------------------------------------------------+
//--- The Trade Class Object
CTrade mytrade;
//--- The PositionInfo Class Object
CPositionInfo myposition;
//--- The AccountInfo Class Object
CAccountInfo myaccount;
//--- The SymbolInfo Class Object
CSymbolInfo mysymbol;

现在我们要做的,就是对将要使用的一些函数进行定义,方便我们自己的工作。

一旦定义完毕,我们到 OnInit()OnTick() 函数的必要部分中去调用它们即可。

2.1.1 checkTrading 函数

此函数会被用于执行所有的初步检查,查看我们的“EA 交易”能否交易。如果此函数返回 true,我们的 EA 则会继续执行交易;否则,EA 不会执行任何交易。

//+------------------------------------------------------------------+
//|  Checks if our Expert Advisor can go ahead and perform trading   |
//+------------------------------------------------------------------+
bool checkTrading()
{
  bool can_trade = false;
  // check if terminal is syncronized with server, etc
  if (myaccount.TradeAllowed() && myaccount.TradeExpert() && mysymbol.IsSynchronized())
  {
    // do we have enough bars?
    int mbars = Bars(_Symbol,_Period);
    if(mbars >Min_Bars)
    {
      can_trade = true;
    }
  }
  return(can_trade);
}

我们声明一个布尔数据类型 can_trade 并设定为 false。我们利用 CAccountInfo 类的对象来检查是否允许交易,以及是否允许“EA 交易”在该账户上交易。我们还利用 CSymbolInfo 类的对象来检查终端是否与交易服务器同步。

一旦上述三个条件得到满足,我们就会检查当前柱的总数是否大于 EA 交易所需的最少柱数。如果此函数返回 true,则我们的 EA 会执行交易活动;否则,我们的 EA 不会从事任何交易活动,直到此函数内的相关条件被满足。

正如您所见,我们已经决定利用标准交易类库的必要对象,将所有必要的交易检查活动纳入到此函数中来。

2.1.2 ConfirmMargin 函数

//+------------------------------------------------------------------+
//|  Confirms if margin is enough to open an order
//+------------------------------------------------------------------+
bool ConfirmMargin(ENUM_ORDER_TYPE otype,double price)
  {
   bool confirm = false;
   double lot_price = myaccount.MarginCheck(_Symbol,otype,Lot,price); // Lot price/ Margin    
   double act_f_mag = myaccount.FreeMargin();                        // Account free margin 
   // Check if margin required is okay based on setting
   if(MathFloor(act_f_mag*TPC)>MathFloor(lot_price))
     {
      confirm =true;
     }
    return(confirm);
  }

我们利用 CAccountInfo 类的对象,根据我们只会使用账户中特定百分比的可用预付款来下达订单的设置,确定是否有足够的预付款来进行交易。

如果要求的账户可用预付款百分比大于订单要求的预付款,则此函数返回 true;否则返回 false。如此一来,我们只会在该函数返回 true 的情况下下达订单。此函数将订单类型作为输入参数。

2.1.3 checkBuy 函数

//+------------------------------------------------------------------+
//|  Checks for a Buy trade Condition                                |
//+------------------------------------------------------------------+
bool checkBuy()
{
  bool dobuy = false;
  if ((maVal[0]>maVal[1]) && (maVal[1]>maVal[2]) &&(p_close > maVal[1]))
  {
    // MA increases upwards and previous price closed above MA
    if ((adxVal[1]>Adx_Min)&& (plsDI[1]>minDI[1]))
    {
      // ADX is greater than minimum and +DI is greater tha -DI for ADX
      dobuy = true;
    }
  }
  return(dobuy);
}

我们决定将建立一个买入交易的条件打包到此函数中。这里的任何类对象功能,我们都没用过。我们要检查存不存在这样的条件:移动平均线指标值持续上升,且前一柱的收盘价高于此点位移动平均线的值。

我们还想要这样的情况:ADX 指标值大于输入参数中设定的所需最小值,而且 ADX 指标的 + DI 高于 - DI 值。一旦上述条件得到满足,我们就让 EA 建立一个买入订单。

2.1.4 checkSell 函数

//+------------------------------------------------------------------+
//|  Checks for a Sell trade Condition                               |
//+------------------------------------------------------------------+
bool checkSell()
{
  bool dosell = false;
  if ((maVal[0]<maVal[1]) && (maVal[1]<maVal[2]) &&(p_close < maVal[1]))
  {
    // MA decreases downwards and previuos price closed below MA
    if ((adxVal[1]>Adx_Min)&& (minDI[1]>plsDI[1]))
    {
      // ADX is greater than minimum and -DI is greater tha +DI for ADX
      dosell = true;
    }
  }
  return(dosell);
} 

此函数的检查项与 CheckBuy 函数正好相反。此函数中的任何类对象,我们也都没用过。此函数会检查存不存在这样的条件:移动平均线指标值持续下降,且前一柱的收盘价低于此点位移动平均线的值。

我们还想要这样的情况:ADX 指标值大于输入参数中设定的所需最小值,而且 ADX 指标的 - DI 高于 + DI 值。一旦上述条件得到满足,我们就让 EA 建立一个卖出订单。

2.1.5 checkClosePos 函数
//+------------------------------------------------------------------+
//|  Checks if an Open position can be closed                        |
//+------------------------------------------------------------------+
bool checkClosePos(string ptype, double Closeprice)
{
   bool mark = false;
   if (ptype=="BUY")
   {
      // Can we close this position
     if (Closeprice < maVal[1]) // Previous price close below MA
      {
         mark = true;
      }
   }
   if (ptype=="SELL")
   {
      // Can we close this position
      if (Closeprice > maVal[1]) // Previous price close above MA
      {
         mark = true;
      }
   }
   return(mark);
}

此函数用于检查当前的敞口仓位可否平仓。此函数用于监控前一柱的收盘价与该点位的移动平均线指标值相比,孰高孰低(取决于交易方向)。

如有任何一个条件被满足,则此函数返回 true,之后,我们就可以静待 EA 平仓了。此函数有两个输入参数,订单类型(此次的名称为买入或卖出),以及前一柱的收盘价。

2.1.6 ClosePosition 函数

//+------------------------------------------------------------------+
//| Checks and closes an open position                                                                |
//+------------------------------------------------------------------+
bool ClosePosition(string ptype,double clp)
  {
   bool marker=false;
     
      if(myposition.Select(_Symbol)==true)
        {
         if(myposition.Magic()==EA_Magic && myposition.Symbol()==_Symbol)
           {
            //--- Check if we can close this position
            if(checkClosePos(ptype,clp)==true)
              {
               //--- close this position and check if we close position successfully?
               if(mytrade.PositionClose(_Symbol)) //--- Request successfully completed 
                 {
                  Alert("An opened position has been successfully closed!!");
                  marker=true;
                 }
               else
                 {
                  Alert("The position close request could not be completed - error: ",
                       mytrade.ResultRetcodeDescription());
                 }
              }
           }
        }
      return(marker);
     }

此函数是上述函数 (checkclosepos) 的实际使用者。它会利用 CPositionInfoCTrade 类的对象。此函数会利用 CPositionInfo 类的对象来检查 EA 建立的仓位和当前交易品种的敞口仓位。如果发现任何仓位,则会检查可否利用 checkclosepos 函数平仓。

如果 checkclosepos 函数返回 true,此函数则利用CTrade 类的对象来平仓,并显示平仓操作的结果。如果平仓成功,此函数则返回 true,否则返回 false。

此函数采用两个输入参数(仓位名称 ,BUY 或 SELL 以及 前一柱收盘价)。上述参数被实际传递给使用它们的 checkclosepos 函数。

2.1.7 CheckModify 函数

//+------------------------------------------------------------------+
//|  Checks if we can modify an open position                        |
//+------------------------------------------------------------------+
bool CheckModify(string otype,double cprc)
{
   bool check=false;
   if (otype=="BUY")
   {
      if ((maVal[2]<maVal[1]) && (maVal[1]<maVal[0]) && (cprc>maVal[1]) && (adxVal[1]>Adx_Min))
      {
         check=true;
      }
   }
   else if (otype=="SELL")
   {
      if ((maVal[2]>maVal[1]) && (maVal[1]>maVal[0]) && (cprc<maVal[1]) && (adxVal[1]>Adx_Min))
      {
         check=true;
      }
   }
   return(check);
} 

此函数用于检查有无可以确认某已建仓可否修改的条件。它会利用订单类型名称和前一柱收盘价格作为输入参数。

此函数的作用,就是检查移动平均线是否仍在持续上升,前一柱收盘价是否仍高于此点位的移动平均值,ADX 的值是否大于所要求的最小值(针对某买入仓位);同时,它还会检查移动平均线是否仍在持续下降,以及前一柱收盘价是否低于该点位的移动平均值(针对某卖出仓位)。根据我们具备的仓位类型,如有任何条件被满足,EA 会考虑修改仓位。

此函数采用两个输入参数(仓位名称 ,买入或卖出,以及 前一柱收盘价)。

2.1.8 Modify 函数

//+------------------------------------------------------------------+
//| Modifies an open position                                        |
//+------------------------------------------------------------------+
   void Modify(string ptype,double stpl,double tkpf)
     {
       //--- New Stop Loss, new Take profit, Bid price, Ask Price
      double ntp,nsl,pbid,pask;                  
      long tsp=Trail_point;
       //--- adjust for 5 & 3 digit prices
      if(_Digits==5 || _Digits==3) tsp=tsp*10;   
       //--- Stops Level
      long stplevel= mysymbol.StopsLevel();      
       //--- Trail point must not be less than stops level
      if(tsp<stplevel) tsp=stplevel;
      if(ptype=="BUY")
        {
          //--- current bid price
         pbid=mysymbol.Bid();           
         if(tkpf-pbid<=stplevel*_Point)
           {
            //--- distance to takeprofit less or equal to Stops level? increase takeprofit
            ntp = pbid + tsp*_Point;
            nsl = pbid - tsp*_Point;
           }
         else
           {
            //--- distance to takeprofit higher than Stops level? dont touch takeprofit
            ntp = tkpf;
            nsl = pbid - tsp*_Point;
           }
        }
      else //--- this is SELL
        {
          //--- current ask price
         pask=mysymbol.Ask();            
         if(pask-tkpf<=stplevel*_Point)
           {
            ntp = pask - tsp*_Point;
            nsl = pask + tsp*_Point;
           }
         else
           {
            ntp = tkpf;
            nsl = pask + tsp*_Point;
           }
        }
      //--- modify and check result
      if(mytrade.PositionModify(_Symbol,nsl,ntp))  
        {
          //--- Request successfully completed    
         Alert("An opened position has been successfully modified!!");
         return;
        }
      else
        {
         Alert("The position modify request could not be completed - error: ",
               mytrade.ResultRetcodeDescription());
         return;
        }

     }

 此函数会利用上述函数 (checkmodify) 完成其工作。它会利用 CSymbolInfoCTrade 类的对象。首先,我们声明四个双精度数据类型,用以保存新的获利价、止损价、卖价和买价。之后,我们声明一个新的长数据类型 tsp ,用以保存输入参数部分中设定的 Trail_point 值。

随后将追踪点值 (tsp) 针对 5 位和 3 位价格调整。然后,我们利用 CSymbolInfo 对象获取止损价位,并确保我们要添加的追踪点不低于要求的止损价位。如果低于止损价位,我们则采用止损值。

根据持仓类型,我们利用 CSymbolInfo 类对象来获取卖价或买价(视情况而定)。如果当前卖价或买价与初始获利价之间的差异低于或等于止损价,则我们决定对止损价与获利价两者同时进行调整;否则,只调整止损值。

之后,我们利用 CTrade 类对象来修改仓位的止损和获利价。还会根据交易结果返回代码,显示成功或失败信息。

为了减少工作量,我们已经完成了一些用户定义函数的定义。现在我们准备处理 EA 代码部分。

2.1.9 OnInit 部分

//--- set the symbol name for our SymbolInfo Object
   mysymbol.Name(_Symbol);
// Set Expert Advisor Magic No using our Trade Class Object
   mytrade.SetExpertMagicNumber(EA_Magic);
// Set Maximum Deviation using our Trade class object
   mytrade.SetDeviationInPoints(dev);
//--- Get handle for ADX indicator
   adxHandle=iADX(NULL,0,ADX_Period);
//--- Get the handle for Moving Average indicator
   maHandle=iMA(_Symbol,Period(),MA_Period,0,MODE_EMA,PRICE_CLOSE);
//--- What if handle returns Invalid Handle
   if(adxHandle<0 || maHandle<0)
     {
      Alert("Error Creating Handles for MA, ADX indicators - error: ",GetLastError(),"!!");
      return(1);
     }
   STP = StopLoss;
   TKP = TakeProfit;
//--- Let us handle brokers that offers 5 or 3 digit prices instead of 4
   if(_Digits==5 || _Digits==3)
     {
      STP = STP*10;
      TKP = TKP*10;
     }
   
//--- Set trade percent
    TPC = TradePct;
    TPC = TPC/100;
//---

我们决定设置 CSymbolInfo 类对象的当前交易品种。我们还要利用 CTrade 类对象设置“EA 交易”的幻数和偏离值(点数)。此后,我们决定获取指标的句柄,如果获取失败,还要显示错误信息。

接下来,我们决定调整 3 位和 5 位价格的止损价与获利价;此外,我们还将交易使用的可用账户预付款转换为百分比。

2.1.10 OnDeinit 部分

//--- Release our indicator handles
   IndicatorRelease(adxHandle);
   IndicatorRelease(maHandle);

这里我们要释放所有的指标句柄。

2.1.11. OnTick 部分

//--- check if EA can trade
    if (checkTrading() == false) 
   {
      Alert("EA cannot trade because certain trade requirements are not meant");
      return;
   }
//--- Define the MQL5 MqlRates Structure we will use for our trade
   MqlRates mrate[];          // To be used to store the prices, volumes and spread of each bar
/*
     Let's make sure our arrays values for the Rates, ADX Values and MA values 
     is store serially similar to the timeseries array
*/
// the rates arrays
   ArraySetAsSeries(mrate,true);
// the ADX values arrays
   ArraySetAsSeries(adxVal,true);
// the MA values arrays
   ArraySetAsSeries(maVal,true);
// the minDI values array
   ArraySetAsSeries(minDI,true);
// the plsDI values array
   ArraySetAsSeries(plsDI,true);

这里我们要做的第一件事,就是检查并确认 EA 应不应该交易。如果 checktrade 函数返回 false,则 EA 会等待下一次价格跳动,并再次检查。

此后,我们声明一个 MQL5 MqlRates 结构以获取每个柱的价格,然后再用 ArraySetAsSeries 函数设置所有的必需数组。

//--- Get the last price quote using the SymbolInfo class object function
   if (!mysymbol.RefreshRates())
     {
      Alert("Error getting the latest price quote - error:",GetLastError(),"!!");
      return;
     }

//--- Get the details of the latest 3 bars
   if(CopyRates(_Symbol,_Period,0,3,mrate)<0)
     {
      Alert("Error copying rates/history data - error:",GetLastError(),"!!");
      return;
     }

//--- EA should only check for new trade if we have a new bar
// lets declare a static datetime variable
   static datetime Prev_time;
// lest get the start time for the current bar (Bar 0)
   datetime Bar_time[1];
   //copy the current bar time
   Bar_time[0] = mrate[0].time;
// We don't have a new bar when both times are the same
   if(Prev_time==Bar_time[0])
     {
      return;
     }
//Save time into static varaiable, 
   Prev_time = Bar_time[0]; 

我们利用 CSymbolInfo 类对象来获取当前报价,再将当前柱价格复制到 mrates 数组。紧接着决定检查是否存在新柱。

如有新柱,我们的 EA 就会继续检查买入或卖出条件是否已满足;否则,它就会等到有新柱出现为止。

//--- Copy the new values of our indicators to buffers (arrays) using the handle
   if(CopyBuffer(adxHandle,0,0,3,adxVal)<3 || CopyBuffer(adxHandle,1,0,3,plsDI)<3
      || CopyBuffer(adxHandle,2,0,3,minDI)<3)
     {
      Alert("Error copying ADX indicator Buffers - error:",GetLastError(),"!!");
      return;
     }
   if(CopyBuffer(maHandle,0,0,3,maVal)<3)
     {
      Alert("Error copying Moving Average indicator buffer - error:",GetLastError());
      return;
     }
//--- we have no errors, so continue
// Copy the bar close price for the previous bar prior to the current bar, that is Bar 1

   p_close=mrate[1].close;  // bar 1 close price

这里,我们利用 CopyBuffer 函数将指标缓冲区放入数组;如果过程中出现错误,则予显示。前一柱收盘价格被复制。

//--- Do we have positions opened already?
  bool Buy_opened = false, Sell_opened=false; 
   if (myposition.Select(_Symbol) ==true)  // we have an opened position
    {
      if (myposition.Type()== POSITION_TYPE_BUY)
       {
            Buy_opened = true;  //It is a Buy
          // Get Position StopLoss and Take Profit
           double buysl = myposition.StopLoss();      // Buy position Stop Loss
           double buytp = myposition.TakeProfit();    // Buy position Take Profit
           // Check if we can close/modify position
           if (ClosePosition("BUY",p_close)==true)
             {
                Buy_opened = false;   // position has been closed
                return; // wait for new bar
             }
           else
           {
              if (CheckModify("BUY",p_close)==true) // We can modify position
              {
                  Modify("BUY",buysl,buytp);
                  return; // wait for new bar
              }
           } 
       }
      else if(myposition.Type() == POSITION_TYPE_SELL)
       {
            Sell_opened = true; // It is a Sell
            // Get Position StopLoss and Take Profit
            double sellsl = myposition.StopLoss();    // Sell position Stop Loss
            double selltp = myposition.TakeProfit();  // Sell position Take Profit
             if (ClosePosition("SELL",p_close)==true)
             {
               Sell_opened = false;  // position has been closed
               return;   // wait for new bar
             }
             else
             {
                 if (CheckModify("SELL",p_close)==true) // We can modify position
                 {
                     Modify("SELL",sellsl,selltp);
                     return;  //wait for new bar
                 }
             } 
       }
    } 

我们利用 CPositionInfo 类对象进行选择,并检查当前交易品种是否有敞口仓位。如果存在仓位,且为买入,我们则将 Buy_opened 设置为 true,再利用 CPositionInfo 类对象获取仓位的止损价获利价。我们利用之前定义的一个函数 - ClosePosition 来确定可否平仓。如果函数返回 true,则已平仓;我们将 Buy_opened 设置为 false,初始买入仓位刚刚平仓。现在,EA 就会等待新的价格跳动。

但是,如果函数返回 false,则尚未平仓。现在就该检查我们能否修改仓位。利用我们之前定义的 CheckModify 函数实现。如果函数返回 true,则意味着仓位可以修改;我们利用 Modify 函数来修改仓位。

另一方面,如果存在仓位且为卖出,我们则将 Sell_opened 设置为 true 并利用 CPositionInfo 类对象来获取仓位的止损价与获利价。重复针对买入仓位执行的同一步骤,从而看看仓位可否平仓或修改。

      if(checkBuy()==true)
        {
         //--- any opened Buy position?
         if(Buy_opened)
           {
            Alert("We already have a Buy position!!!");
            return;    //--- Don't open a new Sell Position
           }

         double mprice=NormalizeDouble(mysymbol.Ask(),_Digits);                //--- latest ask price
         double stloss = NormalizeDouble(mysymbol.Ask() - STP*_Point,_Digits); //--- Stop Loss
         double tprofit = NormalizeDouble(mysymbol.Ask()+ TKP*_Point,_Digits); //--- Take Profit
         //--- check margin
         if(ConfirmMargin(ORDER_TYPE_BUY,mprice)==false)
           {
            Alert("You do not have enough money to place this trade based on your setting");
            return;
           }
         //--- open Buy position and check the result
         if(mytrade.Buy(Lot,_Symbol,mprice,stloss,tprofit))
         //if(mytrade.PositionOpen(_Symbol,ORDER_TYPE_BUY,Lot,mprice,stloss,tprofit)) 
           {
               //--- Request is completed or order placed
             Alert("A Buy order has been successfully placed with deal Ticket#:",
                  mytrade.ResultDeal(),"!!");
           }
         else
           {
            Alert("The Buy order request at vol:",mytrade.RequestVolume(), 
                  ", sl:", mytrade.RequestSL(),", tp:",mytrade.RequestTP(),
                  ", price:", mytrade.RequestPrice(), 
                     " could not be completed -error:",mytrade.ResultRetcodeDescription());
            return;
           }
        }

或者,我们也可以利用 PositionOpen 函数。

      if(checkBuy()==true)
        {
         //--- any opened Buy position?
         if(Buy_opened)
           {
            Alert("We already have a Buy position!!!");
            return;    //--- Don't open a new Sell Position
           }

         double mprice=NormalizeDouble(mysymbol.Ask(),_Digits);               //--- latest Ask price
         double stloss = NormalizeDouble(mysymbol.Ask() - STP*_Point,_Digits); //--- Stop Loss
         double tprofit = NormalizeDouble(mysymbol.Ask()+ TKP*_Point,_Digits); //--- Take Profit
         //--- check margin
         if(ConfirmMargin(ORDER_TYPE_BUY,mprice)==false)
           {
            Alert("You do not have enough money to place this trade based on your setting");
            return;
           }
         //--- open Buy position and check the result
         //if(mytrade.Buy(Lot,_Symbol,mprice,stloss,tprofit))
         if(mytrade.PositionOpen(_Symbol,ORDER_TYPE_BUY,Lot,mprice,stloss,tprofit))
           {
              //--- Request is completed or order placed            
              Alert("A Buy order has been successfully placed with deal Ticket#:",
            mytrade.ResultDeal(),"!!");
           }
         else
           {
            Alert("The Buy order request at vol:",mytrade.RequestVolume(), 
                    ", sl:", mytrade.RequestSL(),", tp:",mytrade.RequestTP(), 
                    ", price:", mytrade.RequestPrice(), 
                    " could not be completed -error:",mytrade.ResultRetcodeDescription());
            return;
           }
        }

这里,我们利用 checkbuy 函数来检查  有无买入设置;如果返回 true,则我们的买入交易条件已被满足。如果我们已有一个买入仓位,并且不想下达新订单。那么,我们可以使用 CSymbolInfo 类对象来获取当前买价,并且根据要求计算出止损价与获利价。

我们还使用 ConfirmMargin 函数来检查下达订单所允许的账户百分比,是否大于下达此订单要求的预付款比例。如果函数返回 true,我们则继续进行交易;否则不执行交易。

我们利用 CTrade 类对象下达订单,并利用相同的调用对象来获取交易操作返回代码。会根据交易结果显示一条信息。

      if(checkSell()==true)
        {
         //--- any opened Sell position?
         if(Sell_opened)
           {
            Alert("We already have a Sell position!!!");
            return;    //--- Wait for a new bar
           }

         double sprice=NormalizeDouble(mysymbol.Bid(),_Digits);             //--- latest Bid price
         double ssloss=NormalizeDouble(mysymbol.Bid()+STP*_Point,_Digits);   //--- Stop Loss
         double stprofit=NormalizeDouble(mysymbol.Bid()-TKP*_Point,_Digits); //--- Take Profit
         //--- check margin
         if(ConfirmMargin(ORDER_TYPE_SELL,sprice)==false)
           {
            Alert("You do not have enough money to place this trade based on your setting");
            return;
           }
         //--- Open Sell position and check the result
         if(mytrade.Sell(Lot,_Symbol,sprice,ssloss,stprofit))
         //if(mytrade.PositionOpen(_Symbol,ORDER_TYPE_SELL,Lot,sprice,ssloss,stprofit))
           {
               //---Request is completed or order placed            
               Alert("A Sell order has been successfully placed with deal Ticket#:",mytrade.ResultDeal(),"!!");
           }
         else
           {
            Alert("The Sell order request at Vol:",mytrade.RequestVolume(), 
                    ", sl:", mytrade.RequestSL(),", tp:",mytrade.RequestTP(), 
                    ", price:", mytrade.RequestPrice(), 
                    " could not be completed -error:",mytrade.ResultRetcodeDescription());
            return;
           }

        }

或者,我们也可以利用 PositionOpen 函数:

      if(checkSell()==true)
        {
         //--- any opened Sell position?
         if(Sell_opened)
           {
            Alert("We already have a Sell position!!!");
            return;    //--- Wait for a new bar
           }

         double sprice=NormalizeDouble(mysymbol.Bid(),_Digits);             //--- latest Bid price
         double ssloss=NormalizeDouble(mysymbol.Bid()+STP*_Point,_Digits);   //--- Stop Loss
         double stprofit=NormalizeDouble(mysymbol.Bid()-TKP*_Point,_Digits); //--- Take Profit
         //--- check margin
         if(ConfirmMargin(ORDER_TYPE_SELL,sprice)==false)
           {
            Alert("You do not have enough money to place this trade based on your setting");
            return;
           }
         //--- Open Sell position and check the result
         //if(mytrade.Sell(Lot,_Symbol,sprice,ssloss,stprofit))
         if(mytrade.PositionOpen(_Symbol,ORDER_TYPE_SELL,Lot,sprice,ssloss,stprofit))
           {
             //---Request is completed or order placed            
             Alert("A Sell order has been successfully placed with deal Ticket#:",mytrade.ResultDeal(),"!!");
           }
         else
           {
            Alert("The Sell order request at Vol:",mytrade.RequestVolume(),
                 ", sl:", mytrade.RequestSL(),", tp:",mytrade.RequestTP(), 
                 ", price:", mytrade.RequestPrice(), 
                   " could not be completed -error:",mytrade.ResultRetcodeDescription());
            return;
           }
        }

就像我们针对买入的做法一样,我们利用 Checksell 函数来检查卖出设置。如其返回 true ,而且我们没有已经敞口的卖出仓位,则利用 ConfirmMargin 函数来检查是否有足够的资金下达订单。如果 ConfirmMargin 返回 true,则 CTrade 类对象会被用于下达订单,而且,根据交易服务器的响应,利用 CTrade 类对象函数显示交易结果。

到现在为止,我们已经学习了在编写“EA 交易”的过程中,我们可以如何使用交易类库。接下来要做的事情,就是利用策略测试程序测试我们的“EA 交易”,同时观察其性能表现。

编译 EA 代码,再将其载入策略测试程序中。

EA 编译报告

         图 3. “EA 交易”编译报告

于 GBPUSD 日线图上利用缺省设置:获利 - 270,止损 - 100 和追踪点 (TP/SL) - 32,我们得到如下结果:

 

图 4. “EA 交易”测试报告 - GBPUSD 日线图

 

 

图 5. “EA 交易”测试图形结果 - GBPUSD 日线图

                                                                                                                                

图 6. “EA 交易”测试报告显示出敞口仓位的修改 - GBPUSD 日线图

 

图 7. GBPUSD 日线图的“EA 交易”测试图表报告

您可以随意就带有不同获利价、止损价及追踪点设置的任何其它交易品种日线图上的 EA 展开测试,再看看您得到了什么。

但是,您要明白,该“EA 交易”仅为测试用途编写……

现在我们来看看,如何利用其它类 COrderInfoCHistoryOrderInfo CDealInfo)来获取订单/交易详情。

2.2 打开/删除挂单  

我们会在本例中编写一个简单的“EA 交易”,如果有买入或卖出设置条件被分别满足,它就会下达一个挂单(BuyStop 或 SellStop)。

2.2.1 包含要求的类

//+------------------------------------------------------------------+
//|  Include ALL classes that will be used                           |
//+------------------------------------------------------------------+
//--- The Trade Class
#include <Trade\Trade.mqh>
//--- The PositionInfo Class
#include <Trade\PositionInfo.mqh>
//--- The SymbolInfo Class
#include <Trade\SymbolInfo.mqh>
//--- The OrderInfo Class
#include <Trade\OrderInfo.mqh>

我们已经纳入了将在这个简单的“EA 交易”中使用的四个类。也在上述的示例中进行了阐释。

我不会就此“EA 交易”的每一个部分进行阐释,因为它们都与上述内容类似。但是,“EA 交易”中最基本的、说明我们要在该部分讨论哪些内容的环节,我会一一说明。

唯一不同的是我们决定在全局范围内声明 MqlRates mrate[]。

//--- Define the MQL5 MqlRates Structure we will use for our trade
   MqlRates mrate[];     // To be used to store the prices, volumes and spread of each bar

一旦我们纳入后,还不能忘了创建每个类的对象:

//+------------------------------------------------------------------+
//|  CREATE CLASS OBJECTS                                            |
//+------------------------------------------------------------------+
//--- The CTrade Class Object
CTrade mytrade;
//--- The CPositionInfo Class Object
CPositionInfo myposition;
//--- The CSymbolInfo Class Object
CSymbolInfo mysymbol;
//--- The COrderInfo Class Object
COrderInfo myorder;

CheckBuy()CheckSell() 函数与之前讲过的“EA 交易”中的情况一样。

这里我们想做的是,有买入设置时下达一个 BUYSTOP 订单;有卖出设置时下达一个 SELLSTOP 订单。

现在,我们来仔细看看为便于使用而创建的几个函数。

2.2.2 CountOrders 函数

//+------------------------------------------------------------------+
//|  Count Total Orders for this expert/symbol                             |
//+------------------------------------------------------------------+
int CountOrders()
  {
   int mark=0;

   for(int i=OrdersTotal()-1; i>=0; i--)
     {
      if(myorder.Select(OrderGetTicket(i)))
        {
         if(myorder.Magic()==EA_Magic && myorder.Symbol()==_Symbol) mark++;
        }
     }
   return(mark);
  }

此函数用于获取某个时间点的总挂单。

我们利用 COrderInfo 类的对象来检查订单详情,检查其是否已利用 myorder.Select() 函数成功选定。

如果我们类对象返回的幻数及返回的交易品种正是我们想要的,则“EA 交易”就会下达订单,然后被计数并存储于 mark 变量中。

2.2.3 DeletePending 函数

//+------------------------------------------------------------------+
//| Checks and Deletes a pending order                                |
//+------------------------------------------------------------------+
bool DeletePending()
  {
   bool marker=false;
//--- check all pending orders
   for(int i=OrdersTotal()-1; i>=0; i--)
     {
      if(myorder.Select(OrderGetTicket(i)))
        {
         if(myorder.Magic()==EA_Magic && myorder.Symbol()==_Symbol)
           {
            //--- check if order has stayed more than two bars time
            if(myorder.TimeSetup()<mrate[2].time)
              {
               //--- delete this pending order and check if we deleted this order successfully?
                if(mytrade.OrderDelete(myorder.Ticket())) //Request successfully completed 
                  {
                    Alert("A pending order with ticket #", myorder.Ticket(), " has been successfully deleted!!");
                    marker=true;
                  }
                 else
                  {
                    Alert("The pending order # ",myorder.Ticket(),
                             " delete request could not be completed - error: ",mytrade.ResultRetcodeDescription());
                  }

              }
           }
        }
     }
   return(marker);
  }

就像 countorder 函数一样,此函数也利用 COrderInfo 类函数来获取订单属性。此函数会检查三柱前是否有任何挂单设置(挂单设置时间早于 mrate[2].time),而且尚未被触发。

如有任何订单属于此类,则利用 CTrade 类函数 OrderDelete 删除该订单。如果成功,此函数会返回 true;否则返回 false。

一旦有新柱形成,就在检查有无新交易设置之前,立即使用上述两个函数。我们要确保每个时间点下达的挂单都不超过三个。为此,我们采用下述代码:

// do we have more than 3 already placed pending orders
if (CountOrders()>3) 
  {
     DeletePending(); 
     return;  
  }

2.2.4 下达挂单

   if(checkBuy()==true)
     {
      Alert("Total Pending Orders now is :",CountOrders(),"!!");
      //--- any opened Buy position?
      if(Buy_opened)
        {
         Alert("We already have a Buy position!!!");
         return;    //--- Don't open a new Sell Position
        }
      //Buy price = bar 1 High + 2 pip + spread
      int sprd=mysymbol.Spread();
      double bprice =mrate[1].high + 10*_Point + sprd*_Point;
      double mprice=NormalizeDouble(bprice,_Digits);               //--- Buy price
      double stloss = NormalizeDouble(bprice - STP*_Point,_Digits); //--- Stop Loss
      double tprofit = NormalizeDouble(bprice+ TKP*_Point,_Digits); //--- Take Profit
      //--- open BuyStop order
      if(mytrade.BuyStop(Lot,mprice,_Symbol,stloss,tprofit))
      //if(mytrade.OrderOpen(_Symbol,ORDER_TYPE_BUY_STOP,Lot,0.0,bprice,stloss,tprofit,ORDER_TIME_GTC,0)) 
        {
         //--- Request is completed or order placed
         Alert("A BuyStop order has been successfully placed with Ticket#:",mytrade.ResultOrder(),"!!");
         return;
        }
      else
        {
         Alert("The BuyStop order request at vol:",mytrade.RequestVolume(), 
                 ", sl:", mytrade.RequestSL(),", tp:",mytrade.RequestTP(),
               ", price:", mytrade.RequestPrice(), 
                 " could not be completed -error:",mytrade.ResultRetcodeDescription());
         return;
        }
     }

或者,我们也可以利用 OrderOpen 函数下达 BUYSTOP 订单。

   if(checkBuy()==true)
     {
      Alert("Total Pending Orders now is :",CountOrders(),"!!");
      //--- any opened Buy position?
      if(Buy_opened)
        {
         Alert("We already have a Buy position!!!");
         return;    //--- Don't open a new Sell Position
        }
      //Buy price = bar 1 High + 2 pip + spread
      int sprd=mysymbol.Spread();
      double bprice =mrate[1].high + 10*_Point + sprd*_Point;
      double mprice=NormalizeDouble(bprice,_Digits);               //--- Buy price
      double stloss = NormalizeDouble(bprice - STP*_Point,_Digits); //--- Stop Loss
      double tprofit = NormalizeDouble(bprice+ TKP*_Point,_Digits); //--- Take Profit
      //--- open BuyStop order
      //if(mytrade.BuyStop(Lot,mprice,_Symbol,stloss,tprofit))
      if(mytrade.OrderOpen(_Symbol,ORDER_TYPE_BUY_STOP,Lot,0.0,bprice,stloss,tprofit,ORDER_TIME_GTC,0)) 
        {
         //--- Request is completed or order placed
         Alert("A BuyStop order has been successfully placed with Ticket#:",mytrade.ResultOrder(),"!!");
         return;
        }
      else
        {
         Alert("The BuyStop order request at vol:",mytrade.RequestVolume(), 
              ", sl:", mytrade.RequestSL(),", tp:",mytrade.RequestTP(),
              ", price:", mytrade.RequestPrice(), 
                " could not be completed -error:",mytrade.ResultRetcodeDescription());
         return;
        }
     }

下达 BUYSTOP 订单后,开盘价即为柱 1最高价 + 2pip + 点差。

记住:图表上显示的价格是卖价,而在下达多头/买入订单的过程中,您会需要买价。正因为如此,我们决定在柱 1 最高价的基础上加上点差;所以,现在我们得到的是相应的买价加 2pip 。止损价与获利价已于输入参数中定义。

准备好所有必要参数后,我们利用 CTrade 类函数 BuyStopOrderOpen 来下达订单。此处的订单类型为 ORDER_TYPE_BUY_STOP (买入止损订单)。我们使用与限价相同的价格,但其并非 BuyLimit 订单。我们还将订单有效时间设置为 ORDER_TIME_GTC (意味着订单被取消之前始终有效)。

如果您利用 ORDER_TIME_GTCORDER_TIME_DAY,则无需指定到期时间,所以我们将到期时间设置为 0。

   if(checkSell()==true)
     {
      Alert("Total Pending Orders now is :",CountOrders(),"!!");
      //--- any opened Sell position?
      if(Sell_opened)
        {
         Alert("We already have a Sell position!!!");
         return;    //--- Wait for a new bar
        }
      //--- Sell price = bar 1 Low - 2 pip 
      double sprice=mrate[1].low-10*_Point;
      double slprice=NormalizeDouble(sprice,_Digits);            //--- Sell price
      double ssloss=NormalizeDouble(sprice+STP*_Point,_Digits);   //--- Stop Loss
      double stprofit=NormalizeDouble(sprice-TKP*_Point,_Digits); //--- Take Profit
      //--- Open SellStop Order
      if(mytrade.SellStop(Lot,slprice,_Symbol,ssloss,stprofit))
      //if(mytrade.OrderOpen(_Symbol,ORDER_TYPE_SELL_STOP,Lot,0.0,slprice,ssloss,stprofit,ORDER_TIME_GTC,0)) 
        {
         //--- Request is completed or order placed
         Alert("A SellStop order has been successfully placed with Ticket#:",mytrade.ResultOrder(),"!!");
         return;
        }
      else
        {
         Alert("The SellStop order request at Vol:",mytrade.RequestVolume(), 
              ", sl:", mytrade.RequestSL(),", tp:",mytrade.RequestTP(), 
                ", price:", mytrade.RequestPrice(), 
                " could not be completed -error:",mytrade.ResultRetcodeDescription());
         return;
        }
     }

或者,我们也可以利用 OrderOpen 函数下达订单:

   if(checkSell()==true)
     {
      Alert("Total Pending Orders now is :",CountOrders(),"!!");
      //--- any opened Sell position?
      if(Sell_opened)
        {
         Alert("We already have a Sell position!!!");
         return;    //--- Wait for a new bar
        }
      //--- Sell price = bar 1 Low - 2 pip 
      double sprice=mrate[1].low-10*_Point;
      double slprice=NormalizeDouble(sprice,_Digits);            //--- Sell price
      double ssloss=NormalizeDouble(sprice+STP*_Point,_Digits);   //--- Stop Loss
      double stprofit=NormalizeDouble(sprice-TKP*_Point,_Digits); //--- Take Profit
      //--- Open SellStop Order
      //if(mytrade.SellStop(Lot,slprice,_Symbol,ssloss,stprofit))
      if(mytrade.OrderOpen(_Symbol,ORDER_TYPE_SELL_STOP,Lot,0.0,slprice,ssloss,stprofit,ORDER_TIME_GTC,0)) 
        {
         //--- Request is completed or order placed
         Alert("A SellStop order has been successfully placed with Ticket#:",mytrade.ResultOrder(),"!!");
         return;
        }
      else
        {
         Alert("The SellStop order request at Vol:",mytrade.RequestVolume(), 
                ", sl:", mytrade.RequestSL(),", tp:",mytrade.RequestTP(), 
                ", price:", mytrade.RequestPrice(), 
              " could not be completed -error:",mytrade.ResultRetcodeDescription());
         return;
        }
     }

就像 BuyStop 订单一样,开盘价为柱 1最低价 + 2 pip。这里我们无需加上点差,因为一般来讲,我们都需要卖价来下达空头/卖出订单。

我们还利用相同的 OrderOpen 函数或 SellStop 函数来下达 SellStop 订单。此处的订单类型为 ORDER_TYPE_SELL_STOP (卖出止损订单)。

下面即为我们简易“EA 交易”的结果。

 


图 8. 挂单 EA 的测试报告

 

图 9 - EA 图形报表

 

图 10 - EA 图表报告

 

2.3 获取订单/交易详情

我们会在本示例中阐明,如何在订单被触发后获取其相关详情。

在此阶段,它不再是一个挂单了,因为它已被触发且已转化为一个交易。

为了全面地理解这一流程,我们查看一下某个交易的日志详情:

订单处理流程

图 11. 订单处理流程

2.3.1 获取订单属性(记录)

//+------------------------------------------------------------------+
//|  Include ALL classes that will be used                           |
//+------------------------------------------------------------------+
//--- The Trade Class
#include <Trade\HistoryOrderInfo.mqh>
//+------------------------------------------------------------------+
//|  CREATE CLASS OBJECT                                             |
//+------------------------------------------------------------------+
//--- The HistoryOrderInfo Class Object
CHistoryOrderInfo myhistory;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Get all orders in History and get their details
   int buystop=0;
   int sellstop=0;
   int buylimit=0;
   int selllimit=0;
   int buystoplimit=0;
   int sellstoplimit=0;
   int buy=0;
   int sell=0;

   int s_started=0;
   int s_placed=0;
   int s_cancelled=0;
   int s_partial=0;
   int s_filled=0;
   int s_rejected=0;
   int s_expired=0;

   ulong o_ticket;
// Get all history records
   if(HistorySelect(0,TimeCurrent())) // get all history orders
     {
      // Get total orders in history
      for(int j=HistoryOrdersTotal(); j>0; j--)
        {
         // select order by ticket
         o_ticket=HistoryOrderGetTicket(j);
         if(o_ticket>0)
           {
            // Set order Ticket to work with
            myhistory.Ticket(o_ticket);
            Print("Order index ",j," Order Ticket is: ",myhistory.Ticket()," !");
            Print("Order index ",j," Order Setup Time is: ",TimeToString(myhistory.TimeSetup())," !");
            Print("Order index ",j," Order Open Price is: ",myhistory.PriceOpen()," !");
            Print("Order index ",j," Order Symbol is: ",myhistory.Symbol() ," !");
            Print("Order index ",j," Order Type is: ", myhistory.Type() ," !");
            Print("Order index ",j," Order Type Description is: ",myhistory.TypeDescription()," !");
            Print("Order index ",j," Order Magic is: ",myhistory.Magic()," !");
            Print("Order index ",j," Order Time Done is: ",myhistory.TimeDone()," !");
            Print("Order index ",j," Order Initial Volume is: ",myhistory.VolumeInitial()," !");
            //
            //
            if(myhistory.Type() == ORDER_TYPE_BUY_STOP) buystop++;
            if(myhistory.Type() == ORDER_TYPE_SELL_STOP) sellstop++;
            if(myhistory.Type() == ORDER_TYPE_BUY) buy++;
            if(myhistory.Type() == ORDER_TYPE_SELL) sell++;
            if(myhistory.Type() == ORDER_TYPE_BUY_LIMIT) buylimit++;
            if(myhistory.Type() == ORDER_TYPE_SELL_LIMIT) selllimit++;
            if(myhistory.Type() == ORDER_TYPE_BUY_STOP_LIMIT) buystoplimit++;
            if(myhistory.Type() == ORDER_TYPE_SELL_STOP_LIMIT) sellstoplimit++;

            if(myhistory.State() == ORDER_STATE_STARTED) s_started++;
            if(myhistory.State() == ORDER_STATE_PLACED) s_placed++;
            if(myhistory.State() == ORDER_STATE_CANCELED) s_cancelled++;
            if(myhistory.State() == ORDER_STATE_PARTIAL) s_partial++;
            if(myhistory.State() == ORDER_STATE_FILLED) s_filled++;
            if(myhistory.State() == ORDER_STATE_REJECTED) s_rejected++;
            if(myhistory.State() == ORDER_STATE_EXPIRED) s_expired++;
           }
        }
     }
// Print summary
   Print("Buy Stop Pending Orders : ",buystop);
   Print("Sell Stop Pending Orders: ",sellstop);
   Print("Buy Orders : ",buy);
   Print("Sell Orders: ",sell);
   Print("Total Orders in History is :",HistoryOrdersTotal()," !");
   
   Print("Orders type summary");
   Print("Market Buy Orders: ",buy);
   Print("Market Sell Orders: ",sell);
   Print("Pending Buy Stop: ",buystop);
   Print("Pending Sell Stop: ",sellstop);
   Print("Pending Buy Limit: ",buylimit);
   Print("Pending Sell Limit: ",selllimit);
   Print("Pending Buy Stop Limit: ",buystoplimit);
   Print("Pending Sell Stop Limit: ",sellstoplimit);
   Print("Total orders:",HistoryOrdersTotal()," !");

   Print("Orders state summary");
   Print("Checked, but not yet accepted by broker: ",s_started);
   Print("Accepted: ",s_placed);
   Print("Canceled by client: ",s_cancelled);
   Print("Partially executed: ",s_partial);
   Print("Fully executed: ",s_filled);
   Print("Rejected: ",s_rejected);
   Print("Expired: ",s_expired);
  }

这只是一个简单的脚本,旨在说明如何于历史记录中获取订单详情。我们纳入了 CHistoryOrderInfo 类,并创建了一个该类的对象。

现在我们就用这个对象来获取订单详情。

该历史订单脚本的结果 

 图 12. 历史订单脚本结果

2.3.2 获取交易属性(记录)

//+------------------------------------------------------------------+
//|  Include ALL classes that will be used                           |
//+------------------------------------------------------------------+
//--- The CDealInfo Class
#include <Trade\DealInfo.mqh>
//+------------------------------------------------------------------+
//|  Create class object                                             |
//+------------------------------------------------------------------+
//--- The CDealInfo Class Object
CDealInfo mydeal;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Get all deals in History and get their details
    int buy=0;
    int sell=0;
    int deal_in=0;
    int deal_out=0;
    ulong d_ticket;
    // Get all history records
    if (HistorySelect(0,TimeCurrent())) 
    {
      // Get total deals in history
      for (int j=HistoryDealsTotal(); j>0; j--)
      {
         // select deals by ticket
         if (d_ticket = HistoryDealGetTicket(j))
         {
          // Set Deal Ticket to work with
          mydeal.Ticket(d_ticket);
          Print("Deal index ", j ," Deal Ticket is: ", mydeal.Ticket() ," !");
          Print("Deal index ", j ," Deal Execution Time is: ", TimeToString(mydeal.Time()) ," !");
          Print("Deal index ", j ," Deal Price is: ", mydeal.Price() ," !");
          Print("Deal index ", j ," Deal Symbol is: ", mydeal.Symbol() ," !");
          Print("Deal index ", j ," Deal Type Description is: ", mydeal.TypeDescription() ," !");
          Print("Deal index ", j ," Deal Magic is: ", mydeal.Magic() ," !");
          Print("Deal index ", j ," Deal Time is: ", mydeal.Time() ," !");
          Print("Deal index ", j ," Deal Initial Volume is: ", mydeal.Volume() ," !");
          Print("Deal index ", j ," Deal Entry Type Description is: ", mydeal.EntryDescription() ," !");
          Print("Deal index ", j ," Deal Profit is: ", mydeal.Profit() ," !");
          //
          if (mydeal.Entry() == DEAL_ENTRY_IN) deal_in++;
          if (mydeal.Entry() == DEAL_ENTRY_OUT) deal_out++;
          if (mydeal.Type() == DEAL_TYPE_BUY) buy++;
          if (mydeal.Type() == DEAL_TYPE_SELL) sell++;
         }
      }
    }
    // Print Summary
    Print("Total Deals in History is :", HistoryDealsTotal(), " !");
    Print("Total Deal Entry IN is : ", deal_in);
    Print("Total Deal Entry OUT is: ", deal_out);
    Print("Total Buy Deal is : ", buy);
    Print("Total Sell Deal is: ", sell);
  }

这也是一个简单的脚本,旨在说明如何获取交易记录的详情。

该交易脚本的结果

图 13. 历史交易脚本结果


总结

在本文中,我们共同探讨了标准交易类库的主要函数,还对一些编写“EA 交易”会用到的部分功能做了演示说明,这些功能负责实施仓位修改、挂单下达与删除以及交易进行之前对预付款的验证。

我们还演示了可以如何使用它们来获取订单与交易详情。还有一些函数,我们在编写“EA 交易”的过程中并未用到。鉴于采用的交易策略,您将用到的函数可能比本例中用的多、也可能少。

建议您重温一下各个函数的描述部分,看看在编写自己的“EA 交易”时可以怎样利用它们。

标准类库旨在方便交易者与开发人员,所以您一定要用。