English Deutsch
preview
ログレコードをマスターする(第7回):チャートにログを表示する方法

ログレコードをマスターする(第7回):チャートにログを表示する方法

MetaTrader 5 |
103 0
joaopedrodev
joaopedrodev

はじめに

開発に没頭していると、記事にするつもりのなかったアイデアや工夫がふと生まれることがあります。ほんの些細な不便を解消するために考えついた即興の仕掛け。今回ご紹介する内容も、まさにそうした類いのものです。最初は「単純すぎてわざわざ共有するまでもない」と思ったのですが、実際に試してみると予想以上に便利で楽しく、隠しておくのはもったいないと感じました。

ここまで読んでくださっている方なら、すでにLogifyをご存知かもしれません。これは、MQL5におけるエキスパートアドバイザー(EA)の開発において、ログの管理と保存を包括的にサポートするライブラリです。MetaTrader 5標準のログが抱える制約を解消し、開発者にとってより高い制御性、整理性、プロフェッショナル性を提供することを目的としています。

本連載の最初の記事「ログレコードをマスターする(第1回):MQL5の基本概念と最初のステップ」では、このライブラリ構築の第一歩を踏み出しました。基本的な考え方を整理し、標準のMetaTraderログに盲目的に依存することがいかに混乱を招くかを検討しながら、堅牢でカスタマイズ可能、かつ拡張性を備えたソリューションの基盤を形にしていきました。

その過程のなかで、当初のロードマップにはなかった新しい発想が生まれました。ライブラリを自分自身で使っているうちに、次第に「ターミナルでログを探す」という行為がいかに不便であるかを痛感したのです。[エキスパート]タブを開き、雑多なメッセージの中から必要な情報を探し出したり、あるいは実行中に重要なエラーが流れて消え、見逃してしまうこともしばしばでした。まさに「燃えさかる干し草の山から針を探す」ようなものでした。

そのとき私はひらめきました。「ログを、本当に意味のある場所に表示したらどうだろう。」そう、チャートの上。トレーダーの目の前。ロボットが「生きている」その場所にです。もちろん、散らばったラベルや点滅する矢印、かえって邪魔になるグラフィックオブジェクトを置こうというのではありません。もっとエレガントで控えめ、そして機能的な方法です。そこで注目したのが、古くから存在するComment()関数でした。

多くの人がほとんど無視し、変数のデバッグに一時的に使ったら削除してしまうあの関数です。しかし少し工夫すれば、読みやすく、クリーンで、リアルタイムに更新される実用的なログコンソールに変身するのです。

百聞は一見にしかず。まずは、その実際の動きをご覧ください。

正直に言えば、このアイデアを記事にまとめるつもりはありませんでした。追加のリソースというより、ほとんど個人的な遊び心に近いものでした。ところが、あまりに実用的で満足度が高かったため、自分のリポジトリに眠らせておく理由はなくなったのです。シンプルでスマート、そして本当に役立つソリューションがお好きな方は、この先もぜひ読み進めてください。今日を境に、Comment()を見る目が変わるはずです。チャートをログコンソールに変えてみましょう。


新しいハンドラの作成

チャート上でログをエレガントに可視化する目的が理解できたところで、実際に作業に取りかかりましょう。ここでのアイデアは、Logifyライブラリ内に新しい専用ハンドラを作成し、ログをキャプチャしてComment()関数を使ってチャート上に直接表示する役割を持たせることです。

ライブラリのHandlersフォルダ内に、LogifyHandlerComment.mqhという新しいファイルを作成します。このファイルには、従来のログをチャート上で動的に表示するためのロジックがすべて含まれます。実質的には、ロボットに付随するコンソールのような役割を果たします。このステップが終わる頃には、ライブラリのファイル構成は以下のように整理されているはずです。

このファイル内で、CLogifyHandlerCommentという新しいクラスを宣言します。このクラスは他のLogifyハンドラと同様に、基底クラスCLogifyHandlerを継承します。これにより、アーキテクチャは一貫性を保ちつつモジュール化され、本連載第1回の記事から構築してきたパターンに準拠することができます。

最初のステップとして、常にそうであるように、ハンドラの基本的なプロパティを定義します。つまり、このハンドラが誰で、何をするのかを決めるのです。これはクラスコンストラクタ内でおこない、名前を「comment」として定義します。この名前が、ライブラリ内でこの特定のタイプのログ出力を有効化するための識別子になります。

以下に、クラスの初期スケルトンを示します。基本的なメソッドであるEmit()、Flush()、Close()はすでに宣言されており、次に実装していく準備が整っています。

//+------------------------------------------------------------------+
//|                                         LogifyHandlerComment.mqh |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "joaopedrodev"
#property link      "https://www.mql5.com/ja/users/joaopedrodev"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "LogifyHandler.mqh"
//+------------------------------------------------------------------+
//| class : CLogifyHandlerComment                                    |
//|                                                                  |
//| [PROPERTY]                                                       |
//| Name        : CLogifyHandlerComment                              |
//| Heritage    : CLogifyHandler                                     |
//| Description : Log handler, inserts data into chart comment.      |
//|                                                                  |
//+------------------------------------------------------------------+
class CLogifyHandlerComment : public CLogifyHandler
  {
public:
                     CLogifyHandlerComment(void);
                    ~CLogifyHandlerComment(void);
   
   virtual void      Emit(MqlLogifyModel &data);         // Processes a log message and sends it to the specified destination
   virtual void      Flush(void);                        // Clears or completes any pending operations
   virtual void      Close(void);                        // Closes the handler and releases any resources
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CLogifyHandlerComment::CLogifyHandlerComment(void)
  {
   m_name = "comment";
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLogifyHandlerComment::~CLogifyHandlerComment(void)
  {
  }
//+------------------------------------------------------------------+
//| Processes a log message and sends it to the specified destination|
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Emit(MqlLogifyModel &data)
  {
  }
//+------------------------------------------------------------------+
//| Clears or completes any pending operations                       |
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Flush(void)
  {
  }
//+------------------------------------------------------------------+
//| Closes the handler and releases any resources                    |
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Close(void)
  {
  }
//+------------------------------------------------------------------+



設定の計画:サイズ、フレーム、タイトル

メイン関数を直接実装する前に、柔軟性を重視するシステムで必ず直面する基本的な問題を解決する必要があります。それは、この仕組みをどう設定するかです。朗報は、Logifyの設計上、このオプションはシンプルで理にかなっているということです。誰も使い道のわからない不明瞭な設定やパラメータはありません。ここでの焦点は、視覚的な制御と整理にあります。

私たちのビジュアルログハンドラは、主に以下の4つの設定パラメータを提供します。

  • size:チャート上に表示するログ行数を定義します。言い換えれば、表示されるメッセージウィンドウのサイズです。
  • frame_style:チャート上のログを囲むフレームのスタイルです。選択肢は、none(フレームなし、シンプルかつ直接的)、single(シングルフレーム)、double(ダブルフレーム)です。
  • direction:メッセージが表示される方向です(上から下、あるいは下から上)。
  • title:フレーム上部に表示されるタイトルです。EAの名前などを追加できます。

これらのパラメータを念頭に置き、MqlLogifyHandleCommentConfigという構造体を作成します。この構造体は設定をすべてカプセル化し、CLogifyHandlerCommentクラス内で使用されます。これが設定の核心部分です。

//+------------------------------------------------------------------+
//| ENUMS                                                            |
//+------------------------------------------------------------------+
enum ENUM_LOG_FRAME_STYLE
  {
   LOG_FRAME_STYLE_NONE = 0,           // No rotation
   LOG_FRAME_STYLE_SINGLE,             // Rotate based on date
   LOG_FRAME_STYLE_DOUBLE,             // Rotate based on file size
  };
enum ENUM_LOG_DIRECTION
  {
   LOG_DIRECTION_UP = 0,               // Up
   LOG_DIRECTION_DOWN,                 // Down
  };
//+------------------------------------------------------------------+
//| Struct: MqlLogifyHandleComment                                   |
//+------------------------------------------------------------------+
struct MqlLogifyHandleCommentConfig
  {
   int size;                           // Space in lines that it will occupy
   ENUM_LOG_FRAME_STYLE frame_style;   // Display grid
   ENUM_LOG_DIRECTION direction;       // Direction
   string title;                       // log title
   
   //--- Default constructor
   MqlLogifyHandleCommentConfig(void)
     {
      size = 20;
      frame_style = LOG_FRAME_STYLE_SINGLE;
      direction = LOG_DIRECTION_UP;
      title = "LOGIFY";
     }
   
   //--- Destructor
   ~MqlLogifyHandleCommentConfig(void)
     {
     }

   //--- Validate configuration
   bool ValidateConfig(string &error_message)
     {
      //--- Saves the return value
      bool is_valid = true;
      
      //--- Check if size is greater than 0
      if(size <= 0)
        {
         size = 20;
         error_message = "Size must be greater than 0.";
         is_valid = false;
        }
      
      //--- Check len
      if(StringLen(title) > 40)
        {
         error_message = "Title is too long for frame. Max 40 chars.";
         is_valid = false;
        }
      
      //--- No errors found
      return(is_valid);
     }
  };

CLogifyHandlerCommentクラスは、この設定をプライベートプロパティとして保持します(m_config)。さらに、設定を操作するための2つの重要なメソッドも公開しています。設定を検証し、適用するSetConfig()と、現在の設定を返すGetConfig()です。

class CLogifyHandlerComment : public CLogifyHandler
  {
private:
   
   MqlLogifyHandleCommentConfig m_config;
   
public:
                     CLogifyHandlerComment(void);
                    ~CLogifyHandlerComment(void);
   
   //--- Configuration management
   void              SetConfig(MqlLogifyHandleCommentConfig &config);
   MqlLogifyHandleCommentConfig GetConfig(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CLogifyHandlerComment::CLogifyHandlerComment(void)
  {
   m_name = "comment";
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLogifyHandlerComment::~CLogifyHandlerComment(void)
  {
  }
//+------------------------------------------------------------------+
//| Set configuration                                                |
//+------------------------------------------------------------------+
void CLogifyHandlerComment::SetConfig(MqlLogifyHandleCommentConfig &config)
  {
   m_config = config;
   
   //--- Validade config
   string err_msg = "";
   if(!m_config.ValidateConfig(err_msg))
     {
      Print("[ERROR] ["+TimeToString(TimeCurrent())+"] Log system error: "+err_msg);
     }
   
   //--- Resize
   ArrayResize(m_logs, m_config.size);
  }
//+------------------------------------------------------------------+
//| Get configuration                                                |
//+------------------------------------------------------------------+
MqlLogifyHandleCommentConfig CLogifyHandlerComment::GetConfig(void)
  {
   return(m_config);
  }
//+------------------------------------------------------------------+


カスケード動作の作成

そもそも、なぜこのカスケード動作が必要なのでしょうか。答えは簡単です。ログをチャート上に、新しいメッセージが下に表示され、古いメッセージが上へ押し上げられて消えていくように表示したい場合、動的なキューのように振る舞う構造が必要だからです。これがなければ、新しいログが前のログを上書きするか、あるいは無制限に蓄積され、チャートが散らかってしまいます。

イメージするとわかりやすいです。チャート上の利用可能なスペースは無限ではありません。視覚的には、たとえば10行のテキストしか表示できず、それ以上になると重なったり画面から消えたりします。したがって、新しいメッセージが現れるたびにキューの先頭に入り、既存のメッセージを上へ押し上げる必要があります。もし上限に達していれば(たとえば10行)、最も古いメッセージは単純に破棄されます。

この挙動は多くのアプリケーションで一般的で、コンピュータサイエンスでは循環バッファやスライディングキュー、より技術的には線形配列内の要素移動として知られています。ここではわかりやすくカスケード動作と呼びます。

情報が水のように、行ごとに流れるイメージです。ロジックは驚くほどシンプルでありながら、非常に効率的です。

  1. m_logs[]という配列を維持します。これは「視覚コンソール」として機能します。この配列のサイズは固定で、たとえば10要素、つまりチャート上で10行です。
  2. 新しいログメッセージが到着すると、リストの先頭(配列の0番目)に表示される必要があります。
  3. 既存の要素を移動させます。配列の8番目は9番目へ、7番目は8番目へ…0番目は1番目へ押し上げます。 
  4. この操作が完了すると、0番目のスペースが空き、新しいメッセージを挿入できます。これで画面の上部に新鮮なメッセージが輝きます。

視覚的にはドミノ効果のように動きます。新しいメッセージが前のメッセージを1ポジション下げ、スペースがなくなると、キューの最後にあったメッセージは画面とメモリから消えます。シンプルでクリーン、かつ効率的です。

この仕組みがなければ、各ログは直接Comment()で上書きされるか、無制御に行が蓄積され、チャートが汚れてしまいます。どちらも望ましい状態ではありません。したがって、カスケードは見た目の問題ではなく、チャート上のログを有用で読みやすく保つための機能的必須事項です。

カスケードの仕組みが理解できたところで、Emit()メソッドで実際に動かします。このメソッドは、新しいログメッセージが処理され、チャート上に表示されるたびに呼ばれます。そして先ほど説明した通り、カスケード動作を適用し、フォーマット済みテキスト(フレーム、タイトル、整列を含む)を組み立て、最後にMQL5のComment()関数を使って出力します。

コードの見通しを良くするために、フレーム、タイトル、行の組み立ては補助関数に分けています。

処理の流れは3つのステップです。

  1. レベルによるフィルター:メッセージがハンドラに設定されたログレベルに達していなければ破棄されます。
  2. カスケード動作:m_logs[]配列内の既存ログを1ポジション前に移動させ、新しいメッセージ用に0番目を空けます。
  3. コメントの組み立て:補助関数を使って以下を生成します。
  • ヘッダ:フレーム上部とタイトル(設定されていれば)
  • 本文:ログリスト(設定された方向に従って並べる)
  • フッター:フレームの閉じ部分(設定されていれば)

生成した結果は、MQL5のComment()で表示します。使用する補助関数は以下の通りです。

  • GetSideBorder():側面の枠文字を返す
    • シングルフレーム:│
    • ダブルフレーム:║
    • フレームなし:""(空文字)
  • GetBorderTop():フレーム上部の線を返す
    • 例:┌───────┐または╔═══════╗
  • GetBorderMiddle():タイトル下のセパレーターを返す
    • 例:├───────┤または╠═══════╣ 
  • GetBorderBottom():フレームの下の線を返す
    • 例:└──────┘または╚═══════╝ 
  • BuildHeader():タイトル(設定されていれば)とフレームを組み立てる
  • BuildFooter():フレームのフッター部分を生成する
  • FormatLogLines():全ログ行をフォーマットする
    • 側面の枠を適用(ある場合)
    • 表示方向を尊重(LOG_DIRECTION_UPまたはLOG_DIRECTION_DOWN)

最終的に、完全なコードは次のようになります。

//+------------------------------------------------------------------+
//| Processes a log message and sends it to the specified destination|
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Emit(MqlLogifyModel &data)
  {
   //--- Check if log level is allowed
   if(data.level < this.GetLevel())
     {
      return;
     }

   //--- Shift logs to maintain history
   for(int i = m_config.size-1; i > 0; i--)
     {
      m_logs[i] = m_logs[i-1];
     }
   m_logs[0] = data;

   //--- Build the complete comment
   string comment = BuildHeader();
   comment += FormatLogLines();
   comment += BuildFooter();

   //--- Display on chart
   Comment(comment);
  }
//+------------------------------------------------------------------+
//| Returns the side border character based on frame style          |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::GetSideBorder()
  {
   if(m_config.frame_style == LOG_FRAME_STYLE_SINGLE) return "│";
   if(m_config.frame_style == LOG_FRAME_STYLE_DOUBLE) return "║";
   return "";
  }
//+------------------------------------------------------------------+
//| Returns the top border based on frame style                     |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::GetBorderTop()
  {
   if(m_config.frame_style == LOG_FRAME_STYLE_SINGLE)
      return "┌───────────────────────────────────────────┐\n";
   if(m_config.frame_style == LOG_FRAME_STYLE_DOUBLE)
      return "╔═══════════════════════════════════════════╗\n";
   return "";
  }
//+------------------------------------------------------------------+
//| Returns the middle separator based on frame style               |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::GetBorderMiddle()
  {
   if(m_config.frame_style == LOG_FRAME_STYLE_SINGLE)
      return "├───────────────────────────────────────────┤\n";
   if(m_config.frame_style == LOG_FRAME_STYLE_DOUBLE)
      return "╠═══════════════════════════════════════════╣\n";
   return "";
  }
//+------------------------------------------------------------------+
//| Returns the bottom border based on frame style                  |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::GetBorderBottom()
  {
   if(m_config.frame_style == LOG_FRAME_STYLE_SINGLE)
      return "└───────────────────────────────────────────┘\n";
   if(m_config.frame_style == LOG_FRAME_STYLE_DOUBLE)
      return "╚═══════════════════════════════════════════╝\n";
   return "";
  }
//+------------------------------------------------------------------+
//| Builds the comment header with optional title and frame         |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::BuildHeader()
  {
   string header = "";

   if(m_config.title != "" && m_config.title != NULL)
     {
      if(m_config.frame_style == LOG_FRAME_STYLE_NONE)
        {
         header += " " + m_config.title + "\n";
         header += "─────────────────────────────────────────────\n";
        }
      else
        {
         header += GetBorderTop();
         header += GetSideBorder() + " " + m_config.title + "\n";
         header += GetBorderMiddle();
        }
     }
   else
     {
      if(m_config.frame_style != LOG_FRAME_STYLE_NONE)
        {
         header += GetBorderTop();
        }
     }

   return header;
  }
//+------------------------------------------------------------------+
//| Builds the comment footer based on frame style                  |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::BuildFooter()
  {
   if(m_config.frame_style != LOG_FRAME_STYLE_NONE)
      return GetBorderBottom();
   return "";
  }
//+------------------------------------------------------------------+
//| Formats all log lines according to direction and frame          |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::FormatLogLines()
  {
   string result = "";
   string side = GetSideBorder();

   if(m_config.direction == LOG_DIRECTION_UP)
     {
      for(int i = m_config.size-1; i >= 0; i--)
        {
         string line = m_logs[i].formated;
         if(line != "")
           {
            result += side + " " + line + "\n";
           }
         else
           {
            result += side + "\n";
           }
        }
     }
   else // LOG_DIRECTION_DOWN
     {
      for(int i = 0; i <= m_config.size-1; i++)
        {
         string line = m_logs[i].formated;
         if(line != "")
           {
            result += side + " " + line + "\n";
           }
         else
           {
            result += side + "\n";
           }
        }
     }

   return result;
  }
//+------------------------------------------------------------------+



最後にログをクリアする

CLogifyHandlerCommentクラスの実装を締めくくるにあたり、まだ1つ基本的ですが非常に重要なポイントが残っています。それは、ハンドラを閉じる際にチャート上に表示されていた内容をクリアすることです。結局のところ、Emit()関数が画面にメッセージを表示する役割を持つのに対し、Close()メソッドの使命はその逆です。すなわち、すべてを削除することです。

そして、ここが一番のポイントですが、これは実に簡単です。他のハンドラのようにファイルを閉じたり、接続を切断したり、メモリを解放する必要はありません。ここでやるべきことは、チャートからコメントを削除するだけです。MetaTraderではこれが非常にシンプルに、引数なしでComment()関数を呼ぶだけで実現できます。つまり、Comment("")を使うだけで完了です。

//+------------------------------------------------------------------+
//| Closes the handler and releases any resources                    |
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Close(void)
  {
   //--- Clear
   Comment("");
  }
//+------------------------------------------------------------------+


CLogifyHandlerCommentハンドラの実践テスト

理論だけでは、結局のところ…理論に過ぎません。では、ハンドラを実際に動かし、MetaTraderのチャート上でどのように動作するかを確認してみましょう。テスト用に、シンプルでわかりやすいスクリプトを作成しました。ここでは、EAが異なるレベルのログをトリガーする典型的なシナリオをシミュレートしています。デバッグメッセージからアラート、クリティカルエラーまで含まれています。

さらに、ハンドラを設定して、ログをチャート上に直接表示するようにしました。フレームスタイルはシングルラインで、ログは1行ずつ下方向に表示されます。つまり、最新のメッセージが下に、古いメッセージが上へ押し上げられる形です。

//+------------------------------------------------------------------+
//| Import CLogify                                                   |
//+------------------------------------------------------------------+
#include <Logify/Logify.mqh>
CLogify logify;
//+------------------------------------------------------------------+
//| Expert initialization                                            |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- Handler config
   MqlLogifyHandleCommentConfig m_config;
   m_config.size = 10;                                     // Max log lines
   m_config.frame_style = LOG_FRAME_STYLE_NONE;            // Frame style
   m_config.direction = LOG_DIRECTION_UP;                  // Log direction (up)
   m_config.title = "Expert name";                         // Log panel title
   
   //--- Create and configure handler
   CLogifyHandlerComment *handler_comment = new CLogifyHandlerComment();
   handler_comment.SetConfig(m_config);
   handler_comment.SetLevel(LOG_LEVEL_DEBUG);              // Min log level
   handler_comment.SetFormatter(new CLogifyFormatter("hh:mm:ss",
                                                      "{date_time} [{levelname}]: {msg}"));
   
   //--- Add handler to Logify
   logify.AddHandler(handler_comment);
   
   //--- Test logs
   logify.Debug("Initializing Expert Advisor...", "Init", "");
   Sleep(1500);
   logify.Debug("RSI indicator value calculated: 72.56", "Indicators", "Period: 14");
   Sleep(800);
   logify.Info("Buy order sent successfully", "Order Management", "Symbol: EURUSD, Volume: 0.1");
   Sleep(800);
   logify.Alert("Stop Loss adjusted to breakeven level", "Risk Management", "Order ID: 12345678");
   Sleep(500);
   logify.Error("Failed to send sell order", "Order Management", "Reason: Insufficient balance");
   Sleep(100);
   logify.Fatal("Failed to initialize EA: Invalid settings", "Initialization", "Missing or incorrect parameters");
   
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

なぜSleep()を使うのでしょうか。Sleep()は必須ではありません。しかし、ログの表示を少し間隔を空けるために使います。これにより、イベントが異なるタイミングで発生していることをシミュレートでき、テストがより現実的になり、チャート上での視覚的な効果も見やすくなります。文章だけでは伝わりにくい場合は、以下の画像で実際の表示例をご覧ください。

続いて、利用可能な他のフレームスタイルがどのように見えるかも確認してみましょう。


これで、CLogifyHandlerCommentハンドラのテストが完了し、検証され、100%機能することが確認できました。


結論

これで、MQL5向けの完全で堅牢、かつフルカスタマイズ可能なログライブラリ「Logify」の構築に関する一連の記事は終了です。この過程で、ログシステムの基本から、アーキテクチャの構築、ハンドラの作成、フォーマットの設定、そしてチャート上にComment()関数を使って直接ログを表示するビジュアルハンドラに至るまで、すべてを網羅しました。

提案自体はシンプルですが、非常に重要なものでした。MetaTrader 5でのEAやツール開発において、適切で柔軟かつ設計の整ったログシステムが存在しないというギャップを埋めることです。これにより、本連載を追ってきた方々は、整理されたログを生成し、レベルでフィルタリングし、必要に応じてフォーマットし、さらにはチャート上でリアルタイムに優雅かつ実用的に確認できるライブラリを手に入れました。

本連載はこの記事をもって一応終了となります。ライブラリは機能的で完成度が高く、一般的なニーズには十分対応しています。しかし、技術は常に進化するものです。改善、調整、最適化、あるいは新しいアイデアが生まれる余地は常にあります。そして、そうした更新があれば、必ず新しい記事で紹介し、Logifyの可能性をさらに拡張していくつもりです。

現時点では、このプロジェクトは、開発者の手に強力なツールを提供し、ロボットやインジケーターのデバッグ、解析、監視を支援するという目的を果たしています。Logifyが混沌を秩序に、ランダムなメッセージを有益な情報に変える助けとなることを願っています。

次の機会に新しいアイデアをお届けします。


ファイル名
説明
Experts/Logify/LogiftTest.mq5
ライブラリの機能をテストするファイル。実用的な例が含まれています。
Include/Logify/Formatter/LogifyFormatter.mqh
ログレコードのフォーマット、プレースホルダーを特定の値に置き換えるクラス
Include/Logify/Handlers/LogifyHandler.mqh
レベル設定やログ送信を含むログハンドラを管理するための基本クラス
Include/Logify/Handlers/LogifyHandlerComment.mqh
MetaTraderのターミナルチャートのコメントにフォーマットされたログを直接送信するログハンドラ
Include/Logify/Handlers/LogifyHandlerConsole.mqh
フォーマットされたログをMetaTraderの端末コンソールに直接送信するログハンドラ
Include/Logify/Handlers/LogifyHandlerDatabase.mqh
フォーマットされたログをデータベースに送信するログハンドラ(現在は出力のみが含まれているが、すぐに実際のSQLiteデータベースに保存する予定)
Include/Logify/Handlers/LogifyHandlerFile.mqh
フォーマットされたログをファイルに送るログハンドラ
Include/Logify/Utils/IntervalWatcher.mqh
時間間隔が経過したかどうかをチェックし、ライブラリ内でルーチンを作成できるようにする
Include/Logify/Logify.mqh
ログ管理、レベル、モデル、フォーマットの統合のためのコアクラス
Include/Logify/LogifyLevel.mqh
Logifyライブラリのログレベルを定義するファイル。詳細な制御が可能
Include/Logify/LogifyModel.mqh レベル、メッセージ、タイムスタンプ、コンテキストなどの詳細を含むログレコードをモデル化する構造


MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/18291

添付されたファイル |
Logify.zip (21.43 KB)
プライスアクション分析ツールキットの開発(第25回):Dual EMA Fractal Breaker プライスアクション分析ツールキットの開発(第25回):Dual EMA Fractal Breaker
プライスアクションは、利益を生む取引機会を特定するための基本的なアプローチです。しかし、価格の動きやパターンを手動で監視することは、非常に手間がかかり、時間も消費します。そこで、本記事では、プライスアクションを自動的に分析し、潜在的な取引機会が検出されるたびにタイムリーなシグナルを提供するツールを開発する取り組みを紹介します。特に、フラクタルのブレイクアウトとEMA 14、EMA 200を組み合わせて信頼性の高い取引シグナルを生成する堅牢なツールを紹介し、トレーダーがより自信を持って意思決定できるよう支援します。
知っておくべきMQL5ウィザードのテクニック(第67回):TRIXパターンとWilliams Percent Rangeの使用 知っておくべきMQL5ウィザードのテクニック(第67回):TRIXパターンとWilliams Percent Rangeの使用
三重指数移動平均オシレーター(TRIX: Triple Exponential Moving Average Oscillator)とウィリアムズパーセントレンジオシレーター(WPR: Williams Percent Range)は、MQL5のエキスパートアドバイザー(EA)において併用できるもう一組のインジケーターです。このインジケーターペアは、これまで取り上げたものと同様に補完関係にあり、TRIXがトレンドを定義し、ウィリアムズパーセントレンジがサポートおよびレジスタンス水準を確認します。いつものように、MQL5ウィザードを使用して、この2つが持つ可能性をプロトタイピングします。
MQL5取引ツール(第3回):戦略的取引のための多時間軸スキャナーダッシュボードの構築 MQL5取引ツール(第3回):戦略的取引のための多時間軸スキャナーダッシュボードの構築
本記事では、MQL5で多時間軸スキャナーダッシュボードを構築し、リアルタイムの取引シグナルを表示する方法を解説します。インタラクティブなグリッドインターフェースの設計、複数のインジケーターによるシグナル計算の実装、そしてクローズボタンの追加を計画しています。記事はバックテストと戦略的取引の利点で締めくくられます。
MQL5での取引戦略の自動化(第18回):Envelopes Trend Bounce Scalping - コア基盤とシグナル生成(その1) MQL5での取引戦略の自動化(第18回):Envelopes Trend Bounce Scalping - コア基盤とシグナル生成(その1)
本記事では、MQL5でのEnvelopes Trend Bounce Scalpingエキスパートアドバイザー(EA)のコア基盤を構築します。シグナル生成のためにエンベロープやその他のインジケーターを初期化します。また、次回の取引実行に備えてバックテストの設定をおこないます。