请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化平台 帖子:3365780 新帖:12

MQL 中的对象方法

今天你爆仓了吗发表于:4 月 17 日 20:05回复(1)

简介

本文对于所有在 MQL 环境下编程的人员,无论是初学者还是专业人士,将会非常有趣。而且 MQL 环境下的开发人员和理论家阅读此文也会相当有帮助,因为这里所分析的问题可能会成为将来实施 MetaTrader 和 MQL 的项目。在某些程度上,类似的想法可见于文章通用的 Expert Advisor 模板在通用的 Expert Advisor 中发送交易信号



所以,

依我作为一个程序员看来,MQL 的劣势之一是在构建交易系统模型时缺少对象方法。MQL 开发者给我们提供了两种解决方法:调用外部函数或使用有序参数 MAGIC 来识别序号归属。

实际上,如果只有一个系统在一个帐户运行,我们不需要识别。但当我们具有在一个帐户上添加多个自动交易系统的程序选项时,就不得不使用 MAGIC 了。即使在调用外部函数时,也需要进行确定。当然,我们可以建立一个 OrderTicket 数组并识别仅属于一个交易系统的数组,但据我们对一些经纪公司的了解,委托单在交换时(即一个订单关闭,另一个订单打开)会改变。这就是我们必须使用 MAGIC 的原因。

所以,在开发者们忙于改善 MQL 语言,使其更加灵活时,我们在建立交易模型时采用对象方法。

这是一个跟我的对象模型一致的交易系统。当然,这并非通用,但目前我没有发现其他方法。

所以,我们来分析该模型。

A).信号系统(SS)

该模块过程的对象和理解即将出现的报价。通常,信号系统的“对象”是一组指标,例如,移动平均线指标。根据处理的报价和指标值,“对象”(或信号量产生信号以进入/退出或改动订单等。

信号量形成其信号并从进入/退出(EE)模块发送至另一个对象。

在 MQL 中设置信号量相当容易。

1.使用 #define 定义全局标识符。

最好不要设置连续的数字,例如 1、2、3、4...,而是在 5-10,这样在 Expert Advisor 内我们可以将一个信号用于多个进程(见第二个模块)。

//+------------------------------------------------------------------+
//|                                                      Signals.mqh |
//|                                    Copyright © 2007 Сергеев Алексей |
//|                                                los@we.kherson.ua |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, Сергеев Алексей "
#property link      "mailto: los@we.kherson.ua"
#property library
 
#define BLACKSYS   10
#define BORCHAN    20
#define ELDER      80
#define ENVELOP    90

2.然后在该模块的全局函数中我们应该启用其处理程序。

int CheckSignal(bool bEntry, int SignalID)
{
      switch (SignalID)
      {
                  case BLACKSYS:             return (BlackSys(bEntry)); break;
                  case BORCHAN:              return (BorChan(bEntry)); break;
                  case ELDER:                   return (Elder(bEntry)); break;
                  case ENVELOP:              return (Envelop(bEntry)); break;
                  default:                                     return (-1);
      }
}

3.最后一步是对函数的描述。

下面是一个对象的处理信号的示例,该对象继承了包络线指标的特征。

int Envelope(bool bEntry)
{
      int MA=21;
      double Deviation=0.6;
      int Mode=MODE_SMA;//0-sma, 1-ema, 2-smma, 3-lwma
      int Price=PRICE_CLOSE;//0-close, 1-open, 2-high, 3-low, 4-median, 5-typic, 6-wieight
      
      double envH0, envL0, m0;
      double envH1, envL1, m1;
      envH0=iEnvelopes(NULL, 0, MA, Mode, 0, Price, Deviation, MODE_UPPER, 0); 
      envL0=iEnvelopes(NULL, 0, MA, Mode, 0, Price, Deviation, MODE_LOWER, 0); 
      envH1=iEnvelopes(NULL, 0, MA, Mode, 0, Price, Deviation, MODE_UPPER, 1); 
      envL1=iEnvelopes(NULL, 0, MA, Mode, 0, Price, Deviation, MODE_LOWER, 1); 
 
      m0 = (Low[0]+High[0])/2;          m1 = (Low[1]+High[1])/2;
      //----- condition for operation execution
      if (bEntry)   //for opening
      {          
                  if (envH0<m0 && envH1<m1) return (OP_SELL);
                  if (envL0>m0 && envL1>m1) return (OP_BUY);
      }
      else //for closing
      {
                  if (envH0<m0 && envH1<m1) return (OP_BUY);
                  if (envL0>m0 && envL1>m1) return (OP_SELL);
      }
 
   return (-1); //no signal
}

这样我们就得到一个包含不同对象信号的模块。

B).EE 程序块的对象具有最小的任务:首先,其对象跟对象信号交互作用——注意观察。生命周期和互动如下:Checking semaphore -> if there are any positive signals, open/close/modify positions -> Passing control to objects in the module PS.EE 模块中的所有对象都有一个前缀进程...,更加具体的确定了其行为。

例如:

ProcessAvgLim         //  -  the object processes signals with opening pending limit-orders and positions averaging
ProcessTurn           //  -  the object processes signals with position turning

交易系统类别的每一个样本(我们对此都理解并在模块中使用)必须具有其自己独立的特征,比如获利、止损、自己的资金管理以及在不同的跟踪变体中使用的其他补充参数等。

在使用这些特征时,我尝试了多种方法,发现在 MQL 中最适合的是创建一个二维数组。以下是它的描述:

double SysPar[nSignal][11];
 
#define _TP        0 // Profit
#define _NullTP    1 // profit level, after which we set into losslessness 
#define _NullTP2   2 // profit level, after which we set into losslessness 
#define _TS        3 // distance of the trailing stop 
#define _NullSL    4 // level, after achieving which the expected profit is transfered into opening point
#define _SL        5 // level, after achieving which the expected profit is transfered into opening point
#define _dSL       6 // the initial step upon the opening level of the next order in the position support
#define _dStep     7 // The step is increased in .. times upon the level of the next opening 
#define _dLot      8 // In how many times (as compared to the last one) we increase the lot on the next one 
#define _nLot      9 // In how many times (as compared to the initial one) we increase the lot on the next one
 
string SysParName[nSignal];

其中:nSignal 是对象信号的标识符。

例如:

SysPar[ENVELOP][_TS] = 40.0;    // distance of the trailing stop 
SysPar[ENVELOP][_NullSL] = 20.0;// level, after achieving which the expected profit is transfered into opening point
SysPar[ENVELOP][_SL] = 70;      // changing stop-loss

根据你自己的要求,可以增加该数组结构中设定参数的数量。

在设置参数后,我们调用信号量处理的函数。换言之,我们跟信号系统交互。这通过我最喜欢的 start() 函数完成。

void start()
{
      ProcessAvgLim(ENVELOP, ENVELOP, Green, Red);
… …

从规划可以看出,在交易系统中我们有 4 个注册信号量和 3 个观察器。每个信号量都基于自己的报价解读变体。

例如,信号量 1发送分析 MACD 指标的信号。观察器 1 在接收到这些信号后在一个简单规划 ProcessSimple 中打开订单。ProcessSimple

观察器 2 和 3 更加困难。每一个都控制两个信号量的信号。因此,订单打开的方法不同。

在设置了观察器的参数并添加信号量之后,我们需要控制和跟踪打开头寸。

“负责”已打开的订单状态的是 头寸支持(PS)模块的对象

C).PS 程序块在我看来是最有趣的一个,重要性不亚于信号量。

在这里使用了不同的跟踪变体,挂单打开,头寸支持和锁定、获利和亏损控制的实施等。这种 PS 应对 EE 信号反应充分,在出现亏损头寸时退出市场将亏损降到最低。

下面这个网站有一个有趣的跟踪变体库:Library of Functions and Expert Advisors for trailing / Yury Dzyuban。所有的跟踪类型都可以轻松添加到系统。

简单来说,所有的支持对象都从前缀跟踪变体开始...

方案如下:

调用,控制从观察器到跟踪变体的传递也通过函数 start() 完成

void start()
{
      ProcessSimple(MACD, MACD, Black, Plum); TrailingSimple(MACD, Black, Plum);
      ProcessAvgLim(ENVELOPE, ENVELOPE, Green, Red);  TrailingAvgLim(ENVELOPE, Green, Red);
}

这是一个使用对象方法建立系统的示例变体。想要如此做的人可以使用。

我想再一次呼吁 MQL 开发者扩展语言选择。作为示例,以下是以 C++ 语言编写的执行对象类的变体。

struct SystemParam
{
    double TP;        // profit
    double NullTP;    // profit level, after which we set into losslessness 
    double NullTP2;   // profit level, after which we set into losslessness a set of one-direction orders
    double TS;        // distance of the trailing stop 
    double NullSL;    // loss level, at which we transfer the expected profit into losslessness
    double SL;        // stop-loss
    double dSL;       // a step upon the opening level of the next order for the position support
    double dStep;     // In how many times we increase the step upon the opening level of the next order
    double dLot;      // In how many times we increase the lot on the next order
}
 
 
class MTS 
{
    public:
    string m_NameTS;    // system name (for making comments for the order)
    int m_SignalID;     // identifier of trading signals (for semaphore inquiry)
 
    long int Tickets[1000];    // array of order tickets, selected upon m_SignalID (MAGIC)
 
    SystemParam SysPar;    // Trading system parameters
    color ClrBuy;         // color for indicating BUY order
    color ClrSell;        // color for indicating SELL order
 
    // Initialization
    void MyMTS ();            // standard function that sets initial values of the system
    void MyMTS (int aSignalID, int nProcessMode, int nTrailingMode); // standard function 
                                    // that sets initial values of the system
    
    
    // Implementation
    int CheckSignal();     //function of checking state of market signals
 
    // Processing
    int m_nProcessMode;          // identifier of observation mode
    int m_nTrailingMode;         // identifier of trailing mode
    void Process();         // EE function - processing CheckSignal()
    void Trailing();        // PS function - order trailing
 
    // Special functions
    bool CreatTicketArray(int dir);    // creating an array of tickets, selected upon m_SignalID (MAGIC) 
                    // and desired type dir: buy, sell, buylim, buystop, sellim, sellstop
    bool ArrangeOrderBy(int iSort);  // arranging array Tickets upon the parameter (date, profit, price...)
 
};
 
…
 
MTS MyTS; // our trading systemint init()  
{   
…
    MyTS.m_SignalID = SIGNAL_MACD; // our system is based on MACD signals
    MyTS.m_NameTS = "MACD";
    MyTS.SysPar.TP = 500;    
    MyTS.SysPar.NullTP = 20;
    MyTS.SysPar.TS = 50;
    MyTS.SysPar.SL = 1000;
 
    MyTS.SetProcess (MODE_AVGLIM);
    MyTS.SetTrailing (MODE_AVGLIM);
…
}
 
void start()
{
…
    MyTS.Process ();        
    MyTS.Trailing ();
…
}
 
…
 
void MTS::Process()
{
…
    int Signal = CheckSignal(true, m_SignalID); //calling the global function of signal processing
    if (Signal == -1) return; // if no signal, do nothing
    
//----- for buying
    if(Signal == OP_BUY)
    {    
    }
 
    if(Signal == OP_SELL)
    {    
    }
…
}
 
…
// global processor of semaphores
 
int CheckSignal(bool bEntry, int SignalID)
{
    switch (SignalID)
    {
        case ELDER:    return (Elder(bEntry)); break;
        case ENVELOP:    return (Envelop(bEntry)); break;
        case LAGUER:    return (Laguer(bEntry)); break;
        case MACD:    return (Macd(bEntry)); break;
        …
    }
}
 
// calling a certain semaphore
int Macd(bool bEntry)
{
    double MACDOpen=3;
    double MACDClose=2;
    double MA=26;
    int MODE_MA    = MODE_EMA; // method of the calculation of averages
    int PRICE_MA   = PRICE_CLOSE; // method of the calculation of averages
    int PERIOD     = PERIOD_H1; // the period to work with
 
    //parameters of averages
    double MacdCur, MacdPre, SignalCur;
    double SignalPre, MaCur, MaPre;
 
    //---- get the value
    MacdCur=iMACD(NULL,0,8,17,9,PRICE_MA,MODE_MAIN,0);   MacdPre=iMACD(NULL,0,8,17,9,PRICE_MA,MODE_MAIN,1);
    SignalCur=iMACD(NULL,0,8,17,9,PRICE_MA,MODE_SIGNAL,0);   SignalPre=iMACD(NULL,0,8,17,9,PRICE_MA,MODE_SIGNAL,1);
    MaCur=iMA(NULL,0,MA,0,MODE_MA,PRICE_MA,0);   MaPre=iMA(NULL,0,MA,0,MODE_MA,PRICE_MA,1);
 
    //----- condition for the operation execution
    if (bEntry)   //for buying bEntry==true
    {     
        if(MacdCur<0 && MacdCur>SignalCur && MacdPre<SignalPre && MathAbs(MacdCur)>(MACDOpen*Point) && MaCur>MaPre) 
         return (OP_BUY);
        if(MacdCur>0 && MacdCur<SignalCur && MacdPre>SignalPre && MacdCur>(MACDOpen*Point) && MaCur<MaPre) 
         return (OP_SELL);
    }
    else //for closing bEntry==false
    {    
        if(MacdCur>0 && MacdCur<SignalCur && MacdPre>SignalPre && MacdCur>(MACDClose*Point)) 
         return (OP_BUY);
        if(MacdCur>0 && MacdCur<SignalCur && MacdPre>SignalPre && MacdCur>(MACDOpen*Point) && MaCur<MaPre) 
         return (OP_BUY);
 
        if(MacdCur<0 && MacdCur>SignalCur && MacdPre<SignalPre && MathAbs(MacdCur)>(MACDClose*Point))  
         return (OP_SELL);
        if(MacdCur<0 && MacdCur>SignalCur && MacdPre<SignalPre && MathAbs(MacdCur)>(MACDOpen*Point) && MaCur>MaPre) 
         return (OP_SELL);
    }
 
    return (-1); //no signal
}

MQL 语言中的系统逻辑没有多大不同。所有的函数变为全局性。为了区分两个交易系统的订单,我们需要向处理订单的所有函数添加 SignalID(即 MAGIC)。

全部回复

0/140

达人推荐

量化课程

    移动端课程