程序库: OnTickMulti - 页 5 12345 新评论 fxsaber 2025.10.03 20:14 #41 Stanislav Korotky #:为了编写一个完整的测试用例,我需要了解具体的实际应用场景——我们是否只根据时间精确到毫秒的交易信号进行交易? 不是。目标是始终确保所有交易品种的环境信息都是最新的。在我给出的示例中,OnTimer中始终满足实时性条件。开仓操作仅用于演示。 Stanislav Korotky 2025.10.03 22:40 #42 fxsaber #: 不。目标是始终确保所有指标的当前状态。在我给出的示例中,OnTimer中始终满足当前状态的条件。开仓操作仅作为演示。 好的。 目前尚不清楚,在 OnTickMulti 事件中的实现为何会“丢失”一个 tick,或者换句话说, 为什么 SymbolInfoTick 返回的价格与预期不符(我之前推测,这可能是由于指标中的自定义事件调度导致的延迟) ——为了确保实验的纯净性,我会尝试在不使用“间谍”指标的情况下进行测试——仅通过常规的 OnTick 事件对所有符号调用 SymbolInfoTick/CopyTicks,至少针对相同的戳时间。 至于 OnTimer,我对此有些疑问(如有错误,请指正): void OnTimer() { if (TimeMsc++ == 1759280400081) // 2025.10.01 01:00:00.081 PositionOpen(); } 我们无法保证处理程序能在1毫秒内执行完毕,因此仅对计数器进行简单递增并不能保证保持初始同步,也就是说 与服务器时间的校验必须每次都严格在运行时实时进行(在开仓后,或进行其他比计数器递增更复杂的计算之后,如果存在此类情况的话)。 换言之,当需要开立大量头寸时,上述优化方案将行不通。 此外,初始同步(计数器的初始化)——在我看来——也并非100%完美。 ulong TimeMsc = TimeTradeServer() * 1000 + (inTimer && EventSetMillisecondTimer(1)); 假设在测试环境中,服务器时间确实不包含毫秒,但这样的代码在在线环境中如何运行?为什么我们要加1毫秒?我还是会从时钟 tick 中获取服务器时间。 这并非吹毛求疵,只是对“时效性条件始终成立”这一假设产生了一些疑虑。 fxsaber 2025.10.04 05:21 #43 Stanislav Korotky #:仅对计数器进行递增并不能保证保持最初的同步状态 在测试环境中可以保证。 但这样的代码在在线环境中如何运行? 在线环境中不存在此类问题,因为发送到终端的所有数据都是参考性的:由于延迟,这些数据已不再准确。 那么,为什么我们要增加1毫秒呢? 因为第一个 OnTimer 不会立即触发,而是在设定的间隔后才会被调用。 fxsaber 2025.10.04 05:36 #44 Stanislav Korotky #:目前尚不清楚,OnTickMulti 事件中的代码在何处“丢失”了 tick,或者换句话说,为何 SymbolInfoTick 返回的价格与预期不符 OnTickMulti 肯定与此无关,因为某个符号的内置 SymbolInfoTick 函数会返回正确的 tick 数据,而对于其他符号则不会。 原因仅在于 tick 数据的传输顺序。 关于交易、自动交易系统及交易策略测试的论坛 库:OnTickMulti fxsaber,2025年9月30日 09:24 时间相同的报价数据不会同时到达,而是依次到达。如果时间较早的欧元/美元(EURUSD)报价数据先到达,那么此时还无法得知时间相同的英镑/美元(GBPUSD)报价数据(它将作为第二条到达)的具体情况。 因此,在第一个EURUSD报价到达时,第二个GBPUSD报价根本不存在,只有前一个GBPUSD报价的数据。 fxsaber 2025.10.04 06:18 #45 fxsaber #:因为第一个 OnTimer 将在指定的间隔后被调用,而不是立即被调用。 我设计了一种快速更新所有数据的方法。 #include <fxsaber\OnTickMulti\OnTickMulti.mqh> //https://www.mql5.com/zh/code/47647 #include <MT4Orders.mqh> //https://www.mql5.com/zh/code/16006 void PositionOpen() { for (uint i = ArraySize(OnTickMultiObject.Symbols); (bool)i--;) { const string SymbName = OnTickMultiObject.Symbols[i]; OrderSend(SymbName, OP_BUY, 1, SymbolInfoDouble(SymbName, SYMBOL_ASK), 0, 0, 0); } } long CurrentTime = 0; // 多字符 OnTick。 void OnTickMulti( const string &Symb, const uint &Index ) { MqlTick Tick; if (SymbolInfoTick(Symb, Tick) && (Tick.time_msc > CurrentTime)) { CurrentTime = Tick.time_msc; EventSetMillisecondTimer(1); } } void OnTimer() { if (CurrentTime == 1759280400081) // 2025.10.01 01:00:00.081 PositionOpen(); EventKillTimer(); } 这种机制使得即使在普通模式下(单币种模式且未启用 OnTickMulti),也能仅处理最新数据。例如,EURUSD 存在多个时间相同的报价点。通过数据刷新,系统将处理最新的一条报价点——即该序列中的最后一条。 附注:这也是创建自定义符号的 另一个原因——仅将时间相同的连续 tick 中最后一个写入历史数据。这样一来,在单币种模式下将始终保持数据更新。 Библиотеки: TicksShort 2025.09.26 www.mql5.com Статьи и техническая библиотека по автоматическому трейдингу: Библиотеки: TicksShort Stanislav Korotky 2025.10.04 19:19 #46 Rorschach #:但开盘价其实是一样的,尽管所有股票的开盘时间都相同。 正因为这种行为,导致一些系统无法进行正常的测试。 我重新回顾了一下开盘价测试模式。关键在于,尽管名称如此,但在该模式下,测试器会生成4个OHLC tick,而不是从名称上直观预期的1个。 在这4个控制点中,对于专家顾问,仅提取第一个O并调用OnTick;而对于指标,还会根据K线方向,针对HLC或LHC额外调用3次OnCalculate,并附带相应价格的Tick数据。 这三个额外数据点的时间被人为设定为与K线末尾的三秒时间相同。这意味着,间谍指标会针对每个符号发送多个事件,而非仅发送一个。对于使用开盘价模式的用户来说,可能需要考虑这一点。 此外,我还观察到一个异常现象(在根据书中内容编写的类似“间谍”指标中添加了调试功能):来自上一根K线的新增符号的Tick事件不知为何会在新K线上重复出现, 随后才触发监视器的 OnCalculate 方法,并收到包含更新后价格的附加符号新 tick 事件。 因此,为了获得附加符号的最新价格,需要在事件本身中包含时间戳,并且不要重复处理已经处理过的事件。我在发送时是这样做的: int OnCalculate(const int rates_total, const int prev_calculated, const int, const double &price[]) { MqlTick tick; SymbolInfoTick(_Symbol, tick); EventChartCustom(Chart, Message, Index, (double)tick.time_msc, NULL); return rates_total; } 而在接收时(此处展示的是单个附加符号的情况,若涉及多个附加符号,则需要使用 timestamp[] 数组!): void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(id == CHARTEVENT_CUSTOM + Message) { static long timestamp; long event = (long)dparam; if(event > timestamp) { OnSymbolTick((int)lparam); timestamp = event; } } } 需要注意的是,当存在毫秒值相同的报价时,系统会优先处理第一个报价,而非最后一个。 Rorschach 2025.10.17 17:08 #47 Rorschach #: 最简单的同步方法 本以为时间序列同步是一个已被深入研究的问题,应该有现成的算法,结果上网一查,却一无所获 12345 新评论 您错过了交易机会: 免费交易应用程序 8,000+信号可供复制 探索金融市场的经济新闻 注册 登录 拉丁字符(不带空格) 密码将被发送至该邮箱 发生错误 使用 Google 登录 您同意网站政策和使用条款 如果您没有帐号,请注册 可以使用cookies登录MQL5.com网站。 请在您的浏览器中启用必要的设置,否则您将无法登录。 忘记您的登录名/密码? 使用 Google 登录
为了编写一个完整的测试用例,我需要了解具体的实际应用场景——我们是否只根据时间精确到毫秒的交易信号进行交易?
不。目标是始终确保所有指标的当前状态。在我给出的示例中,OnTimer中始终满足当前状态的条件。开仓操作仅作为演示。
好的。
目前尚不清楚,在 OnTickMulti 事件中的实现为何会“丢失”一个 tick,或者换句话说, 为什么 SymbolInfoTick 返回的价格与预期不符(我之前推测,这可能是由于指标中的自定义事件调度导致的延迟) ——为了确保实验的纯净性,我会尝试在不使用“间谍”指标的情况下进行测试——仅通过常规的 OnTick 事件对所有符号调用 SymbolInfoTick/CopyTicks,至少针对相同的戳时间。
至于 OnTimer,我对此有些疑问(如有错误,请指正):
我们无法保证处理程序能在1毫秒内执行完毕,因此仅对计数器进行简单递增并不能保证保持初始同步,也就是说 与服务器时间的校验必须每次都严格在运行时实时进行(在开仓后,或进行其他比计数器递增更复杂的计算之后,如果存在此类情况的话)。 换言之,当需要开立大量头寸时,上述优化方案将行不通。
此外,初始同步(计数器的初始化)——在我看来——也并非100%完美。
假设在测试环境中,服务器时间确实不包含毫秒,但这样的代码在在线环境中如何运行?为什么我们要加1毫秒?我还是会从时钟 tick 中获取服务器时间。
这并非吹毛求疵,只是对“时效性条件始终成立”这一假设产生了一些疑虑。
仅对计数器进行递增并不能保证保持最初的同步状态
在测试环境中可以保证。
但这样的代码在在线环境中如何运行?
在线环境中不存在此类问题,因为发送到终端的所有数据都是参考性的:由于延迟,这些数据已不再准确。
那么,为什么我们要增加1毫秒呢?
因为第一个 OnTimer 不会立即触发,而是在设定的间隔后才会被调用。
目前尚不清楚,OnTickMulti 事件中的代码在何处“丢失”了 tick,或者换句话说,为何 SymbolInfoTick 返回的价格与预期不符
OnTickMulti 肯定与此无关,因为某个符号的内置 SymbolInfoTick 函数会返回正确的 tick 数据,而对于其他符号则不会。
原因仅在于 tick 数据的传输顺序。
关于交易、自动交易系统及交易策略测试的论坛
库:OnTickMulti
fxsaber,2025年9月30日 09:24
时间相同的报价数据不会同时到达,而是依次到达。如果时间较早的欧元/美元(EURUSD)报价数据先到达,那么此时还无法得知时间相同的英镑/美元(GBPUSD)报价数据(它将作为第二条到达)的具体情况。 因此,在第一个EURUSD报价到达时,第二个GBPUSD报价根本不存在,只有前一个GBPUSD报价的数据。因为第一个 OnTimer 将在指定的间隔后被调用,而不是立即被调用。
我设计了一种快速更新所有数据的方法。
这种机制使得即使在普通模式下(单币种模式且未启用 OnTickMulti),也能仅处理最新数据。例如,EURUSD 存在多个时间相同的报价点。通过数据刷新,系统将处理最新的一条报价点——即该序列中的最后一条。
附注:这也是创建自定义符号的 另一个原因——仅将时间相同的连续 tick 中最后一个写入历史数据。这样一来,在单币种模式下将始终保持数据更新。
但开盘价其实是一样的,尽管所有股票的开盘时间都相同。
正因为这种行为,导致一些系统无法进行正常的测试。
我重新回顾了一下开盘价测试模式。关键在于,尽管名称如此,但在该模式下,测试器会生成4个OHLC tick,而不是从名称上直观预期的1个。 在这4个控制点中,对于专家顾问,仅提取第一个O并调用OnTick;而对于指标,还会根据K线方向,针对HLC或LHC额外调用3次OnCalculate,并附带相应价格的Tick数据。 这三个额外数据点的时间被人为设定为与K线末尾的三秒时间相同。这意味着,间谍指标会针对每个符号发送多个事件,而非仅发送一个。对于使用开盘价模式的用户来说,可能需要考虑这一点。
此外,我还观察到一个异常现象(在根据书中内容编写的类似“间谍”指标中添加了调试功能):来自上一根K线的新增符号的Tick事件不知为何会在新K线上重复出现, 随后才触发监视器的 OnCalculate 方法,并收到包含更新后价格的附加符号新 tick 事件。 因此,为了获得附加符号的最新价格,需要在事件本身中包含时间戳,并且不要重复处理已经处理过的事件。我在发送时是这样做的:
而在接收时(此处展示的是单个附加符号的情况,若涉及多个附加符号,则需要使用 timestamp[] 数组!):
需要注意的是,当存在毫秒值相同的报价时,系统会优先处理第一个报价,而非最后一个。
最简单的同步方法