English Русский Español Deutsch 日本語 Português
preview
市场模拟(第一部分):跨期订单(一)

市场模拟(第一部分):跨期订单(一)

MetaTrader 5测试者 |
220 0
Daniel Jose
Daniel Jose

概述

在上一篇文章“开发回放系统(第 78 部分):新 Chart Trade(五) ,我展示了 EA 交易如何解释 Chart Trade 发送的指令。Chart Trade 实际传输给 EA 交易的信息取决于用户与它的交互。换句话说,当用户点击买入、卖出或平仓按钮时,就会向图表发送一条消息。当附加到此图表时,EA 交易的任务之一是拦截、解码和执行该消息中包含的指令。

虽然这种机制简单可靠,但我们面临一个小问题。嗯,这并不是一个问题,更多的是一种不便。在我们真正开始向交易服务器发送订单之前,需要解决这个不便之处。

如果你不熟悉我的意思,可能是因为你不交易某些资产,更具体地说,期货合约。这些类型的资产都有到期日。通常,两种类型的合约会同时交易:一种是交易量更大的完整合约,另一种是可视为完整合约一小部分的迷你合约。迷你合约允许采用需要较小交易量或较少合约的策略。

我不会在这里详细讨论这些策略。关键在于,有时候,为了制定策略,你需要更少的合约。对此感兴趣的人应该研究一下对冲策略。对于我们程序员来说,真正关心的是当图表显示完整合约时如何执行迷你合约中的交易。

但还有另一个挑战:长期策略。每次合约到期(在固定的、众所周知的日期),就会开始新的系列。对于从事长期限工作、跨越多个系列的交易者来说,这是一个重大问题。这是因为指标和移动平均线必须在每个新系列中重新开始计算。

为了更好地理解这一点,让我们来看看B3(巴西证券交易所)美元期货合约,该合约每月到期。这意味着每个月都会结束一个系列,然后开始一个新的系列。考虑到每个月大约有 20 个交易日(4 周内每周有 5 个交易日),在尝试使用 20 个周期的移动平均线时会出现问题。当移动平均线完全计算和绘制时,合约到期并重置为新的序列。而这只是 20 周期的平均值。其他指标需要更长的时间,受到的影响更大。总之,这是一个大问题。

为了克服这一点,我们可以使用期货合约的历史数据。然而,使用历史数据并不能解决所有问题。它确实给我们程序员带来了新的挑战。请记住,交易者并不关心服务器如何接收或绘制数据,交易者只希望获得准确的信息和可靠的交易执行。作为程序员,我们有责任解决绘图问题,并确保图表中的交易请求正确路由到交易服务器。

在文章从零开始开发 EA 交易(第 11 部分):跨期订单系统,我对此做了一些细节解释。但在这里,由于我们还要处理回放/模拟器,问题就变得更加大了。不过,我们可以运用拿破仑的策略:分而治之。通过将问题分解为更小的部分,我们可以逐步开发一个模拟订单系统。我们将从一个关键方面开始:处理期货合约中的指标。由于美元期货合约是我所知道的最极端的情况,我们将重点关注它。然而,请记住,同样的原则也适用于具有类似复杂性的其他合约。


开始实现

在前面提到的文章中,我们开发了跨期订单系统,但要使其覆盖其他类型的合约相对复杂。在这里,出于实际原因,我们将采取不同的方法来简化此类调整。不是为了交易者,而是为了我们程序员。交易者需要适应我们的实施方式。但作为交换,他们将获得一个简单的选择:选择交易完整合约还是迷你合约。

为了实现这一点(至少在最初,当我们仍在与实时交易服务器通信时),我们需要对现有代码进行一些有针对性的修改。让我们从一个关键事实开始:正如介绍中所述,最好的图表是基于历史数据的图表。但这个历史图表不能直接交易。

为了解决这个问题,我们需要一个系统,将历史图表上的订单路由到交易者实际想要使用的合约。请记住,交易者可能想要交易完整合约或迷你合约。但我们暂时还不必担心这一点。首先,我们必须了解一个简单的事实:图表上显示的内容来自合约的历史数据,句号。

在B3上,期货合约有六种不同的命名约定,这适用于每一份具体合约。这意味着完整合约有六种类型,迷你合约有六种类型。这似乎是一个巨大的复杂问题,但仔细观察后,就会发现情况并没有看上去那么糟糕。尽管有六种变体,但它们实际上可以归结为三种主要类型,每种类型都有两个子变体。

这种简化对我们有很大帮助。尽管如此,我还是建议研究这三种类型之间的差异。它们之间的图表数据差异很大,许多交易者完全没有意识到这一点。如果您仅编写解决方案,请务必通知交易者。如果您既从事编程又从事交易,请更加认真地对待这个建议 —— 如果您不了解这些区别,您可能会遇到严重的问题。

因此,有三种命名类型,每种类型都有两种变体。好,但对于我们程序员来说,重要的不是名称本身,因为这与交易者更相关。对我们来说,关键问题是:这个命名约定中有规则吗?如果可以,我们如何利用它来构建跨期订单系统?

幸运的是,这样的规则确实存在。事实上,我们已经使用它一段时间了。在下面的代码片段中看看这是如何工作的。

38. //+------------------------------------------------------------------+
39.       void CurrentSymbol(void)
40.          {
41.             MqlDateTime mdt1;
42.             string sz0, sz1;
43.             datetime dt = macroGetDate(TimeCurrent(mdt1));
44.             enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER;
45.       
46.             sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
47.             for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS);
48.             switch (eTS)
49.             {
50.                case DOL   :
51.                case WDO   : sz1 = "FGHJKMNQUVXZ"; break;
52.                case IND   :
53.                case WIN   : sz1 = "GJMQVZ";       break;
54.                default    : return;
55.             }
56.             for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0))
57.                if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break;
58.          }
59. //+------------------------------------------------------------------+

C_Terminal.mqh 文件中的代码

此代码片段来自头文件 C_Terminal.mqh。请注意,在第 44 行我们定义了将支持的期货合约的名称。如果您想使用玉米、牛、标准普尔、欧元等资产,可以将其他合约添加到此列表中。只需记住遵循每个合约的命名规则,以正确识别活动合约。这里描述的程序不会返回过去的合约,也不会返回提前两个或多个到期的合约。它始终解析为当前有效的合约。

为了实现这一点,第 46 行提取资产名称的前三个字符。无论资产是什么,它总是会取得这前三个字符。这是因为 B3(巴西证券交易所)使用前三个字符来标识资产的命名约定。提取后,资产名称将保存在变量中,以便在其余代码中使用。请注意这一事实。

接下来,第 47 行迭代我们定义的合约名称的枚举。这里的目的是找到正确的匹配。这就是为什么在第 44 行的枚举中,名称必须与合约名称本身相似。由于 B3 使用大写字符,枚举也必须大写。一旦找到匹配,或者一旦列表用尽,第 47 行中的循环就结束了。

在第 48 行,我们可以测试检索到的值。如果没有找到匹配项,则执行跳到第 54 行的代码。否则,我们继续构建完整的合约名称。最后的命名发生在第 57 行,在该行中,程序确认正在生成的合约名称对应于当前活动的合约。简而言之,该程序扫描可能的期货合约,直到找到有效的合约。

然而,这里有一个重要的限制。该过程使用从资产名称派生的基本合约名称。这意味着您只能将给定合约的历史数据映射到同一合约的活动版本。使用当前代码,您无法将历史数据从完整合约映射到活动的迷你合约。这就是我们将在本文中解决的不便之处。

通过这样做,我们将使交易者能够选择是交易完整合约还是迷你合约,即使使用其中任何一个的历史数据。我们的目标是通过最少的代码更改来实现这一点,因为我们更改的代码越多,引入错误的可能性就越大。

为了实现这一点,上面的代码片段被修改如下:

38. //+------------------------------------------------------------------+
39.       void CurrentSymbol(bool bUsingFull)
40.          {
41.             MqlDateTime mdt1;
42.             string sz0, sz1;
43.             datetime dt = macroGetDate(TimeCurrent(mdt1));
44.             enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER;
45.       
46.             sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
47.             for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS);
48.             switch (eTS)
49.             {
50.                case DOL   :
51.                case WDO   : sz1 = "FGHJKMNQUVXZ"; break;
52.                case IND   :
53.                case WIN   : sz1 = "GJMQVZ";       break;
54.                default   : return;
55.             }
56.             sz0 = EnumToString((eTypeSymbol)(((eTS & 1) == 1) ? (bUsingFull ? eTS : eTS - 1) : (bUsingFull ? eTS + 1: eTS)));
57.             for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0))
58.                if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break;
59.          }
60. //+------------------------------------------------------------------+

C_Terminal.mqh 文件中的代码

正如您所见,变化很小。第一个是在第 39 行向函数添加一个参数。该参数告诉程序要生成的合约名称应该是完整合约还是迷你合约。选择权在于交易者。作为程序员,我们的责任是让他们能够灵活地使用他们喜欢的任何图表,只要图表中的资产数据在某种程度上与正在交易的合约相对应。当然,我们可以实现更复杂的行为,但不要让需要做的事情过于复杂。

除了第 39 行的这一更改之外,我们还添加了一行新代码。从技术上讲,这种添加并不是严格必要的,因为我们本可以修改现有的代码。但它简化了解释和你对正在发生的事情的理解。这个新行,第 56 行可以直接放置在第 58 行使用变量 sz0 的位置,结果将是相同的。然而,这将使解释变得混乱,更难理解。

那么,第 56 行对变量 sz0 做了什么?让我们来分析一下。本质上,我们忽略了资产名称本身。相反,我们将第 44 行定义的枚举转换为字符串。MQL5 通过 EnumToString 函数允许这样做。

现在,如果你使用的期货合约没有完整版和迷你版,那么一个细节可能会使事情变得复杂。这在大宗商品中相当常见。但在我想要展示的案例中 —— 指数和货币,特别是美元期货 —— 两种合约类型都存在。

除非另有定义,否则枚举始终以零值开始。在我们的例子中,迷你合约被赋予偶数值,而完整合约被赋予奇数值。理解这一点很重要。这些值是二进制的,您应该知道如何选择这个或那个位。在二进制中,最低有效位(最右边的一位)决定数字是偶数还是奇数。通过应用按位与来隔离该位,我们可以检查枚举值是偶数还是奇数。再说一遍:迷你合约是偶数,完整合约是奇数。

因此,如果值是奇数,则执行三元运算符的第一部分。如果是偶数,则执行第二部分。目前,我认为一切都很清楚。然后,在第一个三元运算符的每个分支内,我们使用第二个三元运算符。第二个三元运算符允许我们调整变量 eTS ,确保它反映正确的合约名称。

例如:如果合约是 WDO,那么 eTS 等于 2,即偶数。这将触发第一个三元运算符的第二部分,在其中,第二个三元运算符执行第二次检查。它检查程序调用请求的是完整合约还是迷你合约。

如果交易者要求完整的合约, eTS 就会增加 1。因此,它的值从 2 变为 3。在枚举中,位置 3 对应于 DOL。当 MQL5 执行 EnumToString 时,值 3 被转换为字符串 DOL,从而产生完整的合约名称,即使图表基于迷你美元历史数据。

反过来也是一样的,如果图表显示完整美元合约的历史数据,但交易者请求迷你合约,则第一个三元运算符执行其第一个分支。在其中,第二个三元运算符将 eTS 减 1,将其值从 3 更改为 2。这将其映射回 WDO。

简而言之:在第 47 行找到的值在第 56 行进行了调整,以便合约名称与交易者的选择(迷你或完整)相匹配,同时仍然依赖于其中一个的历史图表。

到目前为止,一切都很好。但是,如果合约没有迷你版本,只有完整合约怎么办?我们该如何处理呢?您可能认为有两种可能的解决方案。事实上,只有一个。如果你试图复制枚举中的值来人为地创建偶数和奇数条目,编译器将拒绝它。相反,解决方案是按逻辑顺序构造枚举。然后,当测试特定值时,如果不存在迷你合约,则变量 sz0 保持不变。在实践中,这需要在代码中进行额外的测试。但那里没有什么复杂的东西。

这样,我们就解决了问题的第一部分。但是,我们还没有完成。要继续,我们需要调整头文件 C_Terminal.mqh 的另一部分:类构造函数。构造函数负责调用我们刚刚修改的过程。因此,现在必须用下面显示的新版本替换原始版本。

72. //+------------------------------------------------------------------+      
73.       C_Terminal(const long id = 0, const uchar sub = 0, const bool bUsingFull = false)
74.          {
75.             m_Infos.ID = (id == 0 ? ChartID() : id);
76.             m_Mem.AccountLock = false;
77.             m_Infos.SubWin = (int) sub;
78.             CurrentSymbol(bUsingFull);
79.             m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
80.             m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
81.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
82.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, true);
83.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, true);
84.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
85.             m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
86.             m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
87.             m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
88.             m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
89.             m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
90.             m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
91.             m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
92.             m_Infos.ChartMode   = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE);
93.             if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE));
94.             ChartChange();
95.          }
96. //+------------------------------------------------------------------+

C_Terminal.mqh 文件中的代码

请注意,只进行了两个非常简单的更改。第一个是第 73 行,我们在这里添加了一个新参数。然后在第 78 行使用此参数,我们在此调用前面解释的过程。默认情况下,我将其设置为优先考虑迷你合约。但交易者可以自由选择最适合其策略的选项。为了支持这种灵活性,需要对代码的特定部分进行一些小调整。

由于我们尚未修改 EA 交易代码,因此必须在 Chart Trade 代码中进行必要的更改。为了清楚起见,让我们在单独的一节中介绍一下。


将 Chart Trade 转变为跨期订单系统

Chart Trade 作为跨期订单系统运行所需的改变非常简单。您可以考虑在界面中添加一个对象,以便交易者可以直接切换跨期类型。然而,我在这里不会采取这种做法。我的目标是尽量减少变化。添加这样的对象需要更多的代码来支持此功能。相反,我们可以允许交易者通过指标设置更改跨期类型。这种方法很简单,对现有代码的修改很少。第一步可以在下面的代码中看到。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Chart Trade Base Indicator."
04. #property description "See the articles for more details."
05. #property version   "1.80"
06. #property icon "/Images/Market Replay/Icons/Indicators.ico"
07. #property link "https://www.mql5.com/pt/articles/12536"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh>
12. //+------------------------------------------------------------------+
13. #define def_ShortName "Indicator Chart Trade"
14. //+------------------------------------------------------------------+
15. C_ChartFloatingRAD *chart = NULL;
16. //+------------------------------------------------------------------+
17. enum eTypeContract {MINI, FULL};
18. //+------------------------------------------------------------------+
19. input ushort         user01 = 1;         //Leverage
20. input double         user02 = 100.1;     //Finance Take
21. input double         user03 = 75.4;      //Finance Stop
22. input eTypeContract  user04 = MINI;      //Cross order in contract
23. //+------------------------------------------------------------------+
24. int OnInit()
25. {
26.    chart = new C_ChartFloatingRAD(def_ShortName, new C_Mouse(0, "Indicator Mouse Study"), user01, user02, user03, (user04 == FULL));
27.    
28.    if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
29. 
30.    return INIT_SUCCEEDED;
31. }
32. //+------------------------------------------------------------------+
33. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
34. {
35.    return rates_total;
36. }
37. //+------------------------------------------------------------------+
38. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
39. {
40.    if (_LastError < ERR_USER_ERROR_FIRST) 
41.       (*chart).DispatchMessage(id, lparam, dparam, sparam);
42. }
43. //+------------------------------------------------------------------+
44. void OnDeinit(const int reason)
45. {
46.    switch (reason)
47.    {
48.       case REASON_INITFAILED:
49.          ChartIndicatorDelete(ChartID(), 0, def_ShortName);
50.          break;
51.       case REASON_CHARTCHANGE:
52.          (*chart).SaveState();
53.          break;
54.    }
55. 
56.    delete chart;
57. }
58. //+------------------------------------------------------------------+

Chart Trade 指标源代码

在第 17 行,我们添加一个枚举。它帮助交易者(或用户)定义合约类型。请注意,第 22 行使用了此枚举。此时,交易者需要决定 EA 交易是否应该使用完整合约或迷你合约进行操作。这里有一个缺点:理想情况下,应该在 EA 交易中进行选择,而不是在 Chart Trade 中进行选择。但由于 Chart Trade 和 EA 交易目前仍然是独立的实体,我们将保持这种状态。

真正的挑战不在于 Chart Trade,甚至不在于 EA 交易。正如我在上一篇文章中所解释的,Chart Trade 已经可以控制 EA 交易。问题在于稍后将开发的系统的另一部分。真正的困难就在这里,因为最终一切都必须通过 EA 交易。理想情况下,我们需要提供其中所需的一切。不过,目前,为了演示目的,我们将在 Chart Trade 中处理选择。

然后在第 26 行使用此配置值。请注意,我们传递给构造函数的是一个布尔值,而不是一个数值。为什么呢?因为对于最终用户来说,布尔值可能看起来不那么具有描述性。对于我们程序员来说这非常清楚。毕竟,只有两种可能的情况:交易者要么使用完整合约,要么使用迷你合约。因此,从编码的角度来看,布尔值是最合适的选择。然后将布尔值传递给类构造函数。让我们在下面的代码片段中看看它是什么样子的。

213. //+------------------------------------------------------------------+
214.       C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const short Leverage, const double FinanceTake, const double FinanceStop, const bool bUsingFull)
215.          :C_Terminal(0, 0, bUsingFull)
216.          {
217.             m_Mouse = MousePtr;
218.             m_Info.IsSaveState = false;
219.             if (!IndicatorCheckPass(szShortName)) return;
220.             if (!RestoreState())
221.             {
222.                m_Info.Leverage = Leverage;
223.                m_Info.IsDayTrade = true;
224.                m_Info.FinanceTake = FinanceTake;
225.                m_Info.FinanceStop = FinanceStop;
226.                m_Info.IsMaximized = true;
227.                m_Info.minx = m_Info.x = 115;
228.                m_Info.miny = m_Info.y = 64;
229.             }
230.             CreateWindowRAD(170, 210);
231.             AdjustTemplate(true);
232.          }
233. //+------------------------------------------------------------------+

C_ChartFloatingRAD.mqh 文件片段

此处的变化与 C_Terminal 类的构造函数一样简单。我们仅添加一个由构造函数接收的新参数(第 214 行),然后将其传递给 C_Terminal 构造函数(第 215 行)。就这么简单。一切都简单明了,不言自明。

尽管如此,我们还需要一个小的修改。这次,它是对 C_ChartFloatingRAD 类的补充。这一变化使 Chart Trade 能够向 EA 交易传达交易者实际想要操作的内容。修改内容如下面代码片段所示。

330.       case MSG_BUY_MARKET:
331.          ev = evChartTradeBuy;
332.       case MSG_SELL_MARKET:
333.          ev = (ev != evChartTradeBuy ? evChartTradeSell : ev);
334.       case MSG_CLOSE_POSITION:
335.          if ((m_Info.IsMaximized) && (sz < 0))
336.          {
337.             string szTmp = StringFormat("%d?%s?%s?%c?%d?%.2f?%.2f", ev, _Symbol, GetInfoTerminal().szSymbol, (m_Info.IsDayTrade ? 'D' : 'S'),
338.                                         m_Info.Leverage, FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage));                           
339.             PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp);
340.             EventChartCustom(GetInfoTerminal().ID, ev, 0, 0, szTmp);
341.          }
342.       break;

C_ChartFloatingRAD.mqh 文件片段

这种调整非常微小,可能会被忽视。它出现在第 337 行,我们在此添加了一个要发送给 EA 交易的新值。该值告诉 EA 交易哪种资产 - 或者更准确地说,哪种合约 - 正在 Chart Trade 中显示。请记住,此更改将强制 EA 交易进行另一次更新。但我们稍后会处理这个问题。


最后的探讨

我们在本文中所做的工作展示了 MQL5 的灵活性。但它也带来了我们以后需要解决的新挑战。这些事情远非简单,并不总是能完全轻松地解决。实施跨期订单系统,允许 Chart Trade 用户告诉 EA 交易,图表资产不一定是正在交易的资产,这带来了很大的复杂性。我想明确一点:大多数这些问题并不是由 Chart Trade 或 EA 交易本身引起的。

当我们引入我尚未引入的元素,即仍需开发的代码部分时,真正的问题就出现了。允许用户在 Chart Trade 中选择合约类型并不是最好的长期解决方案。至少目前在我看来不是。

未来的变化可能会使在 Chart Trade 中直接配置这一点变得实用且可持续。不过,就目前而言,我更喜欢把事情简单到足以解释清楚。快速构建个人解决方案很容易;构建一个其他人可以理解和应用的模型要求更高。这就是为什么您可以期待此 Chart Trade EA 交易系统的未来更新。它们一定会来的。

在下面的视频中,您可以直接在图表上看到该过程。


本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/12536

附加的文件 |
Anexo.zip (490.53 KB)
使用MQL5经济日历进行交易(第六部分):利用新闻事件分析和倒计时器实现交易入场自动化 使用MQL5经济日历进行交易(第六部分):利用新闻事件分析和倒计时器实现交易入场自动化
在本文中,我们将借助MQL5经济日历实现交易入场自动化,具体方法是应用用户自定义的筛选条件和时差偏移量来识别符合条件的新闻事件。我们通过对比预测值和前值,来确定是开立买入(BUY)单还是卖出(SELL)订单。动态倒计时器会显示距离新闻发布剩余的时间,并且在完成一笔交易后自动重置。
MQL5交易策略自动化(第八部分):构建基于蝴蝶谐波形态的智能交易系统(EA) MQL5交易策略自动化(第八部分):构建基于蝴蝶谐波形态的智能交易系统(EA)
在本文中,我们将构建一个MQL5智能交易系统(EA),用于检测蝴蝶谐波形态。我们会识别关键转折点,并验证斐波那契(Fibonacci)水平以确认该形态。之后,我们会在图表上可视化该形态,并在得到确认时自动执行交易。
价格行为分析工具包开发(第十五部分):引入四分位理论(1)——四分位绘图脚本 价格行为分析工具包开发(第十五部分):引入四分位理论(1)——四分位绘图脚本
支撑位与阻力位是预示潜在趋势反转和延续的关键价位。尽管识别这些价位颇具挑战性,但一旦精准定位,您便能从容应对市场波动。如需进一步辅助,请参阅本文介绍的四分位绘图工具,该工具可帮助您识别主要及次要支撑位与阻力位。
在 MQL5 中构建自优化智能交易系统(第六部分):防止爆仓 在 MQL5 中构建自优化智能交易系统(第六部分):防止爆仓
在今天的讨论中,我们将一同寻找一种算法程序,以最大限度地减少我们因盈利交易被止损而平仓的总次数。我们面临的问题极具挑战性,社区讨论中给出的大多数解决方案都缺乏既定且固定的规则。我们解决问题的算法方法提高了我们交易的盈利能力,并降低了我们的平均每笔交易亏损。然而,要完全过滤掉所有将被止损的交易,还需要进一步的改进,但我们的解决方案对任何人来说都是一个很好的初步尝试