# 通用智能交易系统：支持挂单和对冲(第五章)

15 六月 2016, 15:16
0
7 085

### 简介

```double ask = SymbolInfoDouble(ExpertSymbol(), SYMBOL_ASK);
int digits = SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);

```

```//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
{
int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
}
//+------------------------------------------------------------------+
//| 返回Bid价格
//+------------------------------------------------------------------+
double CStrategy::Bid(void)
{
double bid = SymbolInfoDouble(ExpertSymbol(), SYMBOL_BID);
int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
bid = NormalizeDouble(bid, digits);
return bid;
}
//+------------------------------------------------------------------+
//| 返回Last价格
//+------------------------------------------------------------------+
double CStrategy::Last(void)
{
double last = SymbolInfoDouble(ExpertSymbol(), SYMBOL_LAST);
int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
last = NormalizeDouble(last, digits);
return last;
}

```

```//+------------------------------------------------------------------+
//| 返回运行标的的小数位数
//|
//+------------------------------------------------------------------+
int CStrategy::Digits(void)
{
int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
return digits;
}

```

### 支持带有对冲选项的帐户。

```//+------------------------------------------------------------------+
//| 重组持仓头寸列表
//+------------------------------------------------------------------+
void CStrategy::RebuildPositions(void)
{
ActivePositions.Clear();
for(int i = 0; i < PositionsTotal(); i++)
{
string symbol = PositionGetSymbol(i);
PositionSelect(symbol);
CPosition* pos = new CPosition();
}
}

```

```//+------------------------------------------------------------------+
//| 重组持仓头寸列表
//+------------------------------------------------------------------+
void CStrategy::RebuildPositions(void)
{
ActivePositions.Clear();
ENUM_ACCOUNT_MARGIN_MODE mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
if(mode!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
{
for(int i=0; i<PositionsTotal(); i++)
{
string symbol=PositionGetSymbol(i);
PositionSelect(symbol);
CPosition *pos=new CPosition();
}
}
else
{
for(int i=0; i<PositionsTotal(); i++)
{
ulong ticket=PositionGetTicket(i);
PositionSelectByTicket(ticket);
CPosition *pos=new CPosition();
}
}
}

```

```//+------------------------------------------------------------------+
//|                                                  PositionMT5.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include <Object.mqh>
#include "Logs.mqh"
#include "Trailing.mqh"
//+------------------------------------------------------------------+
//| 典型策略的持仓头寸类
//+------------------------------------------------------------------+
class CPosition : public CObject
{
...
};
...
//+------------------------------------------------------------------+
//| 返回当前头寸的止赢价格
//| 如果止损未设置，返回0.0
//+------------------------------------------------------------------+
double CPosition::StopLossValue(void)
{
if(!IsActive())
return 0.0;
return PositionGetDouble(POSITION_SL);
}
//+------------------------------------------------------------------+
//| 设置止赢价格
//+------------------------------------------------------------------+
bool CPosition::StopLossValue(double sl)
{
if(!IsActive())
return false;
}
//+------------------------------------------------------------------+
//| 返回当前头寸的止赢价格
//| 如果止损未设置，返回0.0
//+------------------------------------------------------------------+
double CPosition::TakeProfitValue(void)
{
if(!IsActive())
return 0.0;
return PositionGetDouble(POSITION_TP);
}
//+------------------------------------------------------------------+
//| 设置止赢价格
//+------------------------------------------------------------------+
bool CPosition::TakeProfitValue(double tp)
{
if(!IsActive())
return false;
}
//+------------------------------------------------------------------+
//| 按当前市价平仓并设置一个平仓备注
//| 'comment'
//+------------------------------------------------------------------+
bool CPosition::CloseAtMarket(string comment="")
{
if(!IsActive())
return false;
ENUM_ACCOUNT_MARGIN_MODE mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
}
//+------------------------------------------------------------------+
//| 返回当前头寸大小
//+------------------------------------------------------------------+
double CPosition::Volume(void)
{
if(!IsActive())
return 0.0;
return PositionGetDouble(POSITION_VOLUME);
}
//+------------------------------------------------------------------+
//| 返回以存款货币计算的当前持仓头寸的获利
//+------------------------------------------------------------------+
double CPosition::Profit(void)
{
if(!IsActive())
return 0.0;
return PositionGetDouble(POSITION_PROFIT);
}
//+------------------------------------------------------------------+
//| 返回true如果头寸被激活。否则返回false
//||
//+------------------------------------------------------------------+
bool CPosition::IsActive(void)
{
ENUM_ACCOUNT_MARGIN_MODE mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
if(mode!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
return PositionSelect(m_symbol);
else
return PositionSelectByTicket(m_id);
}
//+------------------------------------------------------------------+

```

### 在先前的CStrategy版本中使用挂单

```//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
{
if(!IsTrackEvents(event))return;                      // 仅处理所需事件
if(positions.open_buy > 0) return;                    // 如果已经存在至少一个未平仓的买单，无需再买入
for(int i = OrdersTotal()-1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(!OrderSelect(ticket))continue;
ulong magic = OrderGetInteger(ORDER_MAGIC);
if(magic != ExpertMagic())continue;
string symbol = OrderGetString(ORDER_SYMBOL);
if(symbol != ExpertSymbol())continue;
ENUM_ORDER_TYPE order_type = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
{
}
}
}

```

### 用于执行挂单的CPendingOrders和COrdersEnvironment。

• 独立于平台的。既然所有的类和方法都是用正式的MQL语言编写的，就可以通过一种通用的方法来获取任何属性。然而，这个方法的实际实现将随着平台的不同而不同。但这对用户层面来说无所谓。这意味着在一个平台上开发的EA理论上可以编译后在另一个平台上运行。但是在实际使用中会遇到各种问题，我们不打算在这篇文章中讨论平台独立性。
• 功能。MQL函数集提供了基本功能，通过组合你可以创建复杂的算法和有用的函数。当这些算法被包含到一个独立的类库中，如CStartegy中后就能更为方便的使用它们了。额外的新函数不会增加其操作的复杂度，因为在这种情况下仅需添加新的模块，这些模块对于EA的开发者来说可以选择用或者不用。

```//+------------------------------------------------------------------+
//| 用于操作挂单的一个类
//+------------------------------------------------------------------+
class COrdersEnvironment
{
private:
CDictionary    m_orders;         // 挂单的总数
public:
COrdersEnvironment(void);
int            Total(void);
CPendingOrder* GetOrder(int index);
};
//+------------------------------------------------------------------+
//| 我们需要知道当前货币对及EA的编号
//+------------------------------------------------------------------+
COrdersEnvironment::COrdersEnvironment(void)
{
}
//+------------------------------------------------------------------+
//| 返回一个挂单
//+------------------------------------------------------------------+
CPendingOrder* COrdersEnvironment::GetOrder(int index)
{
ulong ticket = OrderGetTicket(index);
if(ticket == 0)
return NULL;
if(!m_orders.ContainsKey(ticket))
return m_orders.GetObjectByKey(ticket);
if(OrderSelect(ticket))
return NULL;
CPendingOrder* order = new CPendingOrder(ticket);
return order;
}
//+------------------------------------------------------------------+
//| 返回挂单的数量
//+------------------------------------------------------------------+
int COrdersEnvironment::Total(void)
{
return OrdersTotal();
}

```

Total方法返回当前系统中存在的挂单的总数。方法不会出错，因为它返回的是从OrdersTotal()函数中接收到的系统值。

### EA代码中关于挂单的部分

```//+------------------------------------------------------------------+
//| 当快速MA在慢速MA之下卖出
//+------------------------------------------------------------------+
{
if(!IsTrackEvents(event))return;                      // 仅处理所需事件
if(positions.open_buy > 0) return;                    // 如果已经存在至少一个未平仓的买单，无需再买入
for(int i = PendingOrders.Total()-1; i >= 0; i--)
{
CPendingOrder* Order = PendingOrders.GetOrder(i);
if(Order == NULL || !Order.IsMain(ExpertSymbol(), ExpertMagic()))
continue;
{
}
//删除订单；没有必要删除订单对象！
}
}

```

PendingsOrders对象是一个COrdersEnvironment类。使用系统函数搜索挂单的方式也一样。然后根据挂单索引i变量，尝试获取订单对象。如果订单由于某些原因订单没有被获取到，或者订单属于另一个EA，则继续查找新的订单。在这种情况下使用CPendingorder对象的IsMain方法，如果订单的u哦比对和编号和EA的货币对和编号一致则返回true，这就意味着此订单是属于这个EA的。

### CImpulse策略类

```//+------------------------------------------------------------------+
//|                                                      Impulse.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include <Strategy\Strategy.mqh>
#include <Strategy\Indicators\MovingAverage.mqh>

input double StopPercent = 0.05;
//+------------------------------------------------------------------+
//| 定义挂单相关操作
//|
//+------------------------------------------------------------------+
{
};
//+------------------------------------------------------------------+
//| CImpulse策略
//+------------------------------------------------------------------+
class CImpulse : public CStrategy
{
private:
double            m_percent;        // 挂单位置的百分比值
bool              IsTrackEvents(const MarketEvent &event);
protected:
virtual void      InitSell(const MarketEvent &event);
virtual void      SupportBuy(const MarketEvent &event,CPosition *pos);
virtual void      SupportSell(const MarketEvent &event,CPosition *pos);
virtual void      OnSymbolChanged(string new_symbol);
virtual void      OnTimeframeChanged(ENUM_TIMEFRAMES new_tf);
public:
double            GetPercent(void);
void              SetPercent(double percent);
CIndMovingAverage Moving;
};
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
{
if(!IsTrackEvents(event))return;
if(target < Moving.OutValue(0))                    // 订单的激活价格必须高于移动平均价
else
for(int i = PendingOrders.Total()-1; i >= 0; i--)
{
CPendingOrder* Order = PendingOrders.GetOrder(i);
if(Order == NULL || !Order.IsMain(ExpertSymbol(), ExpertMagic()))
continue;
{
{
Order.Modify(target);
}
else
Order.Delete();
}
}
}
//+------------------------------------------------------------------+
//| 建立一个SellStop空头挂单
//|
//+------------------------------------------------------------------+
void CImpulse::InitSell(const MarketEvent &event)
{
if(!IsTrackEvents(event))return;
if(positions.open_sell > 0) return;
int sell_stop_total = 0;
double target = Bid() - Bid()*(m_percent/100.0);
if(target > Moving.OutValue(0))                    // 订单的激活价格必须低于移动平均价
else
for(int i = PendingOrders.Total()-1; i >= 0; i--)
{
CPendingOrder* Order = PendingOrders.GetOrder(i);
if(Order == NULL || !Order.IsMain(ExpertSymbol(), ExpertMagic()))
continue;
if(Order.Type() == ORDER_TYPE_SELL_STOP)
{
{
sell_stop_total++;
Order.Modify(target);
}
else
Order.Delete();
}
}
Trade.SellStop(MM.GetLotFixed(), target, ExpertSymbol(), 0, 0, NULL);
}
//+------------------------------------------------------------------+
//| 根据移动平均来管理一个多头头寸
//+------------------------------------------------------------------+
{
if(!IsTrackEvents(event))return;
if(Bid() < Moving.OutValue(0))
pos.CloseAtMarket();
}
//+------------------------------------------------------------------+
//| 根据移动平均来管理一个空头头寸
//+------------------------------------------------------------------+
void CImpulse::SupportSell(const MarketEvent &event,CPosition *pos)
{
if(!IsTrackEvents(event))return;
pos.CloseAtMarket();
}
//+------------------------------------------------------------------+
//| 过滤到来的事件如果传入的事件没被
//| 策略处理，返回false；如果已处理，
//| 返回 true
//+------------------------------------------------------------------+
bool CImpulse::IsTrackEvents(const MarketEvent &event)
{
//我们仅在当前运行货币对、当前时间框架下新柱形开始时做处理
if(event.type != MARKET_EVENT_BAR_OPEN)return false;
if(event.period != Timeframe())return false;
if(event.symbol != ExpertSymbol())return false;
return true;
}
//+------------------------------------------------------------------+
//| 响应货币对变化
//+------------------------------------------------------------------+
void CImpulse::OnSymbolChanged(string new_symbol)
{
Moving.Symbol(new_symbol);
}
//+------------------------------------------------------------------+
//| 响应时间框架变化
//+------------------------------------------------------------------+
void CImpulse::OnTimeframeChanged(ENUM_TIMEFRAMES new_tf)
{
Moving.Timeframe(new_tf);
}
//+------------------------------------------------------------------+
//| 返回突破位置的百分比值
//+------------------------------------------------------------------+
double CImpulse::GetPercent(void)
{
return m_percent;
}
//+------------------------------------------------------------------+
//| 设置突破位置的百分比值
//+------------------------------------------------------------------+
void CImpulse::SetPercent(double percent)
{
m_percent = percent;
}

```

```//+------------------------------------------------------------------+
//|                                                ImpulseExpert.mq5 |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property version   "1.00"
#include <Strategy\StrategiesList.mqh>
#include <Strategy\Samples\Impulse.mqh>

CStrategyList Manager;
//+------------------------------------------------------------------+
//| EA初始化函数
//+------------------------------------------------------------------+
int OnInit()
{
//---
CImpulse* impulse = new CImpulse();
impulse.ExpertMagic(1218);
impulse.Timeframe(Period());
impulse.ExpertSymbol(Symbol());
impulse.ExpertName("Impulse");
impulse.Moving.MaPeriod(28);
impulse.SetPercent(StopPercent);
delete impulse;
//---
return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//|  EA的tick函数
//+------------------------------------------------------------------+
void OnTick()
{
//---
Manager.OnTick();
}
//+------------------------------------------------------------------+

```

### 分析CImpulse策略

```//+------------------------------------------------------------------+
//| 根据移动平均来管理一个多头头寸
//+------------------------------------------------------------------+
{
if(!IsTrackEvents(event))return;
ENUM_ACCOUNT_MARGIN_MODE mode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
{
double target = Bid() - Bid()*(m_percent/100.0);
if(target < Moving.OutValue(0))
pos.StopLossValue(target);
else
pos.StopLossValue(0.0);
}
if(Bid() < Moving.OutValue(0))
pos.CloseAtMarket();
}
//+------------------------------------------------------------------+
//| 根据移动平均来管理一个空头头寸
//+------------------------------------------------------------------+
void CImpulse::SupportSell(const MarketEvent &event,CPosition *pos)
{
if(!IsTrackEvents(event))return;
ENUM_ACCOUNT_MARGIN_MODE mode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
{
if(target > Moving.OutValue(0))
pos.StopLossValue(target);
else
pos.StopLossValue(0.0);
}
pos.CloseAtMarket();
}

```