• QQ空间
  • 回复
  • 收藏

开发多模块智能交易系统的步骤

gyshssl 数据策略 2019-9-5 09:35 98781人围观

概述

目前,有若干种编程方法: 模块化,面向对象和结构化。 在本文中,我们将讨论有关交易机器人的模块化编程。

模块化编程是一种程序开发方法,涉及到将程序拆分为独立的模块。


模块化编程的主要原则是 "切分和规则"。 使用模块化架构的便利性在于它具有更新 (替换) 模块时无需更改系统其余部分的能力。

模块化编程的核心有三个基本概念。

  • 帕纳斯 (Parnas) 掩盖信息原则。 模块用于隐藏信息,并形成用于解决特定问题的算法。 模块以后可以用另一个模块替换。
  • 科恩 (Cohen) 的模块化公理。 模块是执行程序特定功能的独立程序单元。
  • Tseytin 的装配编程。 模块是组成程序的 "砖块"。

唯一可替代模块化的是单体程序。 虽然这不是很方便。 如果您需要更改或补充某些程序函数,您需要编辑智能交易系统的代码,在大多数情况下,这可以由代码作者或其他有经验的程序员来完成。 此外,如果是编译单体程序,则只能由版权所有者来编辑。 由您自己或聘请第三方开发人员来更行重要程序函数应该会更方便。

图例 1. 模块化交易机器人的抽象示意图

多模块化原理

模块化编程是将任务切分成众多子任务的技术,这些子任务作为单独的模块 (文件) 实现。 通常,程序模块是单独的程序,或功能完备且自主编译的程序单元以某种方式与被调用模块识别并组合。 换言之,模块是已完成的程序功能片段,是为其它程序中使用而设计开发的单独编译文件。

在确定实现特定算法功能的模块集合时,应考虑以下因素:

  • 每个模块都由父模块调用执行,并在完成其工作后,将控制权返回给调用它的模块;
  • 算法中的主要决策是在层次结构中的最高级别进行;
  • 模块在数据方面彼此独立;
  • 模块不依赖于访问它们的历史。

总结以上所有内容,模块化程序其逻辑结构的任何部分都可以在不引发其它部分变化的情况下进行变更。

主要模块参数:

  • 一个输入和一个输出 — 在输入端,程序模块接收一组初始数据,处理它们并返回一组结果数据,从而实现 IPO 原则 (输入-处理-输出);
  • 功能完整性 — 为了执行单独的功能,模块执行所规定操作的完整列表,足以彻底完成处理;
  • 逻辑独立 — 程序模块的结果仅依赖于源数据。 它不依赖于其它模块的操作;
  • 与其它程序模块弱数据链接 — 模块之间的数据交换应尽可能减少。

MQL5 语言可以开发三类程序: 智能交易系统, 指标或脚本。 管理所有交易功能的模块最适合作为智能交易系统的主模块。而其它模块作为指标实现。 适于作为指标形式的模块: 用给定算法计算的数据可以存储在指标缓冲区中,并在必要时传递给多模块智能交易系统。 反之,智能交易系统可以依据任务选用或忽略这些数据。 在某些项目中,使用智能交易系统作为外部模块是合理的,但与此同时,有必要详细考虑数据交换机制。

许多如您一样的人肯定在自己的智能交易系统中应用了模块化技术: 例如,自定义指标 作为生成和整理交易信号的模块。

而我认为,最合理的解决方案如此这般: 所有基本功能都集中在主模块中,且不需要外部参与。 反之,所需的外部模块只是用来适应不同的市场条件并改善交易策略。 程序功能集合应由用户确定,而不是由代码或策略开发者。 重点要注意,不可有任何人违反彼此的合法权利。

主模块 — 智能交易系统

主模块是整个项目的管控所在,是智能交易系统层次结构中最重要的模块。 它应该包含交易功能。 没有它们,任何交易策略都是毫无意义的。

我们使用代码库的特定 示例 来开发多模块智能交易系统。 初始智能交易系统依据 iBands 指标通道以固定手数进行交易,它会在通道边界上逆向开仓。 该智能交易系统完全自给自足,不需要任何外部程序。

并非每个智能交易系统都是多模块的。


我们应该将什么添加到代码中以便将其转换为模块项目?

  • 声明用户随后要用到的外部模块 (指标)。
  • 为集成添加必要的功能。
  • 为外部模块开发人员准备文档 (启用在单独文件中生成文档的功能)。 开发外部模块可能需要有关正确使用主模块数据结构的信息。 例如,在此示例中,资金管理模块应将手数大小传递给智能交易系统,且将距离当前价格的点数传递给持仓跟踪模块。

变换的结果就是,我们获得了模块化智能交易系统,允许集成多达七个外部模块。

  • 模块 1 — 资金管理模块。 它提供手数。
  • 模块 2 — 跟踪持仓并设置止损。 它提供距开仓价格的止损点数。
  • 模块 3 — 跟踪持仓并设置止盈。 它提供距开仓价格的止盈点数。
  • 模块 4 — 跟踪持仓并设置尾随停止。 它提供距当前价格的止损点数。
  • 模块 5 — 交易信号生成。 它提供一个信号值。
  • 模块 6 — 用于整理交易信号的模块。 它提供过滤值。
  • 模块 7 — 跟踪持仓并设置盈亏平衡。 它提供距开仓价格的止损点数。

图例 2. OnInit() 函数和初始化外部模块



图例 3. OnTick() 函数和从外部模块读取数据



图例 4. OnTrade() 函数和从外部模块读取数据



图例 5. 用于生成交易信号以及从外部模块读取数据的函数

  1. //****** project (module expert): test_module_exp.mq5
  2. //+------------------------------------------------------------------+
  3. //|                                         程序代码由模块化项目生成器生成 |
  4. //|                         版权所有 2010-2017, Sergey Pavlov (DC2008) |
  5. //|                              http://www.mql5.com/en/users/dc2008 |
  6. //+------------------------------------------------------------------+
  7. #property copyright "版权所有 2010-2017, Sergey Pavlov (DC2008)"
  8. #property link      "http://www.mql5.com/en/users/dc2008"
  9. #property link      "1.00"
  10. #property link      "多模块智能系统示例: 项目 TEST 主模块。"
  11. #property link      "该项目使用 7 个外部模块。"
  12. //---
  13. #include <Trade\Trade.mqh>
  14. //---
  15. MqlTick    last_tick;
  16. CTrade     trade;
  17. //---
  18. input int                  e_bands_period=80;            // 移动均线周期
  19. int                        e_bands_shift=0;              // 位移
  20. input double               e_deviation=3.0;              // 标准偏差
  21. input ENUM_APPLIED_PRICE   e_applied_price=PRICE_CLOSE;  // 价格类型
  22. input bool                 on_module=false;              // 是否使用插件
  23. //---
  24. double lot=0.01;           // 固定手数
  25. double min_lot=0.01;       // 最低允许手数
  26. bool   on_trade=false;     // 交易功能标志
  27. //--- 用于存储 iBands 指标句柄的变量
  28. int    handle_Bands;
  29. //--- 模块 1
  30. bool   on_lot=false;
  31. int    handle_m1;
  32. //--- 模块 2
  33. bool   on_SL=false;
  34. int    handle_m2;
  35. //--- 模块 3
  36. bool   on_TP=false;
  37. int    handle_m3;
  38. //--- 模块 4
  39. bool   on_Trail=false;
  40. int    handle_m4;
  41. //--- 模块 5
  42. bool   on_signals=false;
  43. int    handle_m5;
  44. //--- 模块 6
  45. bool   on_Filter=false;
  46. int    handle_m6;
  47. //--- 模块 7
  48. bool   on_Breakeven=false;
  49. int    handle_m7;
  50. //+------------------------------------------------------------------+
  51. //| 交易信号结构                                                       |
  52. //+------------------------------------------------------------------+
  53. struct sSignal
  54.   {
  55.    bool              Buy;    // 买入信号
  56.    bool              Sell;   // 卖出信号
  57.   };
  58. //+------------------------------------------------------------------+
  59. //| 交易信号生成器                                                      |
  60. //+------------------------------------------------------------------+
  61. sSignal Buy_or_Sell()
  62.   {
  63.    sSignal res={false,false};
  64. //--- 模块 5
  65.    if(on_signals)
  66.      { // 如果有额外模块
  67.       double buffer_m5[];
  68.       ArraySetAsSeries(buffer_m5,true);
  69.       if(CopyBuffer(handle_m5,0,0,1,buffer_m5)<0) return(res);
  70.       if(buffer_m5[0]<-1) res.Sell=true;
  71.       if(buffer_m5[0]>1) res.Buy=true;
  72.      }
  73. //--- 模块 6
  74.    if(on_Filter)
  75.      { // 如果有额外模块
  76.       double buffer_m6[];
  77.       ArraySetAsSeries(buffer_m6,true);
  78.       if(CopyBuffer(handle_m6,0,0,1,buffer_m6)<0) return(res);
  79.       lot=buffer_m6[0];
  80.       if(buffer_m6[0]<1) res.Buy=false;
  81.       if(buffer_m6[0]>-1) res.Sell=false;
  82.      }
  83. //---
  84. //--- 指标缓存区
  85.    double         UpperBuffer[];
  86.    double         LowerBuffer[];
  87.    double         MiddleBuffer[];
  88.    ArraySetAsSeries(MiddleBuffer,true); CopyBuffer(handle_Bands,0,0,1,MiddleBuffer);
  89.    ArraySetAsSeries(UpperBuffer,true);  CopyBuffer(handle_Bands,1,0,1,UpperBuffer);
  90.    ArraySetAsSeries(LowerBuffer,true);  CopyBuffer(handle_Bands,2,0,1,LowerBuffer);
  91. //--- 时间序列
  92.    double L[];
  93.    double H[];
  94.    ArraySetAsSeries(L,true); CopyLow(_Symbol,_Period,0,1,L);
  95.    ArraySetAsSeries(H,true); CopyHigh(_Symbol,_Period,0,1,H);
  96.    if(H[0]>UpperBuffer[0]&& L[0]>MiddleBuffer[0]) res.Sell=true;
  97.    if(L[0]<LowerBuffer[0] && H[0]<MiddleBuffer[0]) res.Buy=true;
  98. //---
  99.    return(res);
  100.   }
  101. //+------------------------------------------------------------------+
  102. //| 智能系统初始化函数                                                  |
  103. //+------------------------------------------------------------------+
  104. int OnInit()
  105.   {
  106. //--- 创建指标句柄
  107.    handle_Bands=iBands(_Symbol,_Period,e_bands_period,e_bands_shift,e_deviation,e_applied_price);
  108.    if(handle_Bands==INVALID_HANDLE)
  109.       return(INIT_FAILED);
  110.    else
  111.       on_trade=true;
  112.    if(on_module)
  113.      {
  114.       //--- 模块 1
  115.       //--- 检查: 是否有外部模块?
  116.       handle_m1=iCustom(NULL,0,"Market\\test_module_MM");
  117.       if(handle_m1!=INVALID_HANDLE)
  118.          on_lot=true;
  119.       //--- 模块 2
  120.       //--- 检查: 是否有外部模块?
  121.       handle_m2=iCustom(NULL,0,"Market\\test_module_SL");
  122.       if(handle_m2!=INVALID_HANDLE)
  123.          on_SL=true;
  124.       //--- 模块 3
  125.       //--- 检查: 是否有外部模块?
  126.       handle_m3=iCustom(NULL,0,"Market\\test_module_TP");
  127.       if(handle_m3!=INVALID_HANDLE)
  128.          on_TP=true;
  129.       //--- 模块 4
  130.       //--- 检查: 是否有外部模块?
  131.       handle_m4=iCustom(NULL,0,"Market\\test_module_Trail");
  132.       if(handle_m4!=INVALID_HANDLE)
  133.          on_Trail=true;
  134.       //--- 模块 5
  135.       //--- 检查: 是否有外部模块?
  136.       handle_m5=iCustom(NULL,0,"Market\\test_module_signals");
  137.       if(handle_m5!=INVALID_HANDLE)
  138.          on_signals=true;
  139.       //--- 模块 6
  140.       //--- 检查: 是否有外部模块?
  141.       handle_m6=iCustom(NULL,0,"Market\\test_module_Filter");
  142.       if(handle_m6!=INVALID_HANDLE)
  143.          on_Filter=true;
  144.       //--- 模块 7
  145.       //--- 检查: 是否有外部模块?
  146.       handle_m7=iCustom(NULL,0,"Market\\test_module_Breakeven");
  147.       if(handle_m7!=INVALID_HANDLE)
  148.          on_Breakeven=true;
  149.      }
  150. //--- 交易操作允许的最小交易量
  151.    min_lot=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
  152.    return(INIT_SUCCEEDED);
  153.   }
  154. //+------------------------------------------------------------------+
  155. //| 智能系统逐笔报价函数                                                 |
  156. //+------------------------------------------------------------------+
  157. void OnTick()
  158.   {
  159.    double Equity=AccountInfoDouble(ACCOUNT_EQUITY);
  160. //--- 模块 1
  161.    if(on_lot)
  162.      { // 如果有额外模块
  163.       double buffer_m1[];
  164.       ArraySetAsSeries(buffer_m1,true);
  165.       if(CopyBuffer(handle_m1,0,0,1,buffer_m1)<0) return;
  166.       lot=buffer_m1[0];
  167.      }
  168. //--- 模块 4
  169.    if(on_Trail)
  170.       if(PositionSelect(_Symbol))
  171.          if(PositionGetDouble(POSITION_PROFIT)>0)
  172.            { // 如果有额外模块
  173.             double buffer_m4[];
  174.             ArraySetAsSeries(buffer_m4,true);
  175.             if(CopyBuffer(handle_m4,0,0,1,buffer_m4)<0) return;
  176.             double TR=buffer_m4[0];
  177.             if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
  178.                if(PositionGetDouble(POSITION_PRICE_CURRENT)-TR*_Point>PositionGetDouble(POSITION_PRICE_OPEN))
  179.                  {
  180.                   double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)-TR*_Point;
  181.                   if(price_SL>PositionGetDouble(POSITION_SL))
  182.                      trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),0);
  183.                  }
  184.             if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
  185.                if(PositionGetDouble(POSITION_PRICE_CURRENT)+TR*_Point<PositionGetDouble(POSITION_PRICE_OPEN))
  186.                  {
  187.                   double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)+TR*_Point;
  188.                   if(price_SL<PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL)==NULL)
  189.                      trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),0);
  190.                  }
  191.            }
  192. //--- 模块 7
  193.    if(on_Breakeven)
  194.       if(PositionSelect(_Symbol))
  195.          if(PositionGetDouble(POSITION_PROFIT)>0)
  196.            { // 如果有额外模块
  197.             double buffer_m7[];
  198.             ArraySetAsSeries(buffer_m7,true);
  199.             if(CopyBuffer(handle_m7,0,0,1,buffer_m7)<0) return;
  200.             double TRB=buffer_m7[0];
  201.             if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
  202.                if(PositionGetDouble(POSITION_PRICE_CURRENT)-TRB*_Point>PositionGetDouble(POSITION_PRICE_OPEN))
  203.                  {
  204.                   double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)-5*_Point;
  205.                   if(price_SL>PositionGetDouble(POSITION_SL))
  206.                      trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),PositionGetDouble(POSITION_TP));
  207.                  }
  208.             if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
  209.                if(PositionGetDouble(POSITION_PRICE_CURRENT)+TRB*_Point<PositionGetDouble(POSITION_PRICE_OPEN))
  210.                  {
  211.                   double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)+5*_Point;
  212.                   if(price_SL<PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL)==NULL)
  213.                      trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),PositionGetDouble(POSITION_TP));
  214.                  }
  215.            }
  216. //---
  217.    if(lot<min_lot) lot=min_lot;
  218. //---
  219.    if(on_trade)
  220.      {
  221.       sSignal signal=Buy_or_Sell();
  222.       //--- 所需以及可用保证金的值
  223.       double margin,free_margin=AccountInfoDouble(ACCOUNT_MARGIN_FREE);
  224.       //--- 买入
  225.       if(signal.Buy)
  226.         {
  227.          if(!PositionSelect(_Symbol))
  228.            {
  229.             SymbolInfoTick(_Symbol,last_tick);
  230.             if(OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,NormalizeDouble(lot,2),last_tick.ask,margin))
  231.                if(margin<Equity)
  232.                   trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,NormalizeDouble(lot,2),last_tick.ask,0,0,"买入: 新仓");
  233.            }
  234.          else
  235.            {
  236.             if(PositionGetDouble(POSITION_PROFIT)<0) return;
  237.             if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
  238.               {
  239.                trade.PositionClose(_Symbol);
  240.                SymbolInfoTick(_Symbol,last_tick);
  241.                if(OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,NormalizeDouble(lot,2),last_tick.ask,margin))
  242.                   if(margin<Equity)
  243.                      trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,NormalizeDouble(lot,2),last_tick.ask,0,0,"买入: 逆向");
  244.               }
  245.            }
  246.         }
  247.       //--- 卖出
  248.       if(signal.Sell)
  249.         {
  250.          if(!PositionSelect(_Symbol))
  251.            {
  252.             SymbolInfoTick(_Symbol,last_tick);
  253.             if(OrderCalcMargin(ORDER_TYPE_SELL,_Symbol,NormalizeDouble(lot,2),last_tick.bid,margin))
  254.                if(margin<Equity)
  255.                   trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,NormalizeDouble(lot,2),last_tick.bid,0,0,"卖出: 新仓");
  256.            }
  257.          else
  258.            {
  259.             if(PositionGetDouble(POSITION_PROFIT)<0) return;
  260.             if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
  261.               {
  262.                trade.PositionClose(_Symbol);
  263.                SymbolInfoTick(_Symbol,last_tick);
  264.                if(OrderCalcMargin(ORDER_TYPE_SELL,_Symbol,NormalizeDouble(lot,2),last_tick.bid,margin))
  265.                   if(margin<Equity)
  266.                      trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,NormalizeDouble(lot,2),last_tick.bid,0,0,"卖出: 逆向");
  267.               }
  268.            }
  269.         }
  270.      }
  271.   }
  272. //+------------------------------------------------------------------+
  273. //| 交易函数                                                          |
  274. //+------------------------------------------------------------------+
  275. void OnTrade()
  276.   {
  277.    if(on_SL && on_TP) // 如果有额外模块
  278.      {
  279.       //--- 模块 2
  280.       double buffer_m2[];
  281.       ArraySetAsSeries(buffer_m2,true);
  282.       if(CopyBuffer(handle_m2,0,0,1,buffer_m2)<0) return;
  283.       double SL=buffer_m2[0];
  284.       //--- 模块 3
  285.       double buffer_m3[];
  286.       ArraySetAsSeries(buffer_m3,true);
  287.       if(CopyBuffer(handle_m3,0,0,1,buffer_m3)<0) return;
  288.       double TP=buffer_m3[0];
  289.       //--- 持仓修改
  290.       if(PositionSelect(_Symbol))
  291.          if(PositionGetDouble(POSITION_SL)==0)
  292.            {
  293.             //--- 买入
  294.             if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
  295.               {
  296.                double priceTP=PositionGetDouble(POSITION_PRICE_OPEN)+TP*_Point;
  297.                double priceSL=PositionGetDouble(POSITION_PRICE_OPEN)-SL*_Point;
  298.                trade.PositionModify(_Symbol,NormalizeDouble(priceSL,Digits()),NormalizeDouble(priceTP,Digits()));
  299.               }
  300.             //--- 卖出
  301.             if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
  302.               {
  303.                double priceTP=PositionGetDouble(POSITION_PRICE_OPEN)-TP*_Point;
  304.                double priceSL=PositionGetDouble(POSITION_PRICE_OPEN)+SL*_Point;
  305.                trade.PositionModify(_Symbol,NormalizeDouble(priceSL,Digits()),NormalizeDouble(priceTP,Digits()));
  306.               }
  307.            }
  308.      }
  309.   }
复制代码

如果程序中设置的目录未包含必要的模块 (文件),则交易机器人应用默认功能。 因此,缺少外部模块不会对智能系统的性能产生严重影响。

最重要的模块

如何从单体程序制作多模块智能系统? 模块化项目从分析一般任务和定义功能的封闭片段开始,这可在以后将其形式转化为编译模块。 在这种情况下,您需要选择可以显著改变智能交易系统操作的最典型函数,且这些函数可基于各种算法。 众所周知,大多数智能交易系统都采用相同的过程:

  • 资金 (风险) 管理模块;
  • 持仓跟踪模块 (止损和止盈);
  • 尾随停止模块;
  • 跟踪信号生成模块;
  • 信号过滤模块。

实现每个列举的模块均有很多选项。 在本文中,我们向您展示最简单的解决方案,因为模块化编程的方法对于我们来说比多字符串功能更重要。

开发辅助模块

辅助 (外部) 模块是执行特定功能并将输入数据放置到指标缓冲区的指标。 如有必要,主模块使用这些数据。 因此,智能交易系统要适应交易者运用此交易策略的需求。 相同的初始智能交易系统可为每种特定金融产品或经纪商重新搭配。 事实上,这是一个工具包,允许交易者搭配出无限数量的交易机器人。

编程是一个耗费人力的过程。 尽管它具有创造性,但它所包含许多平凡操作,也许您希望能够自动化。 除此以外,自动化可提升生产力并减少误差。

下面附带的模块生成器允许您在几秒钟内形成多达八个连接至一个多模块项目的文件。 这极大地简化并加速了开发和组装 (参见视频) 工作。

视频 1. 管理多模块项目生成器的面板

该面板允许您设置项目生成的特定模块。 在我们的示例中,创建了一个 "test" 项目。根据所选的模块组合,生成器自动生成代码,而不含不必要的版块和文件。

生成的文件将放置到 Files 文件夹中 (参见图例 2)。 "test_module_exp.mq5" 主模块的名称由项目名称 ("test") 和 "_module_exp.mq5" 前缀组成。 将其放在 Experts 文件夹中,而外部模块的其余文件应位于 Indicators\Market 之中。


图例 6. 生成的 "test" 项目文件

之后,编译所有文件并继续测试多模块项目。

视频 2. 编译 'test' 项目的生成文件
  

手工创建类似项目需要花费很多时间。 显然,我们要从主模块开始。 以后可以连接到项目的外部模块定义完毕后,继续进行开发和编程。 需要跟踪的主要内容是主模块所需等待的辅助模块的输出数据。 由于模块是指标,指标缓冲区 包含的数值均为实数类型,因此在主模块中,必须提供从实数类型到相应类型变量的转换算法。

应当设计外部模块,以便可以在没有输入的情况下在主模块中调用它们,即默认值。 这种调用机制简化了外部数据管理系统的开发。

我们来更详细地研究哪些外部模块在交易策略中最重要。

示例 1: 资金管理模块

此外部模块计算开单的手数。 您可以在下面看到计算交易量的最简单实现方法 (以存款资金的可用百分比计算):

  1. //****** project (module MM): test_module_MM_ind.mq5
  2. //+------------------------------------------------------------------+
  3. //|                                         程序代码由模块化项目生成器生成 |
  4. //|                         版权所有 2010-2017, Sergey Pavlov (DC2008) |
  5. //|                              http://www.mql5.com/en/users/dc2008 |
  6. //+------------------------------------------------------------------+
  7. #property copyright "版权所有 2010-2017, Sergey Pavlov (DC2008)"
  8. #property link      "http://www.mql5.com/en/users/dc2008"
  9. #property link      "1.00"
  10. #property link      "多模块智能系统示例: 项目 test 资金管理模块"
  11. //--- 在图表窗口中显示指标
  12. #property indicator_chart_window
  13. //--- 指标计算的缓冲区数量
  14. #property indicator_buffers 1
  15. //--- 指标中的图形序列数量
  16. #property indicator_plots   1
  17. //---
  18. input double   lot_perc=0.1;  // 净值百分比值
  19. double         Buffer1[];
  20. //+------------------------------------------------------------------+
  21. //| 自定义指标初始化函数                                                 |
  22. //+------------------------------------------------------------------+
  23. int OnInit()
  24.   {
  25. //---
  26.    ArraySetAsSeries(Buffer1,true);
  27.    SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
  28.    return(INIT_SUCCEEDED);
  29.   };
  30. //+------------------------------------------------------------------+
  31. //| 自定义指标迭代函数                                                  |
  32. //+------------------------------------------------------------------+
  33. int OnCalculate (const int rates_total,
  34.                  const int prev_calculated,
  35.                  const datetime& time[],
  36.                  const double& open[],
  37.                  const double& high[],
  38.                  const double& low[],
  39.                  const double& close[],
  40.                  const long& tick_volume[],
  41.                  const long& volume[],
  42.                  const int &spread[])
  43.   {
  44.    double Equity=AccountInfoDouble(ACCOUNT_EQUITY);
  45. //--- 依据净值计算手数
  46.    Buffer1[0]=NormalizeDouble(Equity*lot_perc/1000.0,2); // 确定手数的函数
  47.    if(Buffer1[0]<0.01) Buffer1[0]=0.01;
  48.    return(rates_total);
  49.   };
复制代码

如果我们默认调用此模块 (不设置输入参数的值),则主模块按照可用资金的 0.1% 获取允许的手数,以便执行交易。 从主程序调用此模块的示例:

  1. //+------------------------------------------------------------------+
  2. //| 智能系统逐笔报价函数                                                 |
  3. //+------------------------------------------------------------------+
  4. void OnTick()
  5.   {
  6.    double Equity=AccountInfoDouble(ACCOUNT_EQUITY);
  7. //--- 模块 1
  8.    if(on_lot)
  9.      { // 如果有额外模块
  10.       double buffer_m1[];
  11.       ArraySetAsSeries(buffer_m1,true);
  12.       if(CopyBuffer(handle_m1,0,0,1,buffer_m1)<0) return;
  13.       lot=buffer_m1[0];
  14.      }
  15.   
  16.   ...
  17.   
  18.   }
复制代码
示例 2: 持仓跟踪模块 (止损,止盈和尾随)

设置止损 (SL) 和止盈 (TP) 是跟踪持仓的方法之一。 由于不同的交易策略应用了各种放置止损和止盈计算方法的组合,因此将其分成两个模块 (止损和止盈) 的选项似乎是最实用的。 但是如果我们仍然决定在单个模块中组合止损和止盈,它们的值应该放在不同的 指标缓冲区 中。

放置止损模块:

  1. //****** project (module SL): test_module_SL_ind.mq5
  2. //+------------------------------------------------------------------+
  3. //|                                         程序代码由模块化项目生成器生成 |
  4. //|                         版权所有 2010-2017, Sergey Pavlov (DC2008) |
  5. //|                              http://www.mql5.com/en/users/dc2008 |
  6. //+------------------------------------------------------------------+
  7. #property copyright "版权所有 2010-2017, Sergey Pavlov (DC2008)"
  8. #property link      "http://www.mql5.com/en/users/dc2008"
  9. #property link      "1.00"
  10. #property link      "多模块智能系统示例: 项目 test 止损模块"
  11. //--- 在图表窗口中显示指标
  12. #property indicator_chart_window
  13. //--- 指标计算的缓冲区数量
  14. #property indicator_buffers 1
  15. //--- 指标中的图形序列数量
  16. #property indicator_plots   1
  17. //---
  18. double      Buffer1[];
  19. //+------------------------------------------------------------------+
  20. //| 自定义指标初始化函数                                                 |
  21. //+------------------------------------------------------------------+
  22. int OnInit()
  23.   {
  24. //---
  25.    ArraySetAsSeries(Buffer1,true);
  26.    SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
  27.    return(INIT_SUCCEEDED);
  28.   };
  29. //+------------------------------------------------------------------+
  30. //| 自定义指标迭代函数                                                   |
  31. //+------------------------------------------------------------------+
  32. int OnCalculate (const int rates_total,
  33.                  const int prev_calculated,
  34.                  const datetime& time[],
  35.                  const double& open[],
  36.                  const double& high[],
  37.                  const double& low[],
  38.                  const double& close[],
  39.                  const long& tick_volume[],
  40.                  const long& volume[],
  41.                  const int &spread[])
  42.   {
  43.    double SL=100; // SL in points
  44. //--- 计算止损
  45.    Buffer1[0]=SL;
  46.    return(rates_total);
  47.   };
复制代码

放置止盈模块:

  1. //****** project (module TP): test_module_TP_ind.mq5
  2. //+------------------------------------------------------------------+
  3. //|                                         程序代码由模块化项目生成器生成 |
  4. //|                         版权所有 2010-2017, Sergey Pavlov (DC2008) |
  5. //|                              http://www.mql5.com/en/users/dc2008 |
  6. //+------------------------------------------------------------------+
  7. #property copyright "版权所有 2010-2017, Sergey Pavlov (DC2008)"
  8. #property link      "http://www.mql5.com/en/users/dc2008"
  9. #property link      "1.00"
  10. #property link      "多模块智能系统示例: 项目 test 止盈模块"
  11. //--- 在图表窗口中显示指标
  12. #property indicator_chart_window
  13. //--- 指标计算的缓冲区数量
  14. #property indicator_buffers 1
  15. //--- 指标中的图形序列数量
  16. #property indicator_plots   1
  17. //---
  18. double      Buffer1[];
  19. //+------------------------------------------------------------------+
  20. //| 自定义指标初始化函数                                                 |
  21. //+------------------------------------------------------------------+
  22. int OnInit()
  23.   {
  24. //---
  25.    ArraySetAsSeries(Buffer1,true);
  26.    SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
  27.    return(INIT_SUCCEEDED);
  28.   };
  29. //+------------------------------------------------------------------+
  30. //| 自定义指标迭代函数                                                   |
  31. //+------------------------------------------------------------------+
  32. int OnCalculate (const int rates_total,
  33.                  const int prev_calculated,
  34.                  const datetime& time[],
  35.                  const double& open[],
  36.                  const double& high[],
  37.                  const double& low[],
  38.                  const double& close[],
  39.                  const long& tick_volume[],
  40.                  const long& volume[],
  41.                  const int &spread[])
  42.   {
  43.    double TP=100; // TP in points
  44. //--- 计算止盈
  45.    Buffer1[0]=TP;
  46.    return(rates_total);
  47.   };
复制代码

代码显示了计算止损和止盈数值的最明显选项 — 以点数为单位。 实际上,它们不是计算的,而是由常数设定。 数值直接在程序中指定,而不是在输入中指定。 这样做是为了展示无输入外部模块的实现。 任何新入门的程序员都可以编写这样一个 "粗略" 的代码。

我相信,OnTrade 函数是存储上述模块调用的最佳位置。 近似样子如下:

  1. //+------------------------------------------------------------------+
  2. //| 交易函数                                                          |
  3. //+------------------------------------------------------------------+
  4. void OnTrade()
  5.   {
  6.    if(on_SL && on_TP) // 如果有额外模块
  7.      {
  8.       //--- 模块 2
  9.       double buffer_m2[];
  10.       ArraySetAsSeries(buffer_m2,true);
  11.       if(CopyBuffer(handle_m2,0,0,1,buffer_m2)<0) return;
  12.       double SL=buffer_m2[0];
  13.       //--- 模块 3
  14.       double buffer_m3[];
  15.       ArraySetAsSeries(buffer_m3,true);
  16.       if(CopyBuffer(handle_m3,0,0,1,buffer_m3)<0) return;
  17.       double TP=buffer_m3[0];
  18.       //--- 持仓修改
  19.       if(PositionSelect(_Symbol))
  20.          if(PositionGetDouble(POSITION_SL)==0)
  21.            {
  22.             //--- 买入
  23.             if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
  24.               {
  25.                double priceTP=PositionGetDouble(POSITION_PRICE_OPEN)+TP*_Point;
  26.                double priceSL=PositionGetDouble(POSITION_PRICE_OPEN)-SL*_Point;
  27.                trade.PositionModify(_Symbol,NormalizeDouble(priceSL,Digits()),NormalizeDouble(priceTP,Digits()));
  28.               }
  29.             //--- 卖出
  30.             if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
  31.               {
  32.                double priceTP=PositionGetDouble(POSITION_PRICE_OPEN)-TP*_Point;
  33.                double priceSL=PositionGetDouble(POSITION_PRICE_OPEN)+SL*_Point;
  34.                trade.PositionModify(_Symbol,NormalizeDouble(priceSL,Digits()),NormalizeDouble(priceTP,Digits()));<

路过

雷人

握手

鲜花

鸡蛋
原作者: gyshssl

财经媒体撰稿人,自由投资者

关注微信