简介
距离文章上一个部分的发表已经过了一段时间,它是介绍 CStrategy 交易引擎的。这一次需要实现的是把 CStrategy 发展道路实现从一个小的辅助交易开发库转变为完整功能的复杂交易策略,包含最常用的工具以创建完整功能的交易策略。这一次也是帮助理解 CStrategy 未来开发的方法。这一次对 CStrategy 的实际使用也有助于发现在最新版本引擎中的一些缺点,改正这些缺点使"通用交易专家"系列的新文章得以问世。在当前的第八部分,我们将讨论通过面向对象的 CStrategy 类操作交易资产。
之前版本的 CStrategy 概览
EA交易的交易环境是不同的,它包含了账户信息,价格数据,用于操作时间的函数,以及有关交易终端中可用交易品种的信息。这些信息的大部分在交易品种相关函数中可以使用,例如取得当前的报价以及操作交易品种属性。原则上,所有的EA交易都会常常操作价格数据,它们使用最新的价格数据来计算模式或者交易信号,根据它们再来进行交易。为了正确生成交易订单,它们也需要使用当前交易品种的属性,例如交易的最小交易量或者冻结水平,也就是距离当前价格不能设置挂单的范围。 inside which pending orders cannot be placed.
这些数据应该可以简单访问到,并且一直"就在手边"。在前面版本的 CStrategy 中是这样的吗?让我们查阅历史来看一看。下面是早前版本的引擎如何操作交易品种的描述。在文章的第三部分中,我们讨论了使用传统的索引器[]来访问报价,在 CStrategy 中包含了一些辅助类, 例如 COpen, CHigh, CLow, CClose, CVolume, CTime。它们中的每一个都可以在请求的索引处返回对应的值,这样在EA交易的代码中就能够方便的道当前交易品种的信息。例如,当前柱的收盘价可以使用下面的简单代码获得:
...
double close = Close[0];
...
但是,以OHLC格式访问价格还是不够的,然后就又加上了另外的 Ask(), Bid(), Last() 方法。但是,还是需要更多的方法,例如 FreezeLevel(),来取得当前资产的基本信息。CStrategy 类的大小开始增长,CStrategy 内大量的方法开始变得容易混淆。当开始尝试创建交易多个交易品种的EA交易时就有了更多困难。CStrategy 是一个正式的多交易品种工具,它的意思是它可以用于创建多个单独交易不同交易品种的EA交易,也可以在一个EA交易中交易两个或者更多资产。但是后面的状况很难实现,因为它需要通过交换设置不同的工作交易品种类重新配置时间序列类:
string symbol1 = "EURUSD"; string symbol2 = "GBPUSD"; Close.Symbol(symbol1); double close_eurusd = Close[0]; Close.Symbol(symbol2); double close_gbpusd = Close[0]; ...
这些困难使我们得出结论,在工作交易品种中因为信息量太大,不能在 CStrategy 中直接实现它。CStrategy 类是在组织一系列交易行为中进行复杂工作的,任何额外的功能都可能破坏代码的可管理性,所以,操作交易品种的方法最好实现在独立的CSymbol类中。
首先了解WS对象和CSymbol类
现在,一个基于 CStrategy 的交易策略可以访问由 CSymbol 类创建的特定的WS对象,而不是访问单独的例如 CHigh 和 CLow 中的 Ask(), Bid() 和 Last()方法。这个类属于 CStrategy 开发库的一部分,包含了一些方法,和标准的СSymbolInfo类类似,但是类是不同的。除了操作交易品种属性,它也可以取得交易品种报价,包括限价订单(市场深度)的信息。WS 的对象名称是 "Working Symbol(操作的交易品种)"的简称,对象是在策略代码中的,使用短名称比较方便。我们经常需要访问各种不同的交易品种属性,而两个字符的简称可以使代码缩减并且更清楚。
在文章的前一部分提到,在控制权传到EA交易之前,CStrategy 交易引擎会进行一些内部环境对象的初始化工作,它保存所操作交易品种的名称和时段,并且创建类来跟踪新事件的到来(默认事件是新的订单分时和新的柱)。它也会启用记录并设置操作模式标志。另外,也会初始化 CSymbol 类的 WS 对象。其内部结构非常简单,它包含两个内部栏位: 交易品种和它的时段, 以及用来访问交易品种报价的特定对象。WS 对象是在 InitSeries 方法中初始化的,知道了EA交易所操作的交易品种和时段,我们就可以很容易地初始化它:
CStrategy::CStrategy(void)
{
WS.InitSeries(ExpertSymbol(), Timeframe());
}
当对象被初始化之后,您就可以用它来取得所需的交易品种属性了。例如,为了取得当前柱的最高价,就像下面这样写:
double max = WS.High[0];
WS 对象中还提供了一些额外的属性,使它使用更为方便,可以直接用来计算。让我们看一个常见情况: 您想要在前一个柱最高价的上方设置一个止损买入(BuyStop)订单,假设我们交易 EURUSD,并且想要在前一个柱最高价的上方三个五位小数点的距离设置一个止损挂单,我们需要写下以下的代码:
void CMovingAverage::InitBuy(const MarketEvent &event) { ... Trade.BuyStop(1.0, WS.High[1] + WS.StepToPrice(3), WS.Name()); ... }
这一行代码包含了很多操作:
- 取得前一个柱的极值 (WS.High[1]);
- 用三乘以一个点的数值以取得三个点所需的距离 (WS.StepToPrice(3));
- 把价格距离的结果加上最高价 (WS.High[1] + WS.StepToPrice(3));
- 发送一个使用以上结果数值为触发价格的止损买入挂单, 其交易资产就是当前交易品种 (WS.Name()).
StepToPrice 方法可能看起来和 MetaTrader 中采用的命名系统非常不同,在其它的交易平台中,价格步长是指最小的价格变化,它在 MetaTrader 中也就是 SYMBOL_TRADE_TICK_SIZE。这个名称可能很容易和分时大小或者 SYMBOL_TRADE_TICK_VALUE 的数值相混淆, 所以 CSymbol 为这个参数使用了不同的名称。不过,CSymbol 的大多数其他方法的名称还是和 MQL5系统方法一致的,尽管它们不是完全相同(例如 StepToPrice). CSymbol 的主要目标是提供简单而直观的方法来取得交易资产的完整信息。
CSymbol 类的结构方法的比较表格
在 MetaTrader 中, 交易品种含有大量的属性。首先,所有的属性可以根据条件分成整数型,实数型和字符串型数值。整数型属性包含了布尔(bool)型数值,系统方法的格式有枚举(enum), 日期时间 (datetime) 和整数型属性 (int 和 long). 实数属性包含了各种分数数值(double). 字符串型属性包含了返回字符串型值的属性,例如交易品种的名称,它的字符串型描述,等等。另外,交易品种还可能含有特别针对某些市场的属性。例如,对于 FORTS 交易品种,还有当前交易时间的另外属性,期权也有一些独特的属性。CSymbol 类在它内部另外的 SessionInfo 类中定义了当前 FORTS 交易时间的属性,其他属性没有根据它们的类型被分开,它们是以对应名称的形式存在的。
另外,CSymbol 类还包含了额外的集合以使得可以访问交易品种的报价。例如,公开定义的 COpen, CHigh, CLow, CClose, CVolume 是用于访问 OHLCV 序列的, 而市场深度可以使用特定的 CMarketWatch 类来访问。CMarketWatch 的详细描述在这篇文章中提供: "MQL5 酷客宝典: 实现您自己的市场深度"。除了这些方法和含有对应名称的索引类,例如 CClose 之外,CSymbol 类还包含一些方法,它们在 SymbolInfo 类中没有类似的对应。让我们更加详细地描述它们。
Available: 如果终端中含有给定名称的交易品种,这个方法就返回 true,如果没有找到这样的交易品种,就返回 false。
IndexByTime: 返回对应指定时间的柱的索引,例如,在以下的代码中,'index' 变量被赋值为1:
int index = WS.IndexByTime(WS.Time[1]); // index = 1;
如果我们知道时间,并且我们想要根据这个时间来取得柱的索引,这个方法很方便使用。假定交易中的EA必须在持有仓位 BarsHold 个柱之后平仓,实现这个功能的代码可能看起来如下:
//+------------------------------------------------------------------+ //| 根据移动平均管理买入仓位 | //+------------------------------------------------------------------+ void CImpulse::SupportBuy(const MarketEvent &event,CPosition *pos) { int bar_open = WS.IndexByTime(pos.TimeOpen()); if(bar_open >= BarsHold) pos.CloseAtMarket("根据持有时间出场"); }
StepToPrice 是以交易品种点数表示的价格变化的最小值。这个方法目的的描述在之前已经提供过。
CSymbol 方法的完整列表在下面的表格中提供。描述栏位包含了简要的方法描述,在大多数情况下,它与类似交易品种属性在对应文档部分的官方描述一致,有些方法提供了更加适合的描述。
返回值类型栏位显示了方法或者集合返回值的类型。
MQL5函数或者系统标识符栏位包含了对应的MQL5系统标识符或者用于类似目的函数的名称,如果指定了一个系统函数,在它的名称末尾会加上括号,例如 CopyOpen() 或者 MarketBookGet()。系统修改值是指当调用 SymbolInfoInteger, SymbolInfoDouble 或者 SymbolInfoString 函数时必须指定的三个修改值之一,修改值必须属于三个对应系统枚举其中的一个: ENUM_SYMBOL_INFO_INTEGER, ENUM_SYMBOL_INFO_DOUBLE 或者 ENUM_SYMBOL_INFO_STRING。例如,如果在"MQL5函数或者系统标识符"中指定了 SYMBOL_TRADE_STOPS_LEVEL 修改值,意思就是您应当调用 SymbolInfoInteger 来取得这个属性:
int stop_level = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL);
CSymbol方法名称栏位包含了返回对应属性的方法的名称,例如,为了取得收取三倍隔夜息的星期中的某天,应当调用下面的方法
ENUM_DAY_OF_WEEK day = WS.DayOfSwap3x();
这里是方法的表格:
描述 | 返回值类型 | MQL5 函数或系统标识符 | CSymbol 方法的名称 |
---|---|---|---|
访问交易品种的历史报价 | |||
取得在预先设置的交易品种时段中,指定索引柱的开盘价 | double | CopyOpen() | Open[] |
取得在预先设置的交易品种时段中,指定索引柱的最高价 | double | CopyHigh() | High[] |
取得在预先设置的交易品种时段中,指定索引住的最低价 | double | CopyLow() | Low[] |
取得在预先设置的交易品种时段中,指定索引柱的收盘价 | double | CopyClose() | Close[] |
取得指定索引柱的交易量 | double | CopyVolume() | Volume[] |
取得交易品种的市场深度属性,访问等级2的价格 | MqlBookInfo | MarketBookGet() | MarketBook |
整数型交易品种属性 | |||
交易品种在终端中存在的指示 | bool | 没有类似的 | Available |
该交易品种和时段中的柱数 | int | Bars() | BarsTotal |
交易品种的时段 | ENUM_TIMEFRAMES | Period() | Period |
交易品种是否在市场报价中被选中的指示 | bool | SYMBOL_SELECT | SelectInMarketWatch |
浮动点差的指示 | bool | SYMBOL_SPREAD_FLOAT | SpreadFloat |
点差的点数值 | int | SYMBOL_SPREAD | Spread |
当前收盘价和用于设置止损挂单的最小距离点数 | int | SYMBOL_TRADE_STOPS_LEVEL | StopLevel |
交易操作的冻结距离点数 | int | SYMBOL_TRADE_FREEZE_LEVEL | FreezeLevel |
是否允许订单过期模式的标志 | int | SYMBOL_EXPIRATION_MODE | FlagsExpirationOrders |
是否允许订单执行模式的标志 | int | SYMBOL_FILLING_MODE | FlagsExecutionOrders |
是否允许订单类型的标志 | int | SYMBOL_ORDER_MODE | FlagsAllowedOrders |
返回开启时间对应传入参数的柱的索引 | int | 没有类似的 | IndexByTime |
合约价格计算模式 | ENUM_SYMBOL_CALC_MODE | SYMBOL_TRADE_CALC_MODE | CalcContractType |
订单执行类型 | ENUM_SYMBOL_TRADE_MODE | SYMBOL_TRADE_MODE | ExecuteOrderType |
交易执行类型 | ENUM_SYMBOL_TRADE_EXECUTION | SYMBOL_TRADE_EXEMODE | ExecuteDealsType |
隔夜息计算模式 | ENUM_SYMBOL_SWAP_MODE | SYMBOL_SWAP_MODE | CalcSwapMode |
收取三倍隔夜息的那天 |
ENUM_DAY_OF_WEEK | SYMBOL_SWAP_ROLLOVER3DAYS | DayOfSwap3x |
期权类型 | ENUM_SYMBOL_OPTION_MODE | SYMBOL_OPTION_MODE | OptionType |
期权权益 (买入/卖出) | ENUM_SYMBOL_OPTION_RIGHT | SYMBOL_OPTION_RIGHT | OptionRight |
最新报价的时间 | datetime | SYMBOL_TIME | TimeOfLastQuote |
交易品种交易的起始日期(通常用于期货) | datetime | SYMBOL_START_TIME | StartDate |
交易品种交易截止日期(通常用于期货) | datetime | SYMBOL_EXPIRATION_TIME | ExpirationDate |
莫斯科交易所期货交易品种在当前交易日的属性 | |||
当前交易日的交易数量 | long | SYMBOL_SESSION_DEALS | SymbolInfo.DealsTotal |
此时总的买入订单数 | long | SYMBOL_SESSION_BUY_ORDERS | SymbolInfo.BuyOrdersTotal |
此时总的卖出订单数 | long | SYMBOL_SESSION_SELL_ORDERS | SymbolInfo.SellOrdersTotal |
当前交易日的最高交易量 | long | SYMBOL_VOLUMEHIGH | SymbolInfo.HighVolume |
当前交易日的最低交易量 | long | SYMBOL_VOLUMELOW | SymbolInfo.LowVolume |
当日的最高卖家报价 | double | SYMBOL_BIDHIGH | SymbolInfo.BidHigh |
当日的最高买家报价 | double | SYMBOL_ASKHIGH | SymbolInfo.AskHigh |
当日的最低卖家报价 | double | SYMBOL_BIDLOW | SymbolInfo.BidLow |
当日的最低买家报价 | double | SYMBOL_ASKLOW | SymbolInfo.AskLow |
前一日的最高价格 | double | SYMBOL_LASTHIGH | SymbolInfo.LastHigh |
前一日的最低价格 | double | SYMBOL_LASTLOW | SymbolInfo.LastLow |
当前交易日的交易总量 | double | SYMBOL_SESSION_VOLUME | SymbolInfo.VolumeTotal |
当前交易日的成交总量 | double | SYMBOL_SESSION_TURNOVER | SymbolInfo.TurnoverTotal |
开启仓位的总交易量 | double | SYMBOL_SESSION_INTEREST | SymbolInfo.OpenInterestTotal |
当前买入订单的总的交易量 | double | SYMBOL_SESSION_BUY_ORDERS_VOLUME | SymbolInfo.BuyOrdersVolume |
当前卖出订单的总的交易量 | double | SYMBOL_SESSION_SELL_ORDERS_VOLUME | SymbolInfo.SellOrdersVolume |
交易日的开盘价 | double | SYMBOL_SESSION_OPEN | SymbolInfo.PriceSessionOpen |
交易日的收盘价 | double | SYMBOL_SESSION_CLOSE | SymbolInfo.PriceSessionClose |
交易日的加权平均价格 | double | SYMBOL_SESSION_AW | SymbolInfo.PriceSessionAverage |
当前交易日的结算价格 | double | SYMBOL_SESSION_PRICE_SETTLEMENT | SymbolInfo.PriceSettlement |
交易日允许的最高价格 | double | SYMBOL_SESSION_PRICE_LIMIT_MAX | SymbolInfo.PriceLimitMax |
交易日允许的最低价格 | double | SYMBOL_SESSION_PRICE_LIMIT_MIN | SymbolInfo.PriceLimitMin |
实数型交易品种属性 | |||
可以买入的最佳卖家报价 | double | SYMBOL_ASK | Ask |
可以卖出的最佳买家报价 | double | SYMBOL_BID | Bid |
前一个交易执行的价格 | double | SYMBOL_LAST | Last |
最小价格变化值乘以价格步长数 | double | 没有类似的 | StepToPrice |
一个点(分时)的值 | double | SYMBOL_POINT | PriceStep |
以存款币别表示的一个点(分时)的值 | double | SYMBOL_TRADE_TICK_VALUE | TickValue |
期权执行价格 | double | SYMBOL_OPTION_STRIKE | OptionStrike |
交易合约大小 | double | SYMBOL_TRADE_CONTRACT_SIZE | ContractSize |
交易执行的最小交易量 | double | SYMBOL_VOLUME_MIN | VolumeContractMin |
交易执行的最大交易量 | double | SYMBOL_VOLUME_MAX | VolumeContractMax |
交易执行的最小变化交易量 | double | SYMBOL_VOLUME_STEP | VolumeContractStep |
对于这个交易品种所允许的在一个方向上的(买入或是卖出)开启仓位和挂单最大交易量。 | double | SYMBOL_VOLUME_LIMIT | VolumeContractLimit |
持有一个合约交易量的买入仓位所收取的隔夜息数值 | double | SYMBOL_SWAP_LONG | SwapLong |
持有一个合约交易量的卖出仓位所收取的隔夜息数值 | double | SYMBOL_SWAP_SHORT | SwapShort |
开启1手仓位所需的保证金 | double | SYMBOL_MARGIN_INITIAL | MarginInit |
维持1首仓位所需的保证金 | double | SYMBOL_MARGIN_MAINTENANCE | MarginMaintenance |
维持1手锁单仓位所需的保证金 | double | SYMBOL_MARGIN_HEDGED | MarginHedged |
字符串型交易品种属性 | |||
交易品种名称 | string | Symbol() | Name |
对应资产的交易品种名称 | string | SYMBOL_BASIS | NameBasisSymbol |
资产的基础币别 | string | SYMBOL_CURRENCY_BASE | NameBasisCurrency |
利润的币别 | string | SYMBOL_CURRENCY_PROFIT | NameCurrencyProfit |
保证金币别 | string | SYMBOL_CURRENCY_MARGIN | NameCurrencyMargin |
当前报价的来源 | string | SYMBOL_BANK | NameBank |
交易品种的字符串型描述 | string | SYMBOL_DESCRIPTION | 描述 |
交易品种在国际证券识别号码系统的名称 | string | SYMBOL_ISIN | NameISIN |
交易品种树中的路径 | string | SYMBOL_PATH | SymbolPath |
一次使用多个交易品种
CSymbol 是一个常规类,所以您可以在您的EA交易中没有数量限制地创建这个类的对象。WS 只是由 CStrategy 引擎创建的这样的对象,它代表了正在操作的交易品种和EA交易的时段。EA 也可以创建另外的对象来访问任意其他的交易品种。假定我们的EA在莫斯科交易所衍生市场上做交易,同时跟踪两个交易品种, Si 和 Brent,我们可以在 EA 代码中使用两个 CSymbol 对象,让我们称它们为 Si 和 Brent:
//+------------------------------------------------------------------+ //| EventListener.mqh | //| Copyright 2017, Vasiliy Sokolov, St-Petersburg, Russia | //| https://www.mql5.com/en/users/c-4 | //+------------------------------------------------------------------+ #property copyright "Copyright 2017, Vasiliy Sokolov." #property link "https://www.mql5.com/en/users/c-4" #include <Strategy\Strategy.mqh> //+------------------------------------------------------------------+ //| 一次操作两个交易品种的策略的模板 | //+------------------------------------------------------------------+ class CIntRate : public CStrategy { CSymbol Si; // 卢布-美元 CSymbol Brent; // 布伦特原油 public: virtual void OnEvent(const MarketEvent& event); virtual bool OnInit(); }; //+------------------------------------------------------------------+ //| 初始化卢布和原油交易品种 | //+------------------------------------------------------------------+ bool CIntRate::OnInit(void) { Si.InitSeries("Si Splice", Timeframe()); Brent.InitSeries("BR Splice", Timeframe()); return true; } //+------------------------------------------------------------------+ //| 以卢布表示的布伦特原油价格 | //+------------------------------------------------------------------+ void CIntRate::OnEvent(const MarketEvent &event) { double brent_in_rub = Brent.Last()*Si.Last()/Si.ContractSize(); } //+------------------------------------------------------------------+
该EA交易的代码取得最新的布伦特原油期货和卢布的价格,然后计算以卢布表示的布伦特原油价格公式。一个 Si 期货合约等于 $1000, 所以我们需要除以一个合约的大小,这是一个非常简单的操作,因为所有的交易品种属性都在单独一个类中可用,其他的代码页简单明了。主要的事情是在EA载入时的 OnInit 方法中不要忘记初始化 Si 和 Brent 对象。
使用 CSymbol 构建一个有趣的汇率设置
我们将要探讨的这个最新的使用 CSymbol 的实例会复杂一点,也更加有趣。众所周知,期货合约的交易和当前资产相比会有一些溢价,它的意思是商品未来的价格比当前价格要高一些。这种差别决定了市场对于某种商品或者资产的利率。让我们考虑以卢布/美元期货为例,在写这篇文章时,当前价格是 56.2875 卢布兑 1 美元, 而最近期 Si-6.17 期货合约是 56,682 卢布兑 1000$ 或者 56.682 rubles 兑 1 美元。所以当前价格和30天后(在2017年5月16日, Si-6.17的过期时间是30天)卢布的期货价格差距是 0.395 卢布或者 39.5 俄罗斯铜板。也就是说,市场期待卢布贬值 39.5 俄罗斯铜板, 也就是它价格的 0.7%。我们可以很容易地计算根据市场期待的12个月的通胀为 8.42%. 但是这是以最近的期货计算出来的通胀水平,如果我们使用 Si-9.17 而不是 Si-6.17, 通胀率将低一些,大约每年 7.8%。通过比较所有的 Si期货价格和当前的价格,我们可以获得利率设定。这个设定将以表格方式显示,展示了投资者根据时间的期望。例如,我们将知道后面 30, 100, 200, 300, 400 和 500 天的利率。
我们将需要灵活使用各种交易品种属性和交易品种列表,才能计算出所有这些数值。利率设定是如何计算的:
- 该EA交易可以在任何期货交易品种中载入,它会分析交易品种名称并载入所有相关期货。
- 每个载入的期货交易品种都表示为一个 CSymbol 对象,被放在交易品种列表中。
- 当新的分时到来之时,EA交易就会操作这个交易品种集合。它为每个交易品种找到现时的对应资产,
- 然后 EA 交易计算所选交易品种的价格和它当前价格的差距,这种差距可以转换为利率,然后可以转换为年利率。为此,要考虑计算期货合约剩余的时间,
- 结果上的差异显示在面板上,以表格行的形式显示。每一行显示为 "期货名称 — 过期前的天数 — 利率".
可以从描述中看到,算法事实上没有它可能看起来那么简单,但是,CStrategy 引擎和 CSymbol 对象都帮助在EA中大幅降低了计算的复杂性。下面的代码以EA交易的形式实现,尽管这个EA交易不会进行任何交易活动,相反,它会在面板上显示利率值。这里是结果代码:
//+------------------------------------------------------------------+ //| EventListener.mqh | //| Copyright 2017, Vasiliy Sokolov, St-Petersburg, Russia | //| https://www.mql5.com/en/users/c-4 | //+------------------------------------------------------------------+ #property copyright "Copyright 2017, Vasiliy Sokolov." #property link "https://www.mql5.com/en/users/c-4" #include <Strategy\Strategy.mqh> #include <Arrays\ArrayObj.mqh> #include "Panel.mqh" //+------------------------------------------------------------------+ //| 利率设置文件 | //+------------------------------------------------------------------+ class CIntRate : public CStrategy { CArrayObj Symbols; // 交易品种列表 CPercentPanel Panel; // 用于显示利率的面板 double BaseRate(CSymbol* fut); public: virtual void OnEvent(const MarketEvent& event); virtual bool OnInit(); }; //+-------------------------------------------------------------------+ //| 加上所需期货来计算利率设置文件 | //+-------------------------------------------------------------------+ bool CIntRate::OnInit(void) { string basis = WS.NameBasisSymbol(); for(int i = 0; i < SymbolsTotal(false); i++) { string name = SymbolName(i, false); int index = StringFind(name, basis, 0); if(index != 0) continue; CSymbol* Fut = new CSymbol(name, Timeframe()); if(Fut.ExpirationDate() == 0 || Fut.ExpirationDate() < TimeCurrent()) { delete Fut; continue; } string text = "增加了新的交易品种 " + Fut.Name() + " 到交易品种列表中"; CMessage* msg = new CMessage(MESSAGE_INFO, __FUNCTION__, text); Log.AddMessage(msg); Symbols.Add(Fut); } string text = "所有加上的交易品种 " + (string)Symbols.Total(); CMessage* msg = new CMessage(MESSAGE_INFO, __FUNCTION__, text); Log.AddMessage(msg); if(Symbols.Total() > 0) { Panel.Show(); } return true; } //+------------------------------------------------------------------+ //| 计算设置文件并在表格中显示它 | //+------------------------------------------------------------------+ void CIntRate::OnEvent(const MarketEvent &event) { double sec_one_day = 60*60*24; //86 400 for(int i = 0; i < Symbols.Total(); i++) { CSymbol* Fut = Symbols.At(i); double brate = BaseRate(Fut); double days = (Fut.ExpirationDate()-TimeCurrent())/sec_one_day; if(Fut.Last() == 0.0) continue; double per = (Fut.Last() - brate)/brate*100.0; double per_in_year = per/days*365; Panel.SetLine(i, Fut.NameBasisSymbol() + " " + DoubleToString(days, 0) + " Days:", DoubleToString(per_in_year, 2)+"%"); } } //+------------------------------------------------------------------+ //| 返回期货当前报价 | //+------------------------------------------------------------------+ double CIntRate::BaseRate(CSymbol* fut) { string name = fut.NameBasisSymbol(); if(StringFind(name, "Si", 0) == 0) return SymbolInfoDouble("USDRUB_TOD", SYMBOL_LAST)*fut.ContractSize(); return SymbolInfoDouble(name, SYMBOL_LAST)*fut.ContractSize(); } //+------------------------------------------------------------------+
基本功能实现在 OnInit 方法中,这个方法使用 WS.NameBasisSymbol() 来取得当前资产的名称,并检查所有交易品种来找到对应这个资产的期货。每个这样的期货交易品种都转换为一个 CSymbol 对象并驾到 CArrayObj 的交易品种列表中。在那之前,它会检查期货合约是否有效,有效期货合约的过期时间应该在将来。
交易品种集合中每个期货的利率计算都在 OnEvent 方法中,要计算过期前的天书以及期货和当前资产价格的差距。价格的差距转换为百分率,它一般与年收益对应。结果数值要写到面板的表格中 (使用 SetLine 方法)。
表格本身很简单,并且是基于和 CStrategy 面板类似的一系列图形类,EA图形组件的代码如下:
//+------------------------------------------------------------------+ //| Panel.mqh | //| Copyright 2017, Vasiliy Sokolov. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2017, Vasiliy Sokolov." #property link "https://www.mql5.com" #include <Panel\ElChart.mqh> class CPercentPanel : public CElChart { private: CArrayObj m_fields; CArrayObj m_values; public: CPercentPanel(void); void SetLine(int index, string field, string value); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CPercentPanel::CPercentPanel(void) : CElChart(OBJ_RECTANGLE_LABEL) { Width(200); Height(200); } //+------------------------------------------------------------------+ //| 设置一行 | //+------------------------------------------------------------------+ void CPercentPanel::SetLine(int index,string field,string value) { if(m_fields.Total() <= index) { CElChart* sfield = new CElChart(OBJ_LABEL); sfield.XCoord(XCoord()+10); sfield.YCoord(YCoord()+21*index+10); sfield.Text(field); m_fields.Add(sfield); m_elements.Add(sfield); CElChart* svalue = new CElChart(OBJ_LABEL); svalue.YCoord(YCoord()+21*index+10); svalue.XCoord(XCoord()+132); svalue.Text(value); svalue.TextColor(clrGreen); m_values.Add(svalue); m_elements.Add(svalue); if(IsShowed()) { sfield.Show(); svalue.Show(); } Height(m_fields.Total()*20 + m_fields.Total()*2 + 10); } else { CElChart* el = m_fields.At(index); el.Text(field); el = m_values.At(index); el.Text(value); } ChartRedraw(); }
当EA交易被编译好,并且在一个 Si 期货合约在图表上编译和展示之前,会触线下面的表格:
以表格显示卢布/美元利率图。
可以在表格中看到,利率在所有时间段是相等的,而按每年计算大约超过 7%。最近的期货合约显示稍微高些的利率。
重要提示: 在您在图表上载入EA交易之前,确认所有所需的期货合约报价和重新载入的是更可用的。否则,结果就是未定义的。
结论
我们已经回顾了在 CStrategy 交易引擎中包含的新的 CSymbol 类,这个类通过提供访问各种交易品种属性来简化资产的交易。CSymbol 帮我们创建了一个更有趣,比较复杂的利率设置文件指标,这是一个非常有代表性的例子。很多交易品种属性可以很容易地从 CSymbol 对象中获取,而计算就不是很复杂了。这个EA交易同时操作6个金融交易品种,而这还不会影响到代码的长度。CStrategy 是继承于 CObject的, 而实例可以很容易地驾到标准集合中,以使得数据处理更稳定和容易扩展。另外,有些特定的功能已经从 CStrategy 中转移到 CSymbol, 这使得 CStrategy 更加轻便和容易管理。