MQL におけるオブジェクト・アプローチ

--- | 16 3月, 2016


はじめに

本稿はまず MQL 環境で作業を行う初心者プログラマ、プロのプログラマー両方にとって興味深いものとなります。また、本稿が環境開発者や観念論者に読まれると、役に立つと思われます。というのも、ここで分析される疑問は将来 MetaTrader や MQL を実装する上でのプロジェクトとなりうるからです。ある程度同様の考え方が記事Universal Expert Advisor Template および Sending Trading Signals in a Universal Expert Advisorで見られます。



そして、

MQL のデメリットの一つですが、私のプログラマーによると、トレーディングシステム構築にあたりオブジェクトのアプローチを欠いていることです。MQL 開発者は2つ解決法を提供しています。それは、外部関数を呼び出すこと、または注文がどこに属しているか特定するために注文パラメータ MAGICを使用することです。

実際、1アカウントに1システムが動作しているなら、特定する必要はありません。が、1アカウントに複数の自動売買システムをアタッチするプログラムオプションがある場合は、MAGIC なしでは特定できません。外部関数呼び出しにさえそれらを特定する必要があるのです。もちろん、配列を作成し、配列が1つのトレーディングシステムに対してどこに属しているか特定することはできますが、周知のように仲介会社によっては注文チケットはスワップで変わります(実際1つが終了し、別の1つが開始されます)。そのため MAGIC が必要なのです。

言語をより柔軟なものにするため開発者が多忙な中で、取引モデルを構築するときのオブジェクトのアプローチをここでもう実装してしまいます。

これは私のオブジェクトモデルに従ったトレーディングシステムです。もちろんそれはユニバーサルではありませんが、私にはいまのところこれ以外の方法が見当たらないのです。


そんなわけで、このモデルを分析します。

A)シグナルシステム(SS)

このモジュール処理と受信クオート解釈のオブジェクト通常シグナルシステムの『オブジェクト』とはインディケータのセットを言います。たとえば、複数の移動平均です。クオートとインディケータ値処理の結果として、『オブジェクト』(またはセマフォ)は、エンター/エグジット、または注文変更などの シグナルを作り出します

セマフォはそのシグナルを形成し、それをモジュール Entry/Exit (EE)から別のオブジェクトに送信します。

MQL でセマフォを設定するのはかなり簡単です。

1. #defineによってグローバル識別子を定義します。

1, 2, 3, 4... のような連続した数字ではなく 5~10 で設定する方がよいです。おすすることでにおいて複数処理に対してシグナルを1つ使用することができるでしょう(2番目のモジュール参照)。

//+------------------------------------------------------------------+
//|                                                      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 のオブジェクトは最小のタスクを持ちます。
まず、そのオブジェクトはオブジェクト信号と相互作用します。それらを観察するのです。ファイルサイクルと相互作用は以下です。
セマフォチェック → 正の信号があればポジションをオープン/クローズ/変更する → モジュール PS のオブジェクトに制御を渡す。
モジュール EE 内のオブジェクトにはすべて Process… という接頭辞がついています。それは動作をより具体的に決定するものです。

たとえば:

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 でオーダーをオープンします。

観測2と観測3はもっと複雑です。それぞれセマフォ2つの信号を制御するのです。そしてその結果として、オープンの方法は異なります。

オブザーバーのパラメータを設定し、そこにセマフォをアタッチしたら、ポジションのオープンを制御し追跡する必要があります。

オープンしているオーダーの状態に『関わる』のはモジュール Position Support (PS)のオブジェクトです。

C). 私見では、ブロック PS はもっとも興味が惹かれるブロックで、セマフォ同様重要なものです。

ここでは異なるトレーリングのバリアントが実装され、未決注文がオープンされ、ポジションサポートとロック、利益および損失制御が実装されるなどします。そのような PS は、最小損失の損失ポジションの場合、市場に存在することに関して EE 信号上で適切に反応します。

以下のサイト上にトレーリングの興味深いライブラリがあります:Library of Functions and Expert Advisors for trailing / Yury Dzyuban。トレーリングタイプはすべて簡単にシステムにアタッチされます。

簡単にわかるように、サポートオブジェクトはすべて接頭辞Trailing… から始まるようになっています。

それは以下のようなスキームです。


呼出し、オブザーバーからトレーリングへの制御転送は同じ関数 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)を追加する必要があります。