Exchange symbols, broker Otkritie, version 5.00 build 2190.
对不起,第 23 部分有继续讨论的内容,但我赶时间错过了。总之,我下载了第 25 版,问题依然存在。在第 23 部分的讨论中,您写道有必要在 onInit'e 的某些行之后插入添加行,但您用绿色标出的这些行在第 23 版和第 25 版的 onInit'e 中都没有:
prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
for(int i=0;i<TOTAL_BUTT;i++)
{
butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
}
lot=NormaliseLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
magic_number=InpMagic;
stoploss=InpStopLoss;
takeprofit=InpTakeProfit;
distance_pending=InpDistance;
distance_stoplimit=InpDistanceSL;
滑点=InpSlippage;
trailing_stop=InpTrailingStop*Point();
trailing_step=InpTrailingStep*Point();
trailing_start=InpTrailingStart;
stoploss_to_modify=InpStopLossModify;
takeprofit_to_modify=InpTakeProfitModify;
//--- 初始化 DoEasy 库
OnInitDoEasy();
//--- 检查并删除未发布的智能交易系统图形对象
if(IsPresentObects(prefix))
ObjectsDeleteAll(0,prefix);
//--- 创建按钮栏
if(!CreateButtons(InpButtShiftX,InpButtShiftY))
return INIT_FAILED;
//--- 设置尾部激活按钮的状态
ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);
//tr.SetCorrectTypeExpiration();
引擎.TradingSetCorrectTypeExpiration();
引擎.TradingSetCorrectTypeFilling();
//--- 通过宏替换检查标准声音的播放,通过描述检查自定义声音的播放
Engine.PlaySoundByDescription(SND_OK);
Sleep(600);
Engine.PlaySoundByDescription(TextByLanguage("The sound of a falling coin 2", "The sound of a falling coin 2"));
//---
return(INIT_SUCCEEDED);
Exchange symbols, broker Otkritie, version 5.00 build 2190.
对不起,第 23 部分有继续讨论的内容,但我赶时间错过了。总之,我下载了第 25 版,问题依然存在。在第 23 部分的讨论中,您写道有必要在 onInit'e 的某些行之后插入附加行,但您用绿色标出的这些行在第 23 版和第 25 版的 onInit'e 中都没有:
您使用的是第 25 条中的测试 EA - "TestDoEasyPart25.mq5 "吗?
现在,我在 23 版中插入了 onInit,并添加了我插入的函数,但我使用的 25 版也没有您在 23 版中用绿色标出的行,而且 25 版的运行效果不如 23 版。
我不明白你说的是哪几行绿线。
请不要使用其他文章中的测试 EA - 每篇文章都有自己的测试 EA - 您应该使用它,并根据正确的测试 EA 报告您发现的任何错误,而不是其他文章中的错误。
对不起,与上一篇文章有关:
我下载了最新版本的 库和 Expert Part_25,在测试仪上设置了 可视模式, 市场订单已打开,但所有挂单均未打开,日志显示 2019.11.21 12:59:20.689 2019.11.18 10:00:02 失败的卖出止损限价 2.00 Si-12.19 at 63972 (64022) sl: 64072 tp: 63972 [Invalid expiration]and 2019.11.21 12:59:20.689 2019.11.18 10:00:02 Trade attempt #3.错误:请求中的订单到期 日期无效
好吧,对不起,关于上一篇文章:
订单已打开,但所有挂单均未打开,日志显示 2019.11.21 12:59:20.689 2019.11.18 10:00:02 失败的卖出止损限价 2.00 Si-12.19 at 63972 (64022) sl: 64072 tp: 63972 [Invalid expiration]and 2019.11.21 12:59:20.689 2019.11.18 10:00:02 Trade attempt #3.错误:请求中的订单到期 日期无效
好的,谢谢,我会检查的。
能否附上一张截图,说明出现错误的符号?
文件中 Si-12.19 规范的屏幕。
在这一版本和下 一版本的程序库中,我漏掉了对订单成交类型和到期类型的检查。在下一篇(27)文章中将进行更正。
目前,我建议在发送交易请求时,应明确指定到期类型。例如,要下限价卖出挂单,需要在测试智能交易系统中添加该 挂单:
//--- 如果按下 BUTT_SELL_LIMIT 按钮: 设置卖出上限 else if(button==EnumToString(BUTT_SELL_LIMIT)) { //--- 设置限价卖出订单 engine.PlaceSellLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("待沽限额","Pending order SellLimit"),0,ORDER_TIME_DAY); }
并在所有包含设置挂单 方法调用的行中执行相同操作。
Artem,感谢你所做的工作,非常周到。
老实说,我不太理解复合魔法数字的意义....。
假设我要将一个 EA 中的交易分成几组,每组有不同的神奇数字。
在经典方法中,我随后会查看订单并比较神奇数字,然后将结果分配给各组。
在库中,我认为这种情况不会有太大变化--只是这些数字的分配可以以某种方式系统化?还是会有其他影响?
还有一层特殊的功能是我在你们的资料库中完全没有的:汇总。
很多时候,我们需要回答这样一个问题:一组参数(magik、符号、订单类型)的总利润是多少?例如,总手数是多少?盈亏平衡水平。还有交易次数。
到处都需要重复使用这些总值,在每种新方法中通过样本计算这些总值显然是矫枉过正。
如果能有一组聚合值,为每个聚合值配置一组取样标准(符号、类型、magik、组号),并在 CEngine 中提供对这些聚合值的访问权限--并在刷新中更新一次,那就再好不过了。
如果您没有这种改进计划,我可以最终确定并分享.....但只有我们的代码风格明显不同,这块内容才会显得格格不入。
Artem,感谢你所做的工作,非常周到。
说实话,我不太明白复合魔法的意义,....。
假设我要将智能交易系统(Expert Advisor)中的交易分成几组,每组有不同的神奇数字。
按照传统方法,我随后会查看订单并比较神奇数字,然后将结果分配给各组。
在库中,我认为这种情况不会有太大变化--只是这些数字的分配可以以某种方式系统化?还是说它们会影响到其他什么?
还有一层功能是我在你们的图书馆中完全没有的:汇总。
很多时候,我们需要回答这样一个问题:一组参数(magik、符号、订单类型)的总利润是多少?例如,总手数是多少?盈亏平衡水平。以及交易次数。
这些汇总值到处都需要,而且需要重复计算,在每种新方法中通过样本计算显然是矫枉过正。
如果能有一组聚合值,为每个聚合值配置一组取样标准(符号、类型、magik、组号),并在 CEngine 中提供对这些聚合值的访问权限,并在刷新中更新一次,那就更好了。
如果您没有这种改进计划,我可以最终确定并分享.....但只有我们的代码风格明显不同,这块内容才会显得格格不入。
关于魔力值中记录的信息:
您可以为每个组使用不同的魔力值来创建不同的组。例如,如果顾问的魔力值是 123,那么第一组的魔力值就是 124,第二组的魔力值就是 125,第三组的魔力值就是 126,以此类推。
程序库提供了另一种创建不同组的方法--每个子组的编号直接存储在魔力值中。然后,EA 的神奇数字也是一个组标识符,但它被放在一个名为 MagicID 的独立组中,即 EA 的神奇数字标识符。还有两个组。每个组都有 15 个子组。每个子组都有自己的标识符。
这样,在使用组时就有了更大的 灵活性。
例如:我们想将一格挂单移动到价格后面--我们将它们添加到子组 1 的组 1 中。组 1 移动到价格后面。子组 1 沿 MA 移动。现在,我们希望通过抛物线 SAR 将其中一些订单移动到价格(第 1 组)后面。我们将它们归入子组 2。然后,第 1 组跟随价格移动,但第 1 子组根据 MA 移动,而第 2 子组根据抛物线 SAR 移动。
订单被触发,转为头寸--您可以设置自己的组来修改止损,并在该组中设置自己的子组,以修改不同的值。修改算法在子组中编写。
总的来说,这是一种花式操作。您也可以使用简单的魔术,但必须自己想出跟踪不同组的逻辑。
关于第二个问题:
有一个 CSelect 类。该类可从程序中获取,并提供了从所有现有集合(账户、事件、订单、符号)中选择和搜索的方法,您可以编写这些方法。
您可以根据所有条件在列表中选择每个集合的对象。在创建的列表中,您可以通过细化标准进行重新选择,也可以查找选择标准的最大值和最小值。
不过,还将有更多的用户功能(稍后)可以快速方便地访问所有集合的所有属性并在其中进行搜索。
但现在--只能通过 CSelect 在需要时使用。该类是静态的,因此可以通过"::: "访问其方法。例如,CSelect::ByOrderProperty()。
是的,顺便说一下,在测试 EA 中就有一个在程序中使用该类的示例--例如,在其尾部函数中:
//+------------------------------------------------------------------+ // 清除最远的挂单 //+------------------------------------------------------------------+ void TrailingOrders(void) { MqlTick tick; if(!SymbolInfoTick(Symbol(),tick)) return; double stop_level=StopLevel(Symbol(),2)*Point(); //--- 获取所有已设定订单的列表 CArrayObj* list=engine.GetListMarketPendings(); //--- 仅从列表中选择当前符号的订单 list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); //--- 仅从列表中选择买入方向的订单 CArrayObj* list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_BUY,EQUAL); //--- 按与价格的距离(以点为单位)排序(以利润为单位)。 list_buy.Sort(SORT_BY_ORDER_PROFIT_PT); //--- 获取买入方向上距离最大的订单索引 int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_PT); if(index_buy>WRONG_VALUE) { COrder* buy=list_buy.At(index_buy); if(buy!=NULL) { //--- 如果订单设置低于价格(BuyLimit),并且需要将其 "提升 "到价格之后 if(buy.TypeOrder()==ORDER_TYPE_BUY_LIMIT) { //--- 根据新价格计算新订单设置价格和止损水平 double price=NormalizeDouble(tick.ask-trailing_stop,Digits()); double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0); double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0); //--- 如果计算出的价格低于止损距离,则从卖出订单设置价格开始延迟(观察止损距离)。 if(price<tick.ask-stop_level) { //--- 如果计算价格高于从订单设置价格推迟的拖网步骤 - 修改订单设置价格 if(price>buy.PriceOpen()+trailing_step) { engine.ModifyOrder((ulong)buy.Ticket(),price,sl,tp,-1); } } } //--- 如果订单设置高于价格(BuyStop 和 BuyStopLimit),并且应该 "降低 "到价格之后 else { //--- 根据新价格计算新订单设置价格和止损水平 double price=NormalizeDouble(tick.ask+trailing_stop,Digits()); double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0); double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0); //--- 如果计算出的价格高于止损距离,则从卖出订单设置价格开始延迟(观察止损距离)。 if(price>tick.ask+stop_level) { //--- 如果计算价格低于从订单设置价格推迟的拖网步骤 - 修改订单设置价格 if(price<buy.PriceOpen()-trailing_step) { engine.ModifyOrder((ulong)buy.Ticket(),price,sl,tp,-1); } } } } } //--- 仅从列表中选择卖出方向的订单 CArrayObj* list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_SELL,EQUAL); //--- 按与价格的距离(以点为单位)排序(以利润为单位)。 list_sell.Sort(SORT_BY_ORDER_PROFIT_PT); //--- 获取塞尔方向上距离最大的订单的索引 int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_PT); if(index_sell>WRONG_VALUE) { COrder* sell=list_sell.At(index_sell); if(sell!=NULL) { //--- 如果订单设置高于价格(SellLimit),则应将其 "降低 "到价格之后 if(sell.TypeOrder()==ORDER_TYPE_SELL_LIMIT) { //--- 根据新价格计算新订单设置价格和止损水平 double price=NormalizeDouble(tick.bid+trailing_stop,Digits()); double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0); double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0); //--- 如果计算出的价格高于止损距离,则从出价订单设置价格开始延迟(观察止损距离)。 if(price>tick.bid+stop_level) { //--- 如果计算价格低于从订单设置价格推迟的拖网步骤 - 修改订单设置价格 if(price<sell.PriceOpen()-trailing_step) { engine.ModifyOrder((ulong)sell.Ticket(),price,sl,tp,-1); } } } //--- 如果订单设置低于价格(SellStop 和 SellStopLimit),则应将其 "移至 "价格之后。 else { //--- 根据新价格计算新订单设置价格和止损水平 double price=NormalizeDouble(tick.bid-trailing_stop,Digits()); double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0); double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0); //--- 如果计算出的价格低于止损距离,则从出价订单设置价格开始推迟(观察止损距离)。 if(price<tick.bid-stop_level) { //--- 如果计算价格高于从订单设置价格推迟的拖网步骤 - 修改订单设置价格 if(price>sell.PriceOpen()+trailing_step) { engine.ModifyOrder((ulong)sell.Ticket(),price,sl,tp,-1); } } } } } } //+------------------------------------------------------------------+
新文章 轻松快捷开发 MetaTrader 程序的函数库(第 二十五部分):处理交易服务器返回的错误已发布:
交易订单发送到服务器之后,我们需要检查错误代码,或未出现错误。 在本文中,我们将研究处理交易服务器返回的错误,并着手创建延后交易请求。
在最新的 MetaTrader 5 版本中(自 build 2201 开始),测试器提供了在测试执行期间为品种设置参数的能力。 因此,可以在品种上设置交易限制,并在检测到品种限制时测试函数库行为。
若要调用品种设置窗口,单击测试时间帧选择右侧的按钮:
一个品种只允许开立多头仓位,并将同时开仓和同向挂单的交易量限制为 0.5。
因此,我们将只能使用多头仓位,且最大的多头持仓和订单总交易量不超过 0.5 手。 换言之,当多头开仓 0.1 手时,我们只能开五笔,或四笔持仓加一笔挂单:
为了提高真实性,我们可以禁用超过指定利润时的自动平仓。 不过,我们看到我们无法开立空头持仓,并会收到警告,该品种只允许开立多头仓位。 进而,当尝试开立总交易量超过 0.5 手的多笔持仓时,我们会收到消息:由于超出一个方向的持仓和订单总交易量而无法开仓。
作者:Artyom Trishkin