在 MQL4 和 MQL5 框架下开发 OpenAI 的 ChatGPT 功能
内容
- 概述
- 在 MQL5 中使用 ChatGPT 的前景
- 与 ChatGPT 关联的潜在陷阱
- 利用 ChatGPT 解决数学问题及在代码中开发数学模型的机会
- 在 MQL4 和 MQL5 中正确生成代码的方法
- 利用 ChatGPT 开发交易系统
- 功能评估和结果分析
- 结束语
概述
好了,我们从基础开始。该技术是出自 OpenAI 人工智能的变体之一,旨在帮助人们解决各种任务。这个工具看起来像一个常规的信使聊天。然而,在另一端,有一个所谓的人工智能,它以文本形式回答您。
当然,交流渠道仅限于文本,但对于解决各种问题、或学习很多新事物来说已然足够了。这个文本频道适合解决完全不同的问题,譬如编程、数学、物理、化学,更不用说熟练的翻译、和其它能力了。
而我们对这种模型感兴趣,只是为了开发可盈利的交易系统。我对如何以最优和正确地方式运用这项技术来更快、更轻松地开发交易系统产生了浓厚兴趣。最终,首先开始把技术正确应用于其预期目的之人,会降低所有的开发成本和劳动力成本,从而带来明显的竞争优势。
在 MQL5 中使用 ChatGPT 的前景
我们来更详细地探讨技术前景。我以自己的例子进行了相当详细的研究之后,我意识到这种技术才只是真正伟业的开端。但即使是现在,我也可以强调以下功能:
- 生成任何 MQL4 和 MQL5 代码
- 进行代码重构和优化
- 清理代码
- 添加代码注释
- 修正错误
- 实现数学模型
- 构建基于数学模型的后续代码
- 具现化任何已知算法和数学模型
- 加快智能交易系统 (EA) 开发过程
- 海量信息
这绝不是最终列表,您可在此自行添加。我认为,当人们学习这种技术时,他们所有人开始大致分为三个子组:
- “现在我们将制作一个超级算法”
- 这是那些对人工智能持谨慎态度,并质疑其实用性的人
- 机器不可能比人类更好。这一切都只是一种炒作
我很久以前就开始掌握这项技术,且在一开始我属于第三类人。在与这个 AI 打交道的前两天,我突然从第三类转到了第一类;而在一段更有趣、也相当不愉快的开始调整自己信念的过程之后,更像是回滚到 “1.5” 类,这已经是对这项技术更现实的评估了。
这项技术确实很实用,但并不像您最初想象的那么多。有关于此,值得向这项技术的开发人员和营销人员致敬,如果仅出于事实,它在最初几天的使用中产生了令人难以置信的“哇”效果,这足以引起自抬身价的连锁反应。
为了理解这一点,您与这个 AI 打交道时需要大量练习。就个人而言,我与它大约一百多次的不同对话涉及到各种主题。我可以说,我在运用这项技术方面已经获得了足够的实践,可以开始将其用于 MQL5。在迈上实际应用之前,我只需要告诉您一些非常重要的信息,为此,我们需要更深入地查看这项技术之下掩盖的内容。
与 ChatGPT 关联的潜在陷阱
在运用这项技术初期的那几天,您将体验到所谓的“哇”效果,是因为事实上这主要是一个文本模型,旨在将您的问题转化为答案。您之所以会喜欢它的答案,原因在于其创作者教会了这个模型漂亮地说谎。是的,我不是在开玩笑!它说的谎话是如此美丽,以至于您不由自主地相信它,即使经历多次尝试它暴露后,模型也会经常为您提供这样的东西:
- 不好意思!是的,您是对的。我犯了一个小错误。我将来会考虑到这一点。(事实上,它不会。这只是一个空洞的承诺)
- 很抱歉误会了,我犯了一个错误。这是纠正后的版本。(甚至包含更多错误)
- 您也许会发现模型在计算中犯了错误。(事实上,该模型根本没有进行任何计算,它简单地从某处找到了一个近似数字)
- 您已在代码中检测到错误。(模型会再次找借口,试图欺骗您)
- 该模型模仿任务的执行,并试图说服您它执行了您的要求。(实际上,它会在某个地方找到类似的东西,然后将其提供给您。)
- 不好意思!我帮不了你。(当模型意识到您检测到其谎言或犯错时,这就会发生)
- 该模型在其答案中添加了大量陈言滥调,从而给人一种回答和谐连贯的假象。(我认为,这在某种程度上与资源优化的成本有关)
简言之,模型将尝试以各种可能的方式跳过提供的任务,同时得益于您的“提示”不完善性。犯错的情况下,它会试图以这样一种方式为自己辩解,以至于您难以在同一件事上抓住它,如果它明白您已经盯上它了,它就会试图用某些回复和心术技巧来软化您的负面反应。总体来说,我认为该算法只是为了迎合可接受的用户满意度而量身定制的最优资源消耗。换言之,在这种情况下,其目标并非为您提供最高品质的问题解决方案,而是出于完全不同的可能原因,为您提供迁就您的方案。敞明讲,其目标是用户的正面反应,而任务是否得以正确解决并不那么重要。这就是它所追求的。从营销的角度来看,这是正确的,有关于此,AI 是称职的雄辩家。
考虑到这一切,我意识到,为了在这方面尽量令其自由度最小,我们首先应该积累与它沟通的经验,并得出结论,哪种类型的任务不需要使用这种诡辩,只是因为它很容易满足我们的要求,鉴于给定的功能很容易兑现,且给出直接答案比激发想象力要容易得多。因为它在资源成本方面效率不高,换言之,它欺骗您毫无益处,但给您一个实际的答案就容易得多。
为了开始了解如何提出此类问题,我们需要了解以下内容:
- “提示”的结构和类型并不那么重要,而问题的细节和品质更重要。(该模型理解所有内容,无需从互联网上读取有关如何撰写提示的指南。撰写内容如您所愿,但没有俚语。)
- 将复杂的问题或请划分成小段。(把问题简化和划分,以便获得更精确、简单和清晰的答案。)
- 您的问题越聪明、越精准,答案就越有用。
- 它不会给您一个超级的思路或算法。(该模型不具备广博的思维、计划和智能创造力。但从表面看,它好似有创造的能力,这恰因如我所说,它说谎很在行。)
- 我们只在提升工作速度和降低劳动力成本的背景下才应考虑运用这项技术。
- 每个新请求对整个对话的依赖程度都应当最低。(该模型无法记忆整个对话,并且在回复中往往不考虑旧消息。只要试一下,您就会明白我的意思。)
- 问题越复杂,包含的细节越多,完全胡言乱语的可能性就越高。(这是对第二子段的解释。)
- 该模型不会访问互联网,仅基于其知识库生成所有答案。(如果您催动它从互联网上获取一些数据,它会从其数据库中给您旧数据,或者基于您传递给它的上下文调整答案,当作一个新数据。请记住这一点。模型会这样做,只是因为它意识到与您争论没有用,最好说服您它已做了一切。
- ChatGPT 3.5 的训练持续到 2019 年。(这意味着在开发人员批准下一场次的训练之前,它没有 2019 年之后有关事件的信息。)
- ChatGPT 4.0 的训练持续到 2021 年。(它好一些,因为它撒谎很少,并试图始终正确回答。如果您试着提出问题并比较,您会发现 3.5 明目张胆地撒谎。
事实上,还有很多其它不愉快的片刻,破坏了这项技术的印象。但尽管如此,如果这项技术毫无用处,我也不会撰写有关它的文章。所有都归结为一个事实,即这绝不是人工智能。但如果我们保持务实的态度,并思考这一切是否都如此糟糕,以及借助这项技术我们可以更快、更好地执行哪些常规操作,那么我相信我们的判断不会太苛刻。我们只需要思考如何在交易系统的开发中运用这一切。
总之在这章节里,我想把您的注意力集中在您需要时刻记住的最重要和最基本的关键点上:
- 始终仔细检查 ChatGPT 的答案,尤其是数字、方程式和生成的代码
归功于我对数学和编程的知识,我碰巧看到了许多它出错和短处的例子,故我可以说它们十分常见。从生成的文本的角度来看,这似乎微不足道,但当我们与数学或代码打交道时,即使是最微不足道的错误也会令整个解决方案毫无用处。因此,请始终仔细检查答案,纠正错误,并唤起模型注意到这些。有时它能正确回答一段时间。除其它事项外,该子段落在开发 EA 时极其实用。
利用 ChatGPT 解决数学问题及在代码中开发数学模型的机会
由于该模型是文本的,因此很容易猜到,如果我们以正确的格式编写方程式,那么它就会理解它们,执行数学转换,并解决问题。在开发众多交易系统时,您在创建数学方程式和表达式时需要帮助。它可以解决一些可能的数学问题,并在代码中实现它们。编写数学方程式要按以下格式,ChatGPT 可以理解,并用它来给出答案:
- LaTeX
使用 latex 格式编写方程式的示例
代码:
E &=& mc^2\\ m &=& \frac{m_0}{\sqrt{1-\frac{v^2}{c^2}}}
将其粘贴到任何免费的 latex 转换器中,并得到所有熟知表达式的可视化数据:
我认为,现在很清楚如何直观地解释 LaTeX 格式的模型答案。最重要的是,如果答案包含数学表达式,请不要忘记要求模型以这种格式生成方程式。还有一些神经网络有能力将图片或其它格式的方程式转换回我们需要的格式。我想,如果您需要这些工具,您会找到它们。我的任务是向您展示这种可能性的存在。
有些 Telegram 机器人结合了许多神经网络,包括 ChatGPT,以及将图像转换回 LaTeX 格式的函数。如果您愿意,您可以从我的个人资料中找到其中之一。这个机器人是由我的朋友制作的,并由我亲自测试。
例如,您可以要求 ChatGPT 以数字和显式两种方式求解不等式或方程。您还可以要求求解方程组或不等式组,以及微分方程或积分方程,或执行任何所需的数学变换。不过,作为一名数学家,我可以说它并不能始终有效和理性地做到这一点,有时还会遗留未完成的任务。因此,有必要仔细检查。
当然,这个功能对于非数学家很实用。如果长时间使用,您会犯更多的错误,且您的解决方案将会越来越不合理和笨拙。不过,您能应付代码里的一些数学问题,代码通常仅使用数值方法,而所应用的数学并不太复杂。此处没有微分数学。
在 MQL4 和 MQL5 中正确生成代码的方法
这就是事情变得有趣的所在。鉴于所有品质足够高、且或多或少像点样的交易系统,其代码量都相当庞大,故值得考虑如何接洽生成此类代码的过程。此处的主要障碍是,对于您的问题,答案大小被限制在一定数量的字符以内,在多次尝试生成大型和复杂的代码之后,我得出的结论是,每段代码输出都应足够短。这意味着代码应分部分显示。我们如何才能做到这一点?答案很简单 — 我们需要制定开发 EA、指标或脚本的计划。
应按照条件拟定计划,即每个子项为一个单独的子任务,并可独立求解。然后我们可以简单地按顺序解决每个子任务,然后将所有代码合并到一起。这种方式的另一个优点是,每个子任务都可以单独完成,且由于每个子任务会比所有子任务组合在一起更简单,故最终可以更快、更舒适地执行。此外,我们将避免更多错误。
作为一名开发人员,在不让 AI 干扰的情况下独立思考 EA 的主要架构,对我来说要舒适得多。取而代之,在我的 EA 中让它去实现单独的过程。所有主要逻辑都包含在该过程当中。我们只需要用空函数实现一个近似的代码模板,然后要求它单独实现每个函数。我们还可以要求它实现函数原型或其它结构。
另一个重要的优势是,您可以要求它为 EA 或其它代码准备计划,表明您的要求,然后简单地逐个实现其计划。您最好对 EA 的算法以及它将用到哪些指标或其它方法有一个大致或准确的概念。但如果没有这样的主意,您可以先与它交谈,并要求它帮您选择 EA 策略。它会为您提供一些选项。我们参考此处施加的动作为例。
现在,我们来总结一下上面的内容,并形成简要关键子项,表征我们在从头开始构建新 EA 时将采取的可能路径。一开始,有若干种明显的可能方案可供入门:
- 我们尚未决定未来代码的架构,也不知道从哪里入手,我们也根本不知道选择哪种交易方式。
- 但我们知道主要工作代码的大致全貌,以及我们打算从 EA 中得到什么。
- 我们有一个现成的架构,对我们来说很舒适,但我们绝对不知道选择哪种交易方式。
- 我们知道我们想用的架构,对于 EA 将来的交易逻辑也有清楚地认知。
作为规则,一切都将简化为类似的构造。如果我们如下进行,这四点都可以应用于构建任何交易系统的一般设想方案:
- 如果我们不知道架构(主代码或框架),那么首先我们需要实现它,然后实现确保该框架功能的一切。
这也许意味着,例如,我们可以要求实现类、输入变量、字段和方法原型、接口、以及 EA 的主要交易功能,这些功能将使用我们描述的实体。在正确的 ChatGPT 处理下,以这样方式实现代码可以做到,譬如说,不超过字符总数的 5-10%。在这种情况下,我们可以快速实现它,然后转入过程实现,这将包含整体代码的大约 90%。这些过程将以同样简单的方式实现,因为它们会有很多,而且它们会变得非常小、易于执行。当然,当您拥有现成的模板,它就容易得多,并且您不必实现所有这些,但这需要足够的知识和经验。
利用 ChatGPT 开发交易系统
我相信我已经为您提供了足够的理论信息。现在是时候应用它了。就我而言,我使用一个现成的模板作为 EA 的基础。我在之前的一篇文章中讲述过这种范式。其特点是它提供众多金融产品的并行交易,且是在单个图表上激活。它已经具备所有必要的交易功能和主要架构。我将严格遵照 ChatGPT 的建议构建一个交易系统。我将自行实现 EA 的主要交易逻辑,以及可视化组件,因为这只需要我很少的精力。
当您开始与 ChatGPT 交互时,您会意识到会花费更多精力试图向它解释需要做什么,并且在实现一些请求和任务时您还要纠正它的答案上百次。过了片刻,您就能感觉到哪些问题值得问、哪些不值得。从此您只设置那些最终会为您节省时间的任务,不再浪费时间。这是一条相当细微的分寸,您必须亲自感受 — 别无他法。一切都在实践中学习。由于这些考量,我设计 EA 的方式已经完全成型。
首先,我要求描述 EA 的基础 — 它的工作原理是什么,以及它使用什么方法或指标(我允许模型自行决定使用任何可用信息)。同时,我声明我只需要以可读的形式从中获取逻辑条件,我可以遵照以下四个预案自行实现:
- 开立做多持仓。
- 多头平仓。
- 开立做空持仓。
- 空头平仓。
为了实现这些预案,该模型为我提供了以下条件逻辑:
- 当前价格锁定在 EMA 上方,当前价格与 EMA 之间的差值小于 ATR * 比率,且 RSI 小于 30。
- 当前价格收于 SMA 下方,或当前价格收于布林带指标的上轨上方。
- 当前价格锁定在 EMA 下方,当前价格与 EMA 之间的差值小于 ATR * 比率,且 RSI 大于 70。
- 当前价格锁定在 SMA 上方,或当前价格锁定在布林带指标的下轨下方。
显然,这些布尔条件在成功时返回 “true”,在失败时返回 “false”。这些信号值对于市价单交易来说已经足够了。在此,我想把您的注意力引至这种新兴逻辑的可能性。为此,我们可以如下进行:
- [K1] — RSI 数值较低区域
- [K2 = 100 - K1] — RSI 数值上限区域
这些表达式可用于扩展算法的灵活性,其随后会对 EA 优化的效率产生积极影响:
- 当前价格锁定在 EMA 上方,当前价格与 EMA 之间的差值小于 ATR * 比率,且 RSI 小于 K1
- 当前价格锁定在 SMA 下方,或当前价格收于布林带指标的上轨上方
- 当前价格收于 EMA 下方,当前价格与 EMA 之间的差值小于ATR*比率,且 RSI 大于 K2
- 当前价格锁定在 SMA 上方,或当前价格锁定在布林带指标的下轨下方
我之所以提供这个示例,在于您应该毫不犹豫地扩展模型,这很明显,所讨论的解决方案只是众多更扩展算法的一个特例。即使您不知道这样的扩展可以为您提供什么,然而通过做这些,您至少可以提高算法的灵活性,从而可以对其进行更精细的调整,并可能提高其效率。
考虑到需要实现哪些条件,我们需要以下两个选项之一来实现以下指标:
- SMA — 标准移动平均线(一条线)
- EMA — 指数移动平均线(一条线)
- Bollinger Bands — 布林带(一组三条线)
- RSI — 相对强弱指数(单独窗口中的一条线)
- ATR — 平均真实范围(单独窗口中的一条线)
指标可以使用特殊的预定义 MQL5 函数来实现,但我不喜欢这种方法,因为所实现的代码会更难转换为 MQL4 版本。此外,对我来说,将它整合到我经常使用的其它语言项目中会更加困难。长期以来,我在做每一件事时一直习惯于尽可能地简单,并考虑到未来的使用。我认为,这是一个非常好的习惯。
第二个要点是,常规而言,此类指标当中非必要及冗余的计算和功能很拖累。此外,不可能优调此类指标,因为它们的功能是在代码级别严格设置的。为了进行修改,在任何情况下,您都需要创建自己的指标版本。我认为这很明显,最好在 EA 或脚本中有一个自定义实现。为了实现这样的指标,我想出了以下技巧:
- 创建存储指标值的数组 (仅限于最后 N 根柱线)。
- 在出现新柱线时实现数组值平移。
- 实现在出现错误,或长时间断开连接时清除指标值数组。
- 在最后一根柱线收盘时实现指标值的计算。
在此方式中,前三段为提供的所列动作创建公共数组模块和函数。我们以自己的任务为例,看看这是什么样子的。我们从第一点开始:
double SMA1Values[]; // Array for storing SMA values double EMAValues[]; // Array for storing EMA values (exponential) double RSIValues[]; // Array for storing RSI values double BollingerBandsUpperValues[]; // Array for storing BollingerBands values, upper double BollingerBandsMiddleValues[]; // Array for storing BollingerBands values, middle double BollingerBandsLowerValues[]; // Array for storing BollingerBands values, lower double ATRValues[];// array for storing Average True Range values
在 EA 开始时,按给定长度限制初始化这些数组:
//Prepare indicator arrays void PrepareArrays() { ArrayResize(SMA1Values, LastBars); ArrayResize(EMAValues, LastBars); ArrayResize(RSIValues, LastBars); ArrayResize(BollingerBandsUpperValues, LastBars); ArrayResize(BollingerBandsMiddleValues, LastBars); ArrayResize(BollingerBandsLowerValues, LastBars); ArrayResize(ATRValues, LastBars); }
不像传统指标,这个策略我们不需要所有以前的数值作为拖累。这绝对是一个优势。我喜欢这种实现范式,因为它确保了代码的简单化,以及指标的起始值与从前一个得到的值两者等效。现在我们来看看数值平移是何样的:
//shift of indicator values void ShiftValues() { int shift = 1; for (int i = LastBars - 1; i >= shift; i--) { SMA1Values[i] = SMA1Values[i - shift]; EMAValues[i] = EMAValues[i - shift]; RSIValues[i] = RSIValues[i - shift]; BollingerBandsUpperValues[i] = BollingerBandsUpperValues[i - shift]; BollingerBandsMiddleValues[i] = BollingerBandsMiddleValues[i - shift]; BollingerBandsLowerValues[i] = BollingerBandsLowerValues[i - shift]; ATRValues[i] = ATRValues[i - shift]; } }
如您所见,一切都非常简单。这同样应用在清除数组:
//reset all indicator arrays if connection fails [can also be used when initializing an EA] void EraseValues() { for (int i = 0; i < LastBars; i++) { SMA1Values[i] = -1.0; EMAValues[i] = -1.0; RSIValues[i] = -1.0; BollingerBandsUpperValues[i] = -1.0; BollingerBandsMiddleValues[i] = -1.0; BollingerBandsLowerValues[i] = -1.0; ATRValues[i] = -1.0; } }
我认为,何处使用这个功能是很清楚的。现在我们转入指标本身的实现。为此,我要求 ChatGPT 实现相应的函数,其应与我的代码构建范式相适。我从 SMA 指标开始:
//1 Function that calculates the indicator value to bar "1" double calculateMA(int PeriodMA,int Shift=0) { int barIndex=Shift+1;//bar index SMA is calculated for (with a shift) int StartIndex=barIndex + PeriodMA-1;//starting bar index for calculating SMA if (StartIndex >= LastBars) return -1.0; // Check for the availability of the bars for calculating SMA (if not valid, then the value is -1) double sum = 0.0; for (int i = StartIndex; i >= barIndex; i--) { sum += Charts[chartindex].CloseI[i]; } LastUpdateDateTime=TimeCurrent(); return sum / PeriodMA; }
如您所见,该函数非常简短。最初,这个函数的外观有点不同。在第一代中,我发现其中有很多错误,例如,事实上,它误解了柱线编号相对于时间的方向,等等。但经过一些操纵后,我修复了所有这些,并额外添加了 Shift 参数,在原始实现中没有它。在实现了一些视觉改进之后,我要求以类似的方式实现其余的指标。此后,在其实现中错误更少了。我刚刚发送了以下请求,要求为另一个指标实现类似的函数,在问题上下文中包括以前的实现示例。这节省了大量时间。现在我们来看看它对所有其余指标的后续实现。我们从 EMA 开始:
//2 Function that calculates the value of the exponential moving average to bar "1" double calculateEMA(int PeriodEMA,double Flatness=2.0,int Shift=0) { int barIndex = Shift+1; // bar index EMA is calculated for (with a shift) int StartIndex=barIndex + PeriodEMA-1;//index of the starting bar for calculating the first SMA, for starting the recurrent calculation of EMA if (StartIndex >= LastBars) return -1.0; // Check for the availability of the bars for calculating EMA (if not valid, then the value is -1) double sum = 0.0; double multiplier = Flatness / (PeriodEMA + 1); // Weight multiplier double prevEMA; // Calculate the initial value for the EMA (the first value is considered as a normal SMA) for (int i = StartIndex; i >= barIndex; i--) { sum += Charts[chartindex].CloseI[i]; } prevEMA = sum / PeriodEMA;//this is the starting value for the bar (StartIndex-1) // Apply the EMA formula for the remaining values for (int i = StartIndex; i >= barIndex; i--) { prevEMA = (Charts[chartindex].CloseI[i] - prevEMA) * multiplier + prevEMA; } LastUpdateDateTime = TimeCurrent(); return prevEMA; }
顺便说一句,在研究 ChatGPT 迭代时,我们必须求助于各种互联网资源,以便了解哪个指标基于哪种思路。这令我们更有能力。这在 EMA 中尤为明显。如果我们仔细查看代码,我们会发现它与 SMA 的更简单实现没有太多区别,且看起来像是常见移动平均线的添加物。这里没有指数,尽管出于某种原因它存在于指标名称之中。接下来是 RSI 指标:
//3 Function for calculating RSI to bar "1" double calculateRSI(int PeriodRSI,int Shift=0) { int barIndex = Shift+1; // bar index RSI is calculated for (with a shift) int StartIndex = barIndex + PeriodRSI - 1; // starting bar index for calculating RSI if (StartIndex >= LastBars) return -1.0; // Check for the availability of the bars for calculating RSI (if not valid, then the value is -1) double avgGain = 0.0; double avgLoss = 0.0; double change; // Calculate initial values for avgGain and avgLoss for (int i = StartIndex; i > barIndex; i--) { change = Charts[chartindex].CloseI[i]-Charts[chartindex].OpenI[i]; if (change > 0) { avgGain += change; } else if (change < 0) { avgLoss -= change; } } avgGain /= PeriodRSI; avgLoss /= PeriodRSI; // RSI calculation double RS = 0.0; if (avgLoss != 0) { RS = avgGain / avgLoss; } double RSI = 100 - (100 / (1 + RS)); LastUpdateDateTime = TimeCurrent(); return RSI; }
现在我们需要对布林带指标执行类似的过程。该过程应当返回三条曲线的数值,并可放入初步准备的结构之中:
//structure to return all three bollinger band lines struct BollingerBands { double upper; double middle; double lower; };
现在,将该结构添加到问题的上下文中,我们就能要求实现该函数,同时考虑到它应该返回布林带类型这个事实。该模型将毫无问题地应对这样的任务:
//4 Function for calculating the Bollinger Bands of the indicator BollingerBands calculateBollingerBands(int PeriodBB, double DeviationBB,int Shift=0) { int barIndex = Shift+1; // bar index Bollinger Bands is calculated for (with a shift) int StartIndex = barIndex + PeriodBB - 1; // index of the starting bar for calculating the first SMA, for starting the recurrent calculation of EMA BollingerBands rez; rez.lower=-1.0; rez.middle=-1.0; rez.upper=-1.0; if (StartIndex >= LastBars) return rez; // Check for the availability of the bars for calculating BB (if not valid, then the value is -1) double sum = 0.0; double prevBB; double sumSquares = 0.0; // Calculate the initial value for BB (the first value is considered as a normal SMA) for (int i = StartIndex; i >= barIndex; i--) { double closePrice = Charts[chartindex].CloseI[i]; sum += closePrice; } prevBB = sum / PeriodBB; //this is the starting value for the bar (StartIndex-1) // Calculation of standard deviation for (int i = StartIndex; i >= barIndex; i--) { double closePrice = Charts[chartindex].CloseI[i]; sumSquares += pow(closePrice - prevBB, 2); } double standardDeviation = sqrt(sumSquares / PeriodBB); // Calculate Bollinger Bands double upperBand = prevEMA + DeviationBB * standardDeviation; double lowerBand = prevEMA - DeviationBB * standardDeviation; rez.upper = upperBand; rez.middle = prevEMA; rez.lower = lowerBand; LastUpdateDateTime = TimeCurrent(); return rez; }
现在剩下的就是实现计算 ATR 的函数版本:
//5 Function for calculating Average True Range (Relative) double calculateRelativeATR(int PeriodATR,int Shift=0) { int barIndex = Shift+1; // bar index ATR is calculated for (with a shift) int StartIndex = barIndex + PeriodATR - 1; // starting bar index for calculating the first ATR if (StartIndex >= LastBars) return -1.0; // Check for the availability of the bars for calculating ATR and True Range (if not valid, then the value is -1) double sumPrice=0.0; double sumTrueRange = 0.0; double ATR; // Calculating True Range for bars and the sum of values for calculating the first ATR for (int i = StartIndex; i >= barIndex; i--) { sumPrice+=Charts[chartindex].HighI[i]+Charts[chartindex].LowI[i]+Charts[chartindex].CloseI[i]+Charts[chartindex].OpenI[i];//added by me double high = Charts[chartindex].HighI[i]; double low = Charts[chartindex].LowI[i]; double trueRange = high - low; sumTrueRange += trueRange; } // ATR calculation //ATR = sumTrueRange / PeriodATR; - conventional calculation ATR = 100.0 * (sumTrueRange / PeriodATR)/(sumPrice/(PeriodATR*4.0));//calculation of relative ATR in % LastUpdateDateTime = TimeCurrent(); return ATR; }
此处要注意末尾的注释行。我稍微修改了这个指标,如此它即可操作相对值。这是必要的,这样我们就不必为每种交易金融产品设置自己的权重。代之,他会基于当前价格自动发生。这令多币种优化更有效。我们将需要它来证明这样一个事实,即使这只是一个简单的算法,若运用得当,也可以为我们提供一个小型但足够的前向交易区间。结合其它高效方法,即使在 EA 目前的能力水平上,交易也可以被接受。
我已自行实现了这些预案。这很容易。我们来看看其中之一,比如说第一个:
//to open buy positions bool bBuy() { //determine if an open position is already present bool ord; ulong ticket; bool bOpened=false; for ( int i=0; i<PositionsTotal(); i++ ) { ticket=PositionGetTicket(i); ord=PositionSelectByTicket(ticket); if ( ord && PositionGetInteger(POSITION_MAGIC) == MagicF) { bOpened=true; return false; } } if (!bOpened && EMAValues[1] > 0.0)//only if nothing is open and the indicator has been calculated { //K - control ratio //RSIPercentBorder - control RSI double Val1=Charts[chartindex].CloseI[1]-EMAValues[1]; double Val2=ATRValues[1]*(1.0/K); if (Val1 > 0 && Val1 < Val2 && RSIValues[1] < RSIPercentBorder) return true; } return false; }
开立多头持仓的预案是相似的,只有略微例外。平仓预案甚至更简单:
//to close a buy position bool bCloseBuy() { if (SMA1Values[1] > 0.0) { if (Charts[chartindex].CloseI[1] < SMA1Values[1] || Charts[chartindex].CloseI[1] > BollingerBandsUpperValues[1] ) { return true; } } return false; }
所有这些都会以非常简单的方式操作:
IndicatorDataRecalculate();//recalculate indicators if ( bCloseBuy() ) { CloseBuyF(); } if ( bCloseSell() ) { CloseSellF(); } if ( bBuy() ) { BuyF(); } if ( bSell() ) { SellF(); }
我认为,它应尽可能地简单,没必要那么复杂。所有代码都应在新柱线出现时才会执行。我还单独实现了指标的可视化。唯一我不太喜欢的事,像 ATR 和 RSI 这样的指标被设计为在单独的窗口中绘制。我为它们制作了自己的渲染版本,如此这般它们就能与价格挂钩,由于不能人为地创建单独的窗口,坦率地说,并不真的需要单独窗口。为了实现这一点,我创建了一个绘制窗口指标的特定范式。
- 输入百分比控制值,以便从一条道路中创建三条走廊。
- 判定指标的最大(Max)和最小(Min)值,作为存储数值的整个数组的尺寸。
- 计算给定走廊的增量(Delta = Max - Min)。
- 计算增加值的上走廊(HighBorder = Max - Delta * Percent / 100)。
- 计算增加值的下走廊(LowBorder = Min + Delta * Percent / 100)。
- 因为上走廊和下走廊都已经定义好,故中间走廊业已定义。
如果指标的当前值坐落于其中一条走廊当中,则该点将获得与走廊相对应的相应颜色。一切都很简单。这就是我们如何将数值绑定到图表柱线,例如,只需相应地更改它们的颜色,或者只是创建链接到柱线图的对象,并拥有相应颜色。正如许多人可能已经注意到的那样,我受到 RSI 指标的启发,因为这种特殊的结构通常用于交易。这些区域所在被称为超买和超卖区域。
我认为此处的这段代码并不那么重要,因为它与实现我们的任务只是稍有关系,仅在纠正可能的错误和改进方面有所帮助。如果您愿意,您甚至可以利用 ChatGPT 实现此渲染。不过,我认为展示渲染如何工作是值得的:
于此,仅需 Line 对象类型的帮助,即可尽可能简单地完成一切。如果在创建一条曲线时,这条线的起点和终点坐落于相同的时间和价格,那么这条线就会化为一个点。通过调整线条粗细,我们即可调整相应点的粗重。这些只是我的一些生活小窍门,我已经运用了很长时间。
功能评估和结果分析
尽管 ChatGPT 认为这种算法是最优的,但我们不知道这些决定是基于什么。优良的回测或真实交易或可作为效率的衡量标准。我希望每个人都明白,真实交易应该按照最优设置执行,利用 MetaTrader 5 优化器可完成优化。由于事实上该终端具有多币种优化的可能性,以及我的模板给定的能力,它可充分利用该工具的效力,故我们可以安全地基于所有 “28” 个货币对来优化 EA。也许,不值得罗列它们。但值得注意的是这种方式所具有的明显优势:
- 自动搜索多币种形态
- 多币种形态具有更多的权重,并进一步适应市场变化
- 其明示会有更多交易,因为每种交易的金融产品都各自提供了东西
- 节省时间(您不必再单独优化每个金融产品)
当然,也有缺点。在这种情况下,最重要的一点是无法对每种金融产品进行优调。当然,这个问题可以通过引入附加功能来解决,但这不是本文的主题。但仅仅执行优化是不够的,从它为您提供的整个范围内正确选择结果也很重要。我选择最优之一:
正如您所见,我采用 “H4” 时间帧,优化从 “2018” 到 “2023”,且我把 “2023” 的所有六个月都留作前向验算。正如我们所见,尽管优化部分的盈利曲线远非完美,但我们得到了数月盈利交易,这一事实意味着该策略并非没有意义,且有成功用于交易的潜力。许多可优化的交易系统大多、甚至无法接近这个结果。有时,您测试一些难以置信的超酷系统代码,却也没有得到这样的结果。
我会在这个算法中添加更多内容。我认为这是一个游乐场。但值得一提的是,这其实根本不重要,出品 EA 的扩展潜力才是。于此扮演角色的不是拟议的算法,而是您的能力和创造力。您需要具备的重要素质,才能成功整合到 MQL5 开发流程:
- 编程技能(必修)
- 数学技能(期望)
- 模块思维
- 简化任务并将它们分解为不同阶段的能力
- 要明白事实,ChatGPT 只是一个工具,故没有必要将不起作用的东西归咎于它(修复任何不起作用的东西,取决于您)
- 正确解释获得的交易结果
- 意识到模型无论如何都会犯错,但这不应该打扰您(主要是降低了您的开发劳动力成本)
- 开发自己的应用程序结构(这不是必需的,当然,您可采用我的方式)
- ChatGPT 是您的伙伴,其强度依赖于您。您越有耐心、越聪明、越足智多谋,这个伙伴就越有效。
您已拥有一个工具,您可以自行决定是否使用,但我可以肯定地说,这个工具在很多方面对我都非常有用,包括编程。
结束语
事实上,使用这个工具时没有必要执着于我的模型。我用它尝试了很多东西,我可以说可能性几乎是无限的。当然,在您成功之前,您肯定会像我一样花费一些时间和精力。
但我仍然建议您尝试此工具,腾出一些时间慢慢来。最起码,使用 ChatGPT 会令您对人工智能有正确的态度。有可能将这项技术集成到几乎任何开发链中。我相信这仅仅是个开始,变化指日可待。我建议您尽快开始熟悉此工具,并尝试在更广泛的任务范围里加以应用。
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/12475