English Русский 中文 Español Deutsch Português
preview
ニュース取引が簡単に(第2回):リスク管理

ニュース取引が簡単に(第2回):リスク管理

MetaTrader 5 | 8 11月 2024, 08:10
159 0
Kabelo Frans Mampa
Kabelo Frans Mampa

はじめに

本連載の前回の記事を簡単に振り返りましょう。前回は、DST(夏時間)の概念を取り上げ、さまざまな国での夏時間の設定や、会計年度内でタイムゾーンが1時間進んだり遅れたりする違いについて説明しました。これにより、DSTに対応するブローカーの取引スケジュールがどのように変わるかを理解できました。また、データベースを作成する目的やその利点についても解説しました。具体的には、MQL5経済カレンダーから取得したニュースイベントを保存するデータベースを構築し、ブローカーのDSTスケジュールに応じてイベント時間を調整する仕組みを導入することで、将来的なバックテストにおいて正確なデータを提供できるようにしています。プロジェクトファイルには、異なる国のすべてのMQL5カレンダーイベントにアクセスできるSQLスクリプトがExcel形式で含まれていました。

今回の記事では、前回のコードをいくつか改良し、新しいコードへの継承を実装することで、ニュース/カレンダーデータベースがさらに使いやすく、実用的なものになるよう調整します。また、リスク管理に関する設定にも取り組み、ユーザーのリスク許容度や好みに応じて選べる複数のリスクプロファイルを提供します。


継承

継承の種類


継承

継承

継承は、オブジェクト指向プログラミング(OOP)の基礎的な概念の一つで、新しいクラス(サブクラスまたは派生クラス)が、既存のクラス(スーパークラスまたは基底クラス)のプロパティやメソッドを受け継ぐことを可能にします。このメカニズムにより、既存のクラスの機能を基にして、さらに拡張やカスタマイズを施した新しいクラスを作成することができるため、コードの再利用性が向上し、クラス構造を論理的で階層的に整理しやすくなります。 


継承の目的

継承により、既存のコードの再利用が可能になります。基底クラスから継承することで、サブクラスは既存のメソッドやフィールドをそのまま利用でき、書き換える必要がありません。これにより冗長性が減り、コードの保守性が向上します。  また、継承を使用することでコードを階層的に整理でき、理解しやすく管理もしやすくなります。クラスは共通の属性や動作に基づいてグループ化でき、コードベースが明確で論理的に構成されます。 さらに、継承はポリモーフィズムと密接に関連しており、ポリモーフィズムにより、異なるクラスのオブジェクトを共通のスーパークラスのオブジェクトとして扱うことができます。これにより、柔軟で拡張可能なコードを実装でき、特に便利です。たとえば、同じ基底クラスから継承した異なるクラスのオブジェクトに対して関数を操作できるため、動的なメソッドバインディングと一般化されたコードが可能になります。

また、継承により既存クラスの機能を拡張できます。サブクラスは、新しいメソッドやフィールドを追加したり、既存のメソッドやフィールドをオーバーライドして特定の動作を導入できます。これにより、元のクラスを変更することなく機能を追加でき、オープン/クローズ原則が促進されます。 つまり、クラスは拡張に対してオープンであり、変更に対してはクローズドとなります。加えて、継承はカプセル化をサポートし、実装の詳細を隠蔽したまま、サブクラスがスーパークラスの保護されたメンバーやpublicメンバーにアクセスできるようにします。これにより、インターフェイスと実装が明確に分離され、モジュール性が強化され、意図しない相互作用のリスクが減少します。


アクセス修飾子

アクセス修飾子とは、クラス、メソッド、関数、その他のメンバーへのアクセス権限を設定するためのキーワードです。これにより、コード内でこれらの要素にアクセスできる範囲を制御し、データの隠蔽とカプセル化を促進することができます。

以下は、アクセス修飾子の種類です。

  • public
  • private
  • protected

1. public

目的: クラス、関数、変数を他のクラスまたはプログラムで使用できるようにします。

2. private

目的:クラスのメンバーへのアクセスを制限し、データの整合性を保護します。

3. protected

目的: クラスのオブジェクトからのアクセスを制限しながら、サブクラスがメンバーを継承してアクセスできるようにします。


MQL5に関連する継承の例

まず、クラスとその関係および属性を視覚化するための例として、UMLクラス図を作成します。

UMLクラス図

UnitedStatesクラスとSwitzerlandクラスには、NewsDataクラスからの単一継承があります。

class NewsData
  {
private://Properties are only accessible from this class
   string            Country;//Private variable
   struct EventDetails//Private structure
     {
      int            EventID;
      string         EventName;
      datetime       EventDate;
     };
protected:
   //-- Protected Array Only accessible from this class and its children
   EventDetails      News[];
   //-- Proctected virtual void Function(to be expanded on via child classes)
   virtual void      SetNews();
   //-- Protected Function Only accessible from this class and its children
   void              SetCountry(string myCountry) {Country=myCountry;}
public:
   void              GetNews()//Public function to display 'News' array details
     {
      PrintFormat("+---------- %s ----------+",Country);
      for(uint i=0;i<News.Size();i++)
        {
         Print("ID: ",News[i].EventID," Name: ",News[i].EventName," Date: ",News[i].EventDate);
        }
     }
                     NewsData(void) {}//Class constructor
                    ~NewsData(void) {ArrayFree(News);}//Class destructor
  };

//+------------------------------------------------------------------+
//|(Subclass/Child) for 'NewsData'                                   |
//+------------------------------------------------------------------+
class UnitedStates:private NewsData
//private inheritance from NewsData,
//'UnitedStates' class's objects and children
//will not have access to 'NewsData' class's properties
  {
private:
   virtual void      SetNews()//private Function only Accessible in 'UnitedStates' class
     {
      ArrayResize(News,News.Size()+1,News.Size()+2);
      News[News.Size()-1].EventID = 1;
      News[News.Size()-1].EventName = "NFP(Non-Farm Payrolls)";
      News[News.Size()-1].EventDate = D'2024.01.03 14:00:00';
     }
public:
   void              myNews()//public Function accessible via class's object
     {
      SetCountry("United States");//Calling function from 'NewsData'
      GetNews();//Calling Function from private inherited class 'NewsData'
     }
                     UnitedStates(void) {SetNews();}//Class constructor
  };

//+------------------------------------------------------------------+
//|(Subclass/Child) for 'NewsData'                                   |
//+------------------------------------------------------------------+
class Switzerland: public NewsData
//public inheritance from NewsData
  {
public:
   virtual void      SetNews()//Public Function to set News data
     {
      ArrayResize(News,News.Size()+1,News.Size()+2);//Adjusting News structure array's size
      News[News.Size()-1].EventID = 0;//Setting event id to '0'
      News[News.Size()-1].EventName = "Interest Rate Decision";//Assigning event name
      News[News.Size()-1].EventDate = D'2024.01.06 10:00:00';//Assigning event date
     }
                     Switzerland(void) {SetCountry("Switerland"); SetNews();}//Class construct
  };

この例では、

親クラス(基底クラス、スーパークラス)はNewsDataで、private宣言されたメンバーはこのクラス内でのみアクセス可能です。private宣言されたメンバーには、クラスのオブジェクトや子クラスからアクセスできません。一方、protected宣言されたメンバーはクラスとその子クラスの両方からアクセス可能です。一方、すべてのpublic宣言されたメンバーは、クラスとその子クラス、オブジェクトからアクセス可能です。

以下は、NewsDataのアクセシビリティの表です。

クラスのプロパティ クラス オブジェクト
変数:Country(private)
構造:EventDetails(private)
変数:News(protected)
関数:SetNews(protected)
関数:SetCountry(protected)
関数:GetNews(public)

コンストラクタ:NewsData(public)
デストラクタ:~NewsData(public)
class NewsData
  {
private://Properties are only accessible from this class
   string            Country;//Private variable
   struct EventDetails//Private structure
     {
      int            EventID;
      string         EventName;
      datetime       EventDate;
     };
protected:
   //-- Protected Array Only accessible from this class and its children
   EventDetails      News[];
   //-- Proctected virtual void Function(to be expanded on via child classes)
   virtual void      SetNews();
   //-- Protected Function Only accessible from this class and its children
   void              SetCountry(string myCountry) {Country=myCountry;}
public:
   void              GetNews()//Public function to display 'News' array details
     {
      PrintFormat("+---------- %s ----------+",Country);
      for(uint i=0;i<News.Size();i++)
        {
         Print("ID: ",News[i].EventID," Name: ",News[i].EventName," Date: ",News[i].EventDate);
        }
     }
                     NewsData(void) {}//Class constructor
                    ~NewsData(void) {ArrayFree(News);}//Class destructor
  };

以下は、NewsDataオブジェクトからアクセス可能なプロパティです。  

オブジェクトからアクセス可能なプロパティオブジェクトからのpublic関数


以下は、NewsDataGetNews関数の結果です。

関数出力

継承は残りの両方のクラスで実装されます。

子クラス(サブクラス、派生クラス)のUnitedStatesで、親クラスのNewsDataをprivateに継承します。
つまり、子クラス(UnitedStates)は親クラス(NewsData)のprotectedプロパティとpublicプロパティにアクセスできますが、UnitedStatesクラスの子クラスとそのオブジェクトは親クラス(NewsData)のどのプロパティにもアクセスできません。継承アクセス修飾子がprotectedだった場合、UnitedStatesクラスの子は親(NewsData)クラスのprotectedプロパティとpublicプロパティの両方にアクセスできますが、子(UnitedStates)クラスのオブジェクトは親クラスのプロパティにアクセスできません。

以下は、UnitedStatesのアクセシビリティの表です。

クラスのプロパティ クラス オブジェクト
継承変数(private): Country(private)



継承構造体(private): EventDetails(private)



継承変数(private): News(protected)



継承関数(private): SetNews(protected)



継承関数(private): SetCountry(protected)



継承関数(private): GetNews(public)



継承コンストラクタ(private): NewsData(public)



継承デストラクタ(private):~NewsData(public)



関数:SetNews(private)


関数:myNews(public)


コンストラクタ:UnitedStates(public)


class UnitedStates:private NewsData
//private inheritance from NewsData,
//'UnitedStates' class's objects and children
//will not have access to 'NewsData' class's properties
  {
private:
   virtual void      SetNews()//private Function only Accessible in 'UnitedStates' class
     {
      ArrayResize(News,News.Size()+1,News.Size()+2);
      News[News.Size()-1].EventID = 1;
      News[News.Size()-1].EventName = "NFP(Non-Farm Payrolls)";
      News[News.Size()-1].EventDate = D'2024.01.03 14:00:00';
     }
public:
   void              myNews()//public Function accessible via class's object
     {
      SetCountry("United States");//Calling function from 'NewsData'
      GetNews();//Calling Function from private inherited class 'NewsData'
     }
                     UnitedStates(void) {SetNews();}//Class constructor
  };

以下は、UnitedStatesオブジェクトからアクセス可能なプロパティです。

オブジェクトからアクセス可能なプロパティNewsDataから継承されたprivate関数


NewsDataからprivateに継承されたGetNews関数にアクセスしようとするとコンパイルエラーが発生し、UnitedStatesオブジェクトがその関数にアクセスできなくなります。

コンパイルエラー

子クラス(サブクラス、派生クラス)Switzerland

継承アクセス修飾子はpublicです。これにより、子クラス(Switzerland)の子クラスは親(NewsData)クラスのpublicプロパティとprotectedプロパティにアクセスできるようになりますが、Switzerlandクラスのオブジェクトは関連するすべてのクラスのpublicプロパティにのみアクセスできます。

以下は、Switzerlandのアクセシビリティの表です。

クラスのプロパティ クラス オブジェクト
継承変数(public): Country(private)



継承構造体(public): EventDetails(private)



継承変数(public): News(protected)



継承関数(public): SetNews(protected)



継承関数(public): SetCountry(protected)



継承関数(public): GetNews(public)



継承コンストラクタ(public): NewsData(public)



継承デストラクタ(public):~NewsData(public)



関数:SetNews(public)


コンストラクタ:Switzerland(public)


class Switzerland: public NewsData
//public inheritance from NewsData
  {
public:
   virtual void      SetNews()//Public Function to set News data
     {
      ArrayResize(News,News.Size()+1,News.Size()+2);//Adjusting News structure array's size
      News[News.Size()-1].EventID = 0;//Setting event id to '0'
      News[News.Size()-1].EventName = "Interest Rate Decision";//Assigning event name
      News[News.Size()-1].EventDate = D'2024.01.06 10:00:00';//Assigning event date
     }
                     Switzerland(void) {SetCountry("Switerland"); SetNews();}//Class construct
  };

以下は、Switzerlandオブジェクトからアクセス可能なプロパティです。

オブジェクトからからアクセス可能なプロパティ

以下が結果です。

オブジェクトの結果


夏時間クラス

「ニュース取引が簡単に(第1回)」版

UMLクラス図

UMLクラス図(夏時間クラス)第1回

以下が、プロジェクトファイルです。

夏時間クラス第1回

前のコードでは、夏時間用のクラスが3つありました。

  • CDaylightSavings_AU
  • CDaylightSavings_UK
  • CDaylightSavings_US


「ニュース取引が簡単に(第2回)」版

UMLクラス図

UMLクラス図(夏時間クラス)第2回

以下が、プロジェクトファイルです。

夏時間クラス第2回

夏時間に関しては、以下のクラスがあります。

  • CDaylightSavings
  • CDaylightSavings_AU
  • CDaylightSavings_UK
  • CDaylightSavings_US


別の夏時間クラスを作成する理由

以前のクラスのコードでは、クラス間で多くの繰り返しがありました。これは、リスト内の異なる値に対して同じコードが何度も繰り返し書かれていたためです。この繰り返しを解消するために、類似のクラス間で共通する機能を1つの基底クラスにまとめ、その共通機能をさまざまな夏時間クラスで継承するように変更しました。


仮想関数

オブジェクト指向プログラミング(OOP)において、仮想関数は基底クラスのメンバー関数で、派生クラスでオーバーライドすることができます。関数が「仮想」として宣言されると、ポリモーフィズムが有効になり、派生クラスは基底クラスのポインターまたは参照を介して呼び出される関数の特定の実装を提供できるようになります。

以下が目的です。

  • ポリモーフィズム:仮想関数により、動的(実行時)ポリモーフィズムが可能になります。これにより、実行されるメソッドが参照やポインタの型ではなく、実際に参照されているオブジェクトの型に基づいて実行時に決定されます。
  • 柔軟性:派生クラスが基底クラスの動作を変更または拡張できるようにすることで、より柔軟で再利用可能なコードの実現が可能となります。
  • 分離:仮想関数はインターフェイスを実装から分離するため、コードの分離を促進し、基底クラスのインターフェイスを使用するコードに影響を与えずに、実装を変更することが容易になります。


CDaylightSavingsクラス

このクラスでは、以前のコードで繰り返し記述されていた共通の処理が1つのクラスにまとめられ、それぞれの夏時間スケジュールを初期化するためのいくつかの仮想関数が宣言されます。

CDaylightSavingsクラスは、CObjectクラスからの単一継承があります。

CDaylightSavingsクラスには、次のクラスからのインクルードがあります。

  •  CArrayObj 
  •  CTimeManagement

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+

#include <Object.mqh>

#include <Arrays\ArrayObj.mqh>
#include "../TimeManagement.mqh"

//+------------------------------------------------------------------+
//|DaylightSavings class                                             |
//+------------------------------------------------------------------+
class CDaylightSavings: public CObject
  {

protected:
   CTimeManagement   Time;
                     CDaylightSavings(datetime startdate,datetime enddate);
   CObject           *List() { return savings;}//Gets the list of Daylightsavings time
   datetime          StartDate;
   datetime          EndDate;
   CArrayObj         *savings;
   CArrayObj         *getSavings;
   CDaylightSavings      *dayLight;
   virtual void      SetDaylightSavings_UK();//Initialize UK Daylight Savings Dates into List
   virtual void      SetDaylightSavings_US();//Initialize US Daylight Savings Dates into List
   virtual void      SetDaylightSavings_AU();//Initialize AU Daylight Savings Dates into List

public:
                     CDaylightSavings(void);
                    ~CDaylightSavings(void);
   bool              isDaylightSavings(datetime Date);//This function checks if a given date falls within Daylight Savings Time.
   bool              DaylightSavings(int Year,datetime &startDate,datetime &endDate);//Check if DaylightSavings Dates are available for a certain Year
   string            adjustDaylightSavings(datetime EventDate);//Will adjust the date's timezone depending on DaylightSavings
  };


//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CDaylightSavings::CDaylightSavings(void)
  {
  }

//+------------------------------------------------------------------+
//|Initialize variables                                              |
//+------------------------------------------------------------------+
CDaylightSavings::CDaylightSavings(datetime startdate,datetime enddate)
  {
   StartDate = startdate;//Assign class's global variable StartDate value from parameter variable startdate
   EndDate = enddate;//Assign class's global variable EndDate value from parameter variable enddate
  }

//+------------------------------------------------------------------+
//|checks if a given date falls within Daylight Savings Time         |
//+------------------------------------------------------------------+
bool CDaylightSavings::isDaylightSavings(datetime Date)
  {
// Initialize a list to store daylight savings periods.
   getSavings = List();
// Iterate through all the periods in the list.
   for(int i=0; i<getSavings.Total(); i++)
     {
      // Access the current daylight savings period.
      dayLight = getSavings.At(i);
      // Check if the given date is within the current daylight savings period.
      if(Time.DateIsInRange(dayLight.StartDate,dayLight.EndDate,Date))
        {
         // If yes, return true indicating it is daylight savings time.
         return true;
        }
     }
// If no period matches, return false indicating it is not daylight savings time.
   return false;
  }

//+------------------------------------------------------------------+
//|Check if DaylightSavings Dates are available for a certain Year   |
//+------------------------------------------------------------------+
bool CDaylightSavings::DaylightSavings(int Year,datetime &startDate,datetime &endDate)
  {
// Initialize a list to store daylight savings periods.
   getSavings = List();
   bool startDateDetected=false,endDateDetected=false;
// Iterate through all the periods in the list.
   for(int i=0; i<getSavings.Total(); i++)
     {
      dayLight = getSavings.At(i);
      if(Year==Time.ReturnYear(dayLight.StartDate))//Check if a certain year's date is available within the DaylightSavings start dates in the List
        {
         startDate = dayLight.StartDate;
         startDateDetected = true;
        }
      if(Year==Time.ReturnYear(dayLight.EndDate))//Check if a certain year's date is available within the DaylightSavings end dates in the List
        {
         endDate = dayLight.EndDate;
         endDateDetected = true;
        }
      if(startDateDetected&&endDateDetected)//Check if both DaylightSavings start and end dates are found for a certain Year
        {
         return true;
        }
     }

   startDate = D'1970.01.01 00:00:00';//Set a default start date if no DaylightSaving date is found
   endDate = D'1970.01.01 00:00:00';//Set a default end date if no DaylightSaving date is found
   return false;
  }

//+------------------------------------------------------------------+
//|Will adjust the date's timezone depending on DaylightSavings      |
//+------------------------------------------------------------------+
string CDaylightSavings::adjustDaylightSavings(datetime EventDate)
  {
   if(isDaylightSavings(TimeTradeServer()))//Check if the current tradeserver time is already within the DaylightSavings Period
     {
      if(isDaylightSavings(EventDate))//Checks if the event time is during daylight savings
        {
         return TimeToString(EventDate);//normal event time
        }
      else
        {
         return TimeToString((datetime)(EventDate-Time.HoursS()));//event time minus an hour for DST
        }
     }
   else
     {
      if(isDaylightSavings(EventDate))//Checks if the event time is during daylight savings
        {
         return TimeToString((datetime)(Time.HoursS()+EventDate));//event time plus an hour for DST
        }
      else
        {
         return TimeToString(EventDate);//normal event time
        }
     }
  }

//+------------------------------------------------------------------+
//|Destructor                                                        |
//+------------------------------------------------------------------+
CDaylightSavings::~CDaylightSavings(void)
  {
   delete savings;//Delete CArrayObj Pointer
   delete dayLight;//Delete CDaylightSavings Pointer
   delete getSavings;//Delete CArrayObj Pointer
  }
//+------------------------------------------------------------------+


CDaylightSavings_AUクラス

このクラスでは、virtual void SetDaylightSavings_AUを拡張し、オーストラリアの夏時間スケジュールを追加します。

オーストラリアの夏時間の日付はこちらで確認できます。

CDaylightSavings_AUクラスには、次のクラスからのマルチレベル継承があります。

  • CDaylightSavings 
  • CObject

CDaylightSavings_AUクラスには、次のクラスからの階層的継承があります。

  • CDaylightSavings
  • CArrayObj
  • CTimeManagement

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+

#include "DaylightSavings.mqh"

//+------------------------------------------------------------------+
//|DaylightSavings_AU class                                          |
//+------------------------------------------------------------------+
class CDaylightSavings_AU: public CDaylightSavings
  {

public:

                     CDaylightSavings_AU(void);
  };

//+------------------------------------------------------------------+
//|Set Daylight Savings Schedule for Australia                       |
//+------------------------------------------------------------------+
void CDaylightSavings::SetDaylightSavings_AU()
  {
   savings = new CArrayObj();
//Daylight savings dates to readjust dates in the database for accurate testing in the strategy tester
   savings.Add(new CDaylightSavings(D'2006.10.29 03:00:00',D'2007.03.25 02:00:00'));
   savings.Add(new CDaylightSavings(D'2007.10.28 03:00:00',D'2008.04.06 02:00:00'));
   savings.Add(new CDaylightSavings(D'2008.10.05 03:00:00',D'2009.04.05 02:00:00'));
   savings.Add(new CDaylightSavings(D'2009.10.04 03:00:00',D'2010.04.04 02:00:00'));
   savings.Add(new CDaylightSavings(D'2010.10.03 03:00:00',D'2011.04.03 02:00:00'));
   savings.Add(new CDaylightSavings(D'2011.10.02 03:00:00',D'2012.04.01 02:00:00'));
   savings.Add(new CDaylightSavings(D'2012.10.07 03:00:00',D'2013.04.07 02:00:00'));
   savings.Add(new CDaylightSavings(D'2013.10.06 03:00:00',D'2014.04.06 02:00:00'));
   savings.Add(new CDaylightSavings(D'2014.10.05 03:00:00',D'2015.04.05 02:00:00'));
   savings.Add(new CDaylightSavings(D'2015.10.04 03:00:00',D'2016.04.03 02:00:00'));
   savings.Add(new CDaylightSavings(D'2016.10.02 03:00:00',D'2017.04.02 02:00:00'));
   savings.Add(new CDaylightSavings(D'2017.10.01 03:00:00',D'2018.04.01 02:00:00'));
   savings.Add(new CDaylightSavings(D'2018.10.07 03:00:00',D'2019.04.07 02:00:00'));
   savings.Add(new CDaylightSavings(D'2019.10.06 03:00:00',D'2020.04.05 02:00:00'));
   savings.Add(new CDaylightSavings(D'2020.10.04 03:00:00',D'2021.04.04 02:00:00'));
   savings.Add(new CDaylightSavings(D'2021.10.03 03:00:00',D'2022.04.03 02:00:00'));
   savings.Add(new CDaylightSavings(D'2022.10.02 03:00:00',D'2023.04.02 02:00:00'));
   savings.Add(new CDaylightSavings(D'2023.10.01 03:00:00',D'2024.04.07 02:00:00'));
   savings.Add(new CDaylightSavings(D'2024.10.06 03:00:00',D'2025.04.06 02:00:00'));
   savings.Add(new CDaylightSavings(D'2025.10.05 03:00:00',D'2026.04.05 02:00:00'));
   savings.Add(new CDaylightSavings(D'2026.10.04 03:00:00',D'2027.04.04 02:00:00'));
   savings.Add(new CDaylightSavings(D'2027.10.03 03:00:00',D'2028.04.02 02:00:00'));
   savings.Add(new CDaylightSavings(D'2028.10.01 03:00:00',D'2029.04.01 02:00:00'));
  }

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CDaylightSavings_AU::CDaylightSavings_AU(void)
  {
   SetDaylightSavings_AU();
  }
//+------------------------------------------------------------------+


CDaylightSavings_UKクラス

このクラスでは、virtual void SetDaylightSavings_UKを拡張し、ヨーロッパの夏時間スケジュールを追加します。

英国の夏時間の日付はこちらで確認できます。

CDaylightSavings_UKクラスには、次のクラスからのマルチレベル継承があります。

  • CDaylightSavings 
  • CObject

CDaylightSavings_UKクラスには、次のクラスからの階層的継承があります。

  • CDaylightSavings
  • CArrayObj
  • CTimeManagement

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+

#include "DaylightSavings.mqh"

//+------------------------------------------------------------------+
//|DaylightSavings_UK class                                          |
//+------------------------------------------------------------------+
class CDaylightSavings_UK: public CDaylightSavings
  {

public:

                     CDaylightSavings_UK(void);
  };

//+------------------------------------------------------------------+
//|Set Daylight Savings Schedule for Europe                          |
//+------------------------------------------------------------------+
void CDaylightSavings::SetDaylightSavings_UK()
  {
   savings = new CArrayObj();
//Daylight savings dates to readjust dates in the database for accurate testing in the strategy tester
   savings.Add(new CDaylightSavings(D'2007.03.25 02:00:00',D'2007.10.28 01:00:00'));
   savings.Add(new CDaylightSavings(D'2008.03.30 02:00:00',D'2008.10.26 01:00:00'));
   savings.Add(new CDaylightSavings(D'2009.03.29 02:00:00',D'2009.10.25 01:00:00'));
   savings.Add(new CDaylightSavings(D'2010.03.28 02:00:00',D'2010.10.31 01:00:00'));
   savings.Add(new CDaylightSavings(D'2011.03.27 02:00:00',D'2011.10.30 01:00:00'));
   savings.Add(new CDaylightSavings(D'2012.03.25 02:00:00',D'2012.10.28 01:00:00'));
   savings.Add(new CDaylightSavings(D'2013.03.31 02:00:00',D'2013.10.27 01:00:00'));
   savings.Add(new CDaylightSavings(D'2014.03.30 02:00:00',D'2014.10.26 01:00:00'));
   savings.Add(new CDaylightSavings(D'2015.03.29 02:00:00',D'2015.10.25 01:00:00'));
   savings.Add(new CDaylightSavings(D'2016.03.27 02:00:00',D'2016.10.30 01:00:00'));
   savings.Add(new CDaylightSavings(D'2017.03.26 02:00:00',D'2017.10.29 01:00:00'));
   savings.Add(new CDaylightSavings(D'2018.03.25 02:00:00',D'2018.10.28 01:00:00'));
   savings.Add(new CDaylightSavings(D'2019.03.31 02:00:00',D'2019.10.27 01:00:00'));
   savings.Add(new CDaylightSavings(D'2020.03.29 02:00:00',D'2020.10.25 01:00:00'));
   savings.Add(new CDaylightSavings(D'2021.03.28 02:00:00',D'2021.10.31 01:00:00'));
   savings.Add(new CDaylightSavings(D'2022.03.27 02:00:00',D'2022.10.30 01:00:00'));
   savings.Add(new CDaylightSavings(D'2023.03.26 02:00:00',D'2023.10.29 01:00:00'));
   savings.Add(new CDaylightSavings(D'2024.03.31 02:00:00',D'2024.10.27 01:00:00'));
   savings.Add(new CDaylightSavings(D'2025.03.30 02:00:00',D'2025.10.26 01:00:00'));
   savings.Add(new CDaylightSavings(D'2026.03.29 02:00:00',D'2026.10.25 01:00:00'));
   savings.Add(new CDaylightSavings(D'2027.03.28 02:00:00',D'2027.10.31 01:00:00'));
   savings.Add(new CDaylightSavings(D'2028.03.26 02:00:00',D'2028.10.29 01:00:00'));
   savings.Add(new CDaylightSavings(D'2029.03.25 02:00:00',D'2029.10.28 01:00:00'));
  }

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CDaylightSavings_UK::CDaylightSavings_UK(void)
  {
   SetDaylightSavings_UK();
  }
//+------------------------------------------------------------------+


CDaylightSavings_USクラス

このクラスでは、virtual void SetDaylightSavings_USを拡張し、米国の夏時間スケジュールを追加します。

米国の夏時間の日付はこちらで確認できます。

CDaylightSavings_USクラスには、次のクラスからのマルチレベル継承があります。

  • CDaylightSavings 
  • CObject

CDaylightSavings_USクラスには、次のクラスからの階層的継承があります。

  • CDaylightSavings
  • CArrayObj
  • CTimeManagement

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+

#include "DaylightSavings.mqh"

//+------------------------------------------------------------------+
//|DaylightSavings_US Class                                          |
//+------------------------------------------------------------------+
class CDaylightSavings_US: public CDaylightSavings
  {

public:

                     CDaylightSavings_US(void);
  };

//+------------------------------------------------------------------+
//|Set Daylight Savings Schedule for the United States               |
//+------------------------------------------------------------------+
void CDaylightSavings::SetDaylightSavings_US()
  {
   savings = new CArrayObj();
//Daylight savings dates to readjust dates in the database for accurate testing in the strategy tester
   savings.Add(new CDaylightSavings(D'2007.03.11 03:00:00',D'2007.11.04 01:00:00'));
   savings.Add(new CDaylightSavings(D'2008.03.09 03:00:00',D'2008.11.02 01:00:00'));
   savings.Add(new CDaylightSavings(D'2009.03.08 03:00:00',D'2009.11.01 01:00:00'));
   savings.Add(new CDaylightSavings(D'2010.03.14 03:00:00',D'2010.11.07 01:00:00'));
   savings.Add(new CDaylightSavings(D'2011.03.13 03:00:00',D'2011.11.06 01:00:00'));
   savings.Add(new CDaylightSavings(D'2012.03.11 03:00:00',D'2012.11.04 01:00:00'));
   savings.Add(new CDaylightSavings(D'2013.03.10 03:00:00',D'2013.11.03 01:00:00'));
   savings.Add(new CDaylightSavings(D'2014.03.09 03:00:00',D'2014.11.02 01:00:00'));
   savings.Add(new CDaylightSavings(D'2015.03.08 03:00:00',D'2015.11.01 01:00:00'));
   savings.Add(new CDaylightSavings(D'2016.03.13 03:00:00',D'2016.11.06 01:00:00'));
   savings.Add(new CDaylightSavings(D'2017.03.12 03:00:00',D'2017.11.05 01:00:00'));
   savings.Add(new CDaylightSavings(D'2018.03.11 03:00:00',D'2018.11.04 01:00:00'));
   savings.Add(new CDaylightSavings(D'2019.03.10 03:00:00',D'2019.11.03 01:00:00'));
   savings.Add(new CDaylightSavings(D'2020.03.08 03:00:00',D'2020.11.01 01:00:00'));
   savings.Add(new CDaylightSavings(D'2021.03.14 03:00:00',D'2021.11.07 01:00:00'));
   savings.Add(new CDaylightSavings(D'2022.03.13 03:00:00',D'2022.11.06 01:00:00'));
   savings.Add(new CDaylightSavings(D'2023.03.12 03:00:00',D'2023.11.05 01:00:00'));
   savings.Add(new CDaylightSavings(D'2024.03.10 03:00:00',D'2024.11.03 01:00:00'));
   savings.Add(new CDaylightSavings(D'2025.03.09 03:00:00',D'2025.11.02 01:00:00'));
   savings.Add(new CDaylightSavings(D'2026.03.08 03:00:00',D'2026.11.01 01:00:00'));
   savings.Add(new CDaylightSavings(D'2027.03.14 03:00:00',D'2027.11.07 01:00:00'));
   savings.Add(new CDaylightSavings(D'2028.03.12 03:00:00',D'2028.11.05 01:00:00'));
   savings.Add(new CDaylightSavings(D'2029.03.11 03:00:00',D'2029.11.04 01:00:00'));
  }

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CDaylightSavings_US::CDaylightSavings_US(void)
  {
   SetDaylightSavings_US();
  }
//+------------------------------------------------------------------+


銘柄プロパティクラス

このクラスでは、データを取得する銘柄を設定します。このクラスは、他のクラス内の銘柄プロパティを取得するための簡単で迅速なメソッドを提供し、コードの冗長性を排除します。

リストを取得するためにプロパティの組み合わせを選択しますが、リストは拡張できますが、拡張できない場合は、リストは次のようになります。

  • Ask価格
  • Bid価格
  • 契約サイズ
  • 最小取引量
  • 最大取引量
  • 取引量ステップ
  • 取引量制限
  • スプレッド
  • ストップレベル
  • フリーズレベル
  • 銘柄の時間
  • 銘柄の正規化価格
  • 銘柄の桁数
  • 銘柄のポイント
  • 銘柄の取引モード
  • 銘柄の注文取引量の合計
  • 銘柄のポジション取引量の合計
  • 銘柄の基本通貨
  • 銘柄の利益通貨
  • 銘柄の証拠金通貨
  • 銘柄のカスタムステータス
  • 銘柄の背景色

CSymbolPropertiesクラスには、CSymbolInfoクラスからのインクルードがあります。

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+
#include <Trade/SymbolInfo.mqh>
//+------------------------------------------------------------------+
//|SymbolProperties class                                            |
//+------------------------------------------------------------------+
class CSymbolProperties
  {
private:
   double            ASK;//Store Ask Price
   double            BID;//Store Bid Price
   double            LOTSMIN;//Store Minimum Lotsize
   double            LOTSMAX;//Store Maximum Lotsize
   double            LOTSSTEP;//Store Lotsize Step
   double            LOTSLIMIT;//Store Lotsize Limit(Maximum sum of Volume)
   long              SPREAD;//Store Spread value
   long              STOPLEVEL;//Store Stop level
   long              FREEZELEVEL;//Store Freeze level
   long              TIME;//Store time
   long              DIGITS;//Store Digits
   double            POINT;//Store Point
   double            ORDERSVOLUME;//Store Orders volume
   double            POSITIONSVOLUME;//Store Positions volume
   long              CUSTOM;//Store if Symbol is Custom
   long              BACKGROUND_CLR;//Store Symbol's background color

protected:
   CSymbolInfo       CSymbol;//Creating class CSymbolInfo's Object
   bool              SetSymbolName(string SYMBOL)
     {
      //-- If Symbol's name was successfully set.
      if(!CSymbol.Name((SYMBOL==NULL)?Symbol():SYMBOL))
        {
         Print("Invalid Symbol: ",SYMBOL);
         return false;
        }
      return true;
     }

   //-- Retrieve Symbol's name
   string            GetSymbolName()
     {
      return CSymbol.Name();
     }

public:
                     CSymbolProperties(void);//Constructor
   double            Ask(string SYMBOL=NULL);//Retrieve Ask Price
   double            Bid(string SYMBOL=NULL);//Retrieve Bid Price
   double            ContractSize(string SYMBOL=NULL);//Retrieve Contract Size
   double            LotsMin(string SYMBOL=NULL);//Retrieve Min Volume
   double            LotsMax(string SYMBOL=NULL);//Retrieve Max Volume
   double            LotsStep(string SYMBOL=NULL);//Retrieve Volume Step
   double            LotsLimit(string SYMBOL=NULL);//Retrieve Volume Limit
   int               Spread(string SYMBOL=NULL);//Retrieve Spread
   int               StopLevel(string SYMBOL=NULL);//Retrieve Stop Level
   int               FreezeLevel(string SYMBOL=NULL);//Retrieve Freeze Level
   datetime          Time(string SYMBOL=NULL);//Retrieve Symbol's Time
   //-- Normalize Price
   double            NormalizePrice(const double price,string SYMBOL=NULL);
   int               Digits(string SYMBOL=NULL);//Retrieve Symbol's Digits
   double            Point(string SYMBOL=NULL);//Retrieve Symbol's Point
   ENUM_SYMBOL_TRADE_MODE TradeMode(string SYMBOL=NULL);//Retrieve Symbol's Trade Mode
   double            OrdersVolume(string SYMBOL=NULL);//Retrieve Symbol's Orders Volume
   double            PositionsVolume(string SYMBOL=NULL);//Retrieve Symbol's Positions Volume
   string            CurrencyBase(string SYMBOL=NULL);//Retrieve Symbol's Currency Base
   string            CurrencyProfit(string SYMBOL=NULL);//Retrieve Symbol's Currency Profit
   string            CurrencyMargin(string SYMBOL=NULL);//Retrieve Symbol's Currency Margin
   bool              Custom(string SYMBOL=NULL);//Retrieve Symbol's Custom status
   color             SymbolBackground(string SYMBOL=NULL,bool allow_black=false);//Retrieve Symbol's Background color
  };

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
//Initializing Variables
CSymbolProperties::CSymbolProperties(void):ASK(0.0),BID(0.0),
   LOTSMIN(0.0),LOTSMAX(0.0),
   LOTSSTEP(0.0),LOTSLIMIT(0.0),DIGITS(0),
   SPREAD(0),STOPLEVEL(0),ORDERSVOLUME(0.0),
   FREEZELEVEL(0),TIME(0),POINT(0.0),POSITIONSVOLUME(0.0),
   CUSTOM(0),BACKGROUND_CLR(0)
  {
  }

//+------------------------------------------------------------------+
//|Retrieve Ask Price                                                |
//+------------------------------------------------------------------+
double CSymbolProperties::Ask(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_ASK,ASK))
        {
         return ASK;
        }
     }
   Print("Unable to retrieve Symbol's Ask Price");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Bid Price                                                |
//+------------------------------------------------------------------+
double CSymbolProperties::Bid(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_BID,BID))
        {
         return BID;
        }
     }
   Print("Unable to retrieve Symbol's Bid Price");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Contract Size                                            |
//+------------------------------------------------------------------+
double CSymbolProperties::ContractSize(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.ContractSize();
        }
     }
   Print("Unable to retrieve Symbol's Contract size");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Min Volume                                               |
//+------------------------------------------------------------------+
double CSymbolProperties::LotsMin(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_MIN,LOTSMIN))
        {
         return LOTSMIN;
        }
     }
   Print("Unable to retrieve Symbol's LotsMin");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Max Volume                                               |
//+------------------------------------------------------------------+
double CSymbolProperties::LotsMax(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_MAX,LOTSMAX))
        {
         return LOTSMAX;
        }
     }
   Print("Unable to retrieve Symbol's LotsMax");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Volume Step                                              |
//+------------------------------------------------------------------+
double CSymbolProperties::LotsStep(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_STEP,LOTSSTEP))
        {
         return LOTSSTEP;
        }
     }
   Print("Unable to retrieve Symbol's LotsStep");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Volume Limit                                             |
//+------------------------------------------------------------------+
double CSymbolProperties::LotsLimit(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_LIMIT,LOTSLIMIT))
        {
         return LOTSLIMIT;
        }
     }
   Print("Unable to retrieve Symbol's LotsLimit");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Spread                                                   |
//+------------------------------------------------------------------+
int CSymbolProperties::Spread(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_SPREAD,SPREAD))
        {
         return int(SPREAD);
        }
     }
   Print("Unable to retrieve Symbol's Spread");
   return 0;
  }

//+------------------------------------------------------------------+
//|Retrieve Stop Level                                               |
//+------------------------------------------------------------------+
int CSymbolProperties::StopLevel(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_TRADE_STOPS_LEVEL,STOPLEVEL))
        {
         return int(STOPLEVEL);
        }
     }
   Print("Unable to retrieve Symbol's StopLevel");
   return 0;
  }

//+------------------------------------------------------------------+
//|Retrieve Freeze Level                                             |
//+------------------------------------------------------------------+
int CSymbolProperties::FreezeLevel(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_TRADE_FREEZE_LEVEL,FREEZELEVEL))
        {
         return int(FREEZELEVEL);
        }
     }
   Print("Unable to retrieve Symbol's FreezeLevel");
   return 0;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Time                                            |
//+------------------------------------------------------------------+
datetime CSymbolProperties::Time(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_TIME,TIME))
        {
         return datetime(TIME);
        }
     }
   Print("Unable to retrieve Symbol's Time");
   TIME=0;
   return datetime(TIME);
  }

//+------------------------------------------------------------------+
//|Normalize Price                                                   |
//+------------------------------------------------------------------+
double CSymbolProperties::NormalizePrice(const double price,string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh()&&CSymbol.RefreshRates())
        {
         return CSymbol.NormalizePrice(price);
        }
     }
   Print("Unable to Normalize Symbol's Price");
   return price;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Digits                                          |
//+------------------------------------------------------------------+
int CSymbolProperties::Digits(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_DIGITS,DIGITS))
        {
         return int(DIGITS);
        }
     }
   Print("Unable to retrieve Symbol's Digits");
   return 0;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Point                                           |
//+------------------------------------------------------------------+
double CSymbolProperties::Point(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_POINT,POINT))
        {
         return POINT;
        }
     }
   Print("Unable to retrieve Symbol's Point");
   return 0.0;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Trade Mode                                      |
//+------------------------------------------------------------------+
ENUM_SYMBOL_TRADE_MODE CSymbolProperties::TradeMode(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.TradeMode();
        }
     }
   Print("Unable to retrieve Symbol's TradeMode");
   return SYMBOL_TRADE_MODE_DISABLED;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Orders Volume                                   |
//+------------------------------------------------------------------+
double CSymbolProperties::OrdersVolume(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      for(int i=0; i<OrdersTotal(); i++)
        {
         if(OrderSelect(OrderGetTicket(i)))
           {
            if(OrderGetString(ORDER_SYMBOL)==GetSymbolName())
              {
               ORDERSVOLUME+=OrderGetDouble(ORDER_VOLUME_CURRENT);
              }
           }
        }
     }
   else
     {
      Print("Unable to retrieve Symbol's OrdersVolume");
      return 0.0;
     }
   return ORDERSVOLUME;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Positions Volume                                |
//+------------------------------------------------------------------+
double CSymbolProperties::PositionsVolume(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      for(int i=0; i<PositionsTotal(); i++)
        {
         if(PositionGetTicket(i)>0)
           {
            if(PositionGetString(POSITION_SYMBOL)==GetSymbolName())
              {
               POSITIONSVOLUME+=PositionGetDouble(POSITION_VOLUME);
              }
           }
        }
     }
   else
     {
      Print("Unable to retrieve Symbol's PositionsVolume");
      return 0.0;
     }
   return POSITIONSVOLUME;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Currency Base                                   |
//+------------------------------------------------------------------+
string CSymbolProperties::CurrencyBase(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.CurrencyBase();
        }
     }
   Print("Unable to retrieve Symbol's CurrencyBase");
   return "";
  }
//+------------------------------------------------------------------+
//|Retrieve Symbol's Currency Profit                                 |
//+------------------------------------------------------------------+
string CSymbolProperties::CurrencyProfit(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.CurrencyProfit();
        }
     }
   Print("Unable to retrieve Symbol's CurrencyProfit");
   return "";
  }
//+------------------------------------------------------------------+
//|Retrieve Symbol's Currency Margin                                 |
//+------------------------------------------------------------------+
string CSymbolProperties::CurrencyMargin(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.CurrencyMargin();
        }
     }
   Print("Unable to retrieve Symbol's CurrencyMargin");
   return "";
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Custom status                                   |
//+------------------------------------------------------------------+
bool CSymbolProperties::Custom(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_CUSTOM,CUSTOM))
        {
         return bool(CUSTOM);
        }
     }
   Print("Unable to retrieve if Symbol is Custom");
   return false;
  }

//+------------------------------------------------------------------+
//|Retrieve Symbol's Background color                                |
//+------------------------------------------------------------------+
color CSymbolProperties::SymbolBackground(string SYMBOL=NULL,bool allow_black=false)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_BACKGROUND_COLOR,BACKGROUND_CLR))
        {
         /*Avoid any Symbol black background color */
         BACKGROUND_CLR = ((ColorToString(color(BACKGROUND_CLR))=="0,0,0"||
                            color(BACKGROUND_CLR)==clrBlack)&&!allow_black)?
                          long(StringToColor("236,236,236")):BACKGROUND_CLR;
         return color(BACKGROUND_CLR);
        }
     }
   Print("Unable to retrieve Symbol's Background color");
   return color(StringToColor("236,236,236"));//Retrieve a lightish gray color
  }
//+------------------------------------------------------------------+

以下のクラスコンストラクタでは、double型変数ASKなどの以前に宣言した変数を初期化し、銘柄のAsk価格がまだないため、ASKに値0.0を割り当てます。

//Initializing Variables
CSymbolProperties::CSymbolProperties(void):ASK(0.0),BID(0.0),
   LOTSMIN(0.0),LOTSMAX(0.0),
   LOTSSTEP(0.0),LOTSLIMIT(0.0),DIGITS(0),
   SPREAD(0),STOPLEVEL(0),ORDERSVOLUME(0.0),
   FREEZELEVEL(0),TIME(0),POINT(0.0),POSITIONSVOLUME(0.0),
   CUSTOM(0),BACKGROUND_CLR(0)
  {
  }

以下のコードでは、一連の手順を実行して、最終的に銘柄のAsk価格を取得します。

Ask価格

1. まず、変数SYMBOLに銘柄の名前を入力または編集できるオプションのパラメータがあります。 

2. 次に、銘柄の名前をパラメータ値に設定します。パラメータ値がまだデフォルト値のNULLである場合は、現在のチャートの銘柄(Symbol())の銘柄のプロパティが必要であると想定します。

3. 銘柄名が見つからない場合は、銘柄のAsk価格を取得できないことをユーザーに通知するエラーメッセージを出力し、0.0を返します。

4. 銘柄の名前を設定できたら、その特定の銘柄のAsk価格を取得し、その値を返します。

double CSymbolProperties::Ask(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))
     {
      if(CSymbol.InfoDouble(SYMBOL_ASK,ASK))
        {
         return ASK;
        }
     }
   Print("Unable to retrieve Symbol's Ask Price");
   return 0.0;
  }
   bool              SetSymbolName(string SYMBOL)
     {
      //-- If Symbol's name was successfully set.
      if(!CSymbol.Name((SYMBOL==NULL)?Symbol():SYMBOL))
        {
         Print("Invalid Symbol: ",SYMBOL);
         return false;
        }
      return true;
     }

以下の関数はSYMBOL変数に基づいて銘柄のBid価格を取得します。

Bid価格

double CSymbolProperties::Bid(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_BID,BID))
        {
         return BID;
        }
     }
   Print("Unable to retrieve Symbol's Bid Price");
   return 0.0;
  }

以下の関数は銘柄の契約サイズを取得します。銘柄の契約サイズはトレーダーの取引リスクに直接影響します。契約サイズが大きいほど、個々の取引で負うリスクも増大します。逆に、契約サイズが小さいほど、取引リスクは抑えられます。

契約サイズ

double CSymbolProperties::ContractSize(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.ContractSize();
        }
     }
   Print("Unable to retrieve Symbol's Contract size");
   return 0.0;
  }

以下の関数は銘柄の最小許容ロットサイズ/取引量を取得します。トレーダーは最小ロットサイズより小さいロットサイズでポジションを開くことはできません。

最小取引量

double CSymbolProperties::LotsMin(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_MIN,LOTSMIN))
        {
         return LOTSMIN;
        }
     }
   Print("Unable to retrieve Symbol's LotsMin");
   return 0.0;
  }

以下の関数は銘柄の最大許容ロットサイズ/取引量を取得します。トレーダーは最大値よりも大きいロットサイズ/取引量でポジションを開くことはできませんが、ブローカーの取引量制限と口座の注文制限に応じて、最大値よりも大きいロットサイズ/取引量になる可能性のある複数のポジションを開くことができます。

最大取引量


double CSymbolProperties::LotsMax(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_MAX,LOTSMAX))
        {
         return LOTSMAX;
        }
     }
   Print("Unable to retrieve Symbol's LotsMax");
   return 0.0;
  }

以下の関数は銘柄の取引量/ロットサイズのステップを取得します。つまり、ロットサイズはこの値の間隔を持つ必要があります。たとえば、取引量ステップが1の場合、トレーダーはロットサイズ/取引量1.5を選択できません。 

取引量ステップ


double CSymbolProperties::LotsStep(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_STEP,LOTSSTEP))
        {
         return LOTSSTEP;
        }
     }
   Print("Unable to retrieve Symbol's LotsStep");
   return 0.0;
  }

以下の関数は銘柄の取引量/ロットサイズの制限を取得します。これは、特定の銘柄のトレーダーの口座に制限が適用される前に設定できる取引量/ロットサイズの合計です。

取引量制限
double CSymbolProperties::LotsLimit(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_VOLUME_LIMIT,LOTSLIMIT))
        {
         return LOTSLIMIT;
        }
     }
   Print("Unable to retrieve Symbol's LotsLimit");
   return 0.0;
  }

以下の関数は銘柄のスプレッドを取得します。スプレッドはトレーダーに影響を与え、スプレッドが高いほどトレーダーの収益性は低下します。スプレッドの大きさによって、戦略が収益性を持つかどうかが決まります。もちろん、戦略が収益性を持たない状況も多々ありますが、銘柄のスプレッドは重要な要素となる場合があります。スプレッドは本質的にブローカーの収入の一種であり、その銘柄に対するブローカーの税金と考えることができます。

スプレッド

int CSymbolProperties::Spread(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_SPREAD,SPREAD))
        {
         return int(SPREAD);
        }
     }
   Print("Unable to retrieve Symbol's Spread");
   return 0;
  }

以下の関数は銘柄のストップレベルを取得します。これは、始値とストップロスまたはテイクプロフィット間の最小距離、および現在のAskまたはBid価格と注文開始価格間の最小距離に対する制限です。

ストップレベル

int CSymbolProperties::StopLevel(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_TRADE_STOPS_LEVEL,STOPLEVEL))
        {
         return int(STOPLEVEL);
        }
     }
   Print("Unable to retrieve Symbol's StopLevel");
   return 0;
  }

以下の関数は銘柄のフリーズレベルを取得します。これは、取引の終了が許可されるために価格が始値から移動する必要がある最小距離です(特定の取引を利益または損失で終了することが許可されている場合)。

int CSymbolProperties::FreezeLevel(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_TRADE_FREEZE_LEVEL,FREEZELEVEL))
        {
         return int(FREEZELEVEL);
        }
     }
   Print("Unable to retrieve Symbol's FreezeLevel");
   return 0;
  }

以下の関数は銘柄の時間を取得します。

時間

datetime CSymbolProperties::Time(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_TIME,TIME))
        {
         return datetime(TIME);
        }
     }
   Print("Unable to retrieve Symbol's Time");
   TIME=0;
   return datetime(TIME);
  }

以下の関数は特定の銘柄の価格を正規化します。

たとえば、EURUSDのAsk価格が1.07735で、価格レベル1.077351で買い取引を開始しようとした場合、小数点以下の桁数が許容範囲(例:5桁)を超えているため、無効な価格に関するエラーが発生する可能性があります。この関数は6桁の価格を5桁に変換し、価格を正規化します。

double CSymbolProperties::NormalizePrice(const double price,string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh()&&CSymbol.RefreshRates())
        {
         return CSymbol.NormalizePrice(price);
        }
     }
   Print("Unable to Normalize Symbol's Price");
   return price;
  }

以下の関数は銘柄の桁数を取得します。桁数は銘柄の価格の小数点以下の桁数として表されます。

数字

銘柄の小数点以下

int CSymbolProperties::Digits(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_DIGITS,DIGITS))
        {
         return int(DIGITS);
        }
     }
   Print("Unable to retrieve Symbol's Digits");
   return 0;
  }

以下の関数は銘柄のポイントを取得します。

double CSymbolProperties::Point(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoDouble(SYMBOL_POINT,POINT))
        {
         return POINT;
        }
     }
   Print("Unable to retrieve Symbol's Point");
   return 0.0;
  }

以下の関数は銘柄の取引モードを取得します。

取引モード

ENUM_SYMBOL_TRADE_MODE CSymbolProperties::TradeMode(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.TradeMode();
        }
     }
   Print("Unable to retrieve Symbol's TradeMode");
   return SYMBOL_TRADE_MODE_DISABLED;
  }

以下の関数は銘柄の注文の数量/ロットの合計を取得します。

double CSymbolProperties::OrdersVolume(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      for(int i=0; i<OrdersTotal(); i++)
        {
         if(OrderSelect(OrderGetTicket(i)))
           {
            if(OrderGetString(ORDER_SYMBOL)==GetSymbolName())
              {
               ORDERSVOLUME+=OrderGetDouble(ORDER_VOLUME_CURRENT);
              }
           }
        }
     }
   else
     {
      Print("Unable to retrieve Symbol's OrdersVolume");
      return 0.0;
     }
   return ORDERSVOLUME;
  }

以下の関数は銘柄のポジションの取引量/ロットの合計を取得します。

double CSymbolProperties::PositionsVolume(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      for(int i=0; i<PositionsTotal(); i++)
        {
         if(PositionGetTicket(i)>0)
           {
            if(PositionGetString(POSITION_SYMBOL)==GetSymbolName())
              {
               POSITIONSVOLUME+=PositionGetDouble(POSITION_VOLUME);
              }
           }
        }
     }
   else
     {
      Print("Unable to retrieve Symbol's PositionsVolume");
      return 0.0;
     }
   return POSITIONSVOLUME;
  }

以下の関数は銘柄の基本通貨を取得します。

string CSymbolProperties::CurrencyBase(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.CurrencyBase();
        }
     }
   Print("Unable to retrieve Symbol's CurrencyBase");
   return "";
  }

以下の関数は銘柄の利益通貨を取得します。

利益通貨

string CSymbolProperties::CurrencyProfit(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.CurrencyProfit();
        }
     }
   Print("Unable to retrieve Symbol's CurrencyProfit");
   return "";
  }

以下の関数は銘柄の証拠金通貨を取得します。

証拠金通貨

string CSymbolProperties::CurrencyMargin(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.Refresh())
        {
         return CSymbol.CurrencyMargin();
        }
     }
   Print("Unable to retrieve Symbol's CurrencyMargin");
   return "";
  }

以下の関数は銘柄がカスタムかどうかを識別するためのブール値を取得します。

bool CSymbolProperties::Custom(string SYMBOL=NULL)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_CUSTOM,CUSTOM))
        {
         return bool(CUSTOM);
        }
     }
   Print("Unable to retrieve if Symbol is Custom");
   return false;
  }

以下の関数は銘柄の背景色を取得します。オプションのパラメータのallow_blackallow_blackはデフォルトでfalseに設定されています。このパラメータは、後でチャートの背景色を銘柄に応じて設定する際に使用されます。allow_blackをtrueに設定すると、チャート内で黒色の使用が許可されますが、その結果、チャートの他の要素も黒色になるため、視認性が低下し、読みづらくなる可能性があります。

以下は、後で確立される新しいチャート形式を使用した黒背景チャートの例です。

黒背景

color CSymbolProperties::SymbolBackground(string SYMBOL=NULL,bool allow_black=false)
  {
   if(SetSymbolName(SYMBOL))//Set Symbol
     {
      if(CSymbol.InfoInteger(SYMBOL_BACKGROUND_COLOR,BACKGROUND_CLR))
        {
        /*Avoid any Symbol black background color */
         BACKGROUND_CLR = ((ColorToString(color(BACKGROUND_CLR))=="0,0,0"||
                            color(BACKGROUND_CLR)==clrBlack)&&!allow_black)?
                          long(StringToColor("236,236,236")):BACKGROUND_CLR;
         return color(BACKGROUND_CLR);
        }
     }
   Print("Unable to retrieve Symbol's Background color");
   return color(StringToColor("236,236,236"));//Retrieve a lightish gray color
  }


時間管理クラス

このクラスでは、クラスの関数に追加された新しい機能について説明します。このクラスの目的は、時間データを操作したり対話したりすることです。

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//|TimeManagement class                                              |
//+------------------------------------------------------------------+
class CTimeManagement
  {

private:

   MqlDateTime       today;//private variable
   MqlDateTime       timeFormat;//private variable

public:

   //-- Checks if a date is within two other dates
   bool              DateIsInRange(datetime FirstTime,datetime SecondTime,datetime compareTime);
   //-- Check if two dates(Start&End) are within CompareStart & CompareEnd
   bool              DateIsInRange(datetime Start,datetime End,datetime CompareStart,datetime CompareEnd);
   bool              DateisToday(datetime TimeRepresented);//Checks if a date is within the current day
   int               SecondsS(int multiple=1);//Returns seconds
   int               MinutesS(int multiple=1);//Returns Minutes in seconds
   int               HoursS(int multiple=1);//Returns Hours in seconds
   int               DaysS(int multiple=1);//Returns Days in seconds
   int               WeeksS(int multiple=1);//Returns Weeks in seconds
   int               MonthsS(int multiple=1);//Returns Months in seconds
   int               YearsS(int multiple=1);//Returns Years in seconds
   int               ReturnYear(datetime time);//Returns the Year for a specific date
   int               ReturnMonth(datetime time);//Returns the Month for a specific date
   int               ReturnDay(datetime time);//Returns the Day for a specific date
   //-- Will return a datetime type of a date with an subtraction offset in seconds
   datetime          TimeMinusOffset(datetime standardtime,int timeoffset);
   //-- Will return a datetime type of a date with an addition offset in seconds
   datetime          TimePlusOffset(datetime standardtime,int timeoffset);
  };

//+------------------------------------------------------------------+
//|Checks if a date is within two other dates                        |
//+------------------------------------------------------------------+
bool CTimeManagement::DateIsInRange(datetime FirstTime,datetime SecondTime,datetime compareTime)
  {
   return(FirstTime<=compareTime&&SecondTime>compareTime);
  }

//+------------------------------------------------------------------+
//|Check if two dates(Start&End) are within CompareStart & CompareEnd|
//+------------------------------------------------------------------+
bool CTimeManagement::DateIsInRange(datetime Start,datetime End,datetime CompareStart,datetime CompareEnd)
  {
   return(Start<=CompareStart&&CompareEnd<End);
  }

//+------------------------------------------------------------------+
//|Checks if a date is within the current day                        |
//+------------------------------------------------------------------+
bool CTimeManagement::DateisToday(datetime TimeRepresented)
  {
   MqlDateTime TiM;
   TimeToStruct(TimeRepresented,TiM);
   TimeCurrent(today);
   return(TiM.year==today.year&&TiM.mon==today.mon&&TiM.day==today.day);
  }

//+------------------------------------------------------------------+
//|Returns seconds                                                   |
//+------------------------------------------------------------------+
int CTimeManagement::SecondsS(int multiple=1)
  {
   return (1*multiple);
  }

//+------------------------------------------------------------------+
//|Returns Minutes in seconds                                        |
//+------------------------------------------------------------------+
int CTimeManagement::MinutesS(int multiple=1)
  {
   return (SecondsS(60)*multiple);
  }

//+------------------------------------------------------------------+
//|Returns Hours in seconds                                          |
//+------------------------------------------------------------------+
int CTimeManagement::HoursS(int multiple=1)
  {
   return (MinutesS(60)*multiple);
  }

//+------------------------------------------------------------------+
//|Returns Days in seconds                                           |
//+------------------------------------------------------------------+
int CTimeManagement::DaysS(int multiple=1)
  {
   return (HoursS(24)*multiple);
  }

//+------------------------------------------------------------------+
//|Returns Weeks in seconds                                          |
//+------------------------------------------------------------------+
int CTimeManagement::WeeksS(int multiple=1)
  {
   return (DaysS(7)*multiple);
  }

//+------------------------------------------------------------------+
//|Returns Months in seconds                                         |
//+------------------------------------------------------------------+
int CTimeManagement::MonthsS(int multiple=1)
  {
   return (WeeksS(4)*multiple);
  }

//+------------------------------------------------------------------+
//|Returns Years in seconds                                          |
//+------------------------------------------------------------------+
int CTimeManagement::YearsS(int multiple=1)
  {
   return (MonthsS(12)*multiple);
  }

//+------------------------------------------------------------------+
//|Returns the Year for a specific date                              |
//+------------------------------------------------------------------+
int CTimeManagement::ReturnYear(datetime time)
  {
   TimeToStruct(time,timeFormat);
   return timeFormat.year;
  }

//+------------------------------------------------------------------+
//|Returns the Month for a specific date                             |
//+------------------------------------------------------------------+
int CTimeManagement::ReturnMonth(datetime time)
  {
   TimeToStruct(time,timeFormat);
   return timeFormat.mon;
  }

//+------------------------------------------------------------------+
//|Returns the Day for a specific date                               |
//+------------------------------------------------------------------+
int CTimeManagement::ReturnDay(datetime time)
  {
   TimeToStruct(time,timeFormat);
   return timeFormat.day;
  }

//+------------------------------------------------------------------+
//|Will return a datetime type of a date with an subtraction offset  |
//|in seconds                                                        |
//+------------------------------------------------------------------+
datetime CTimeManagement::TimeMinusOffset(datetime standardtime,int timeoffset)
  {
   standardtime-=timeoffset;
   return standardtime;
  }

//+------------------------------------------------------------------+
//|Will return a datetime type of a date with an addition offset     | 
//|in seconds                                                        |
//+------------------------------------------------------------------+
datetime CTimeManagement::TimePlusOffset(datetime standardtime,int timeoffset)
  {
   standardtime+=timeoffset;
   return standardtime;
  }
//+------------------------------------------------------------------+


チャートプロパティクラス

チャートプロパティの目的は、チャートのレイアウトを変更する前にチャートの構成を保存することです。エキスパートアドバイザー(EA)がチャートから削除されると、クラスのデストラクタは、チャートに加えた変更前のチャート構成の状態を復元します。

EA機能において、チャートに変更を加えることは必須ではありませんが、デフォルトのチャートレイアウト(緑と黒以外の配色を含む)で視覚的に美しいチャートが提供されると、取引中に価格や取引レベルが見えにくくなる場合があります。

CChartPropertiesクラスには、クラスCSymbolPropertiesからの単一継承があります。

CChartPropertiesクラスには、次のクラスからの階層的継承があります。

  • CSymbolProperties
  • CSymbolInfo
//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+
#include "SymbolProperties.mqh"
//+------------------------------------------------------------------+
//|ChartProperties class                                             |
//+------------------------------------------------------------------+
class CChartProperties : public CSymbolProperties
  {
private:
   struct ChartFormat
     {
      ulong             CHART_MODE;//Chart Candle Mode
      ulong             CHART_COLOR_BACKGROUND;//Chart Background Color
      ulong             CHART_COLOR_FOREGROUND;//Chart Foreground Color
      ulong             CHART_COLOR_CHART_LINE;//Chart Line Color
      ulong             CHART_COLOR_CANDLE_BEAR;//Chart Bear Candle Color
      ulong             CHART_COLOR_CHART_DOWN;//Chart Down Candle Color
      ulong             CHART_COLOR_CANDLE_BULL;//Chart Bull Candle Color
      ulong             CHART_COLOR_CHART_UP;//Chart Up Candle Color
      ulong             CHART_COLOR_ASK;//Chart Ask Color
      ulong             CHART_COLOR_BID;//Chart Bid Color
      ulong             CHART_COLOR_STOP_LEVEL;//Chart Stoplevel Color
      ulong             CHART_SHOW_PERIOD_SEP;//Chart Show Period Separator
      ulong             CHART_SCALE;//Chart Scale
      ulong             CHART_FOREGROUND;//Chart Show Foreground
      ulong             CHART_SHOW_ASK_LINE;//Chart Show Ask Line
      ulong             CHART_SHOW_BID_LINE;//Chart Show Bid Line
      ulong             CHART_SHOW_TRADE_LEVELS;//Chart Show Trade Levels
      ulong             CHART_SHOW_OHLC;//Chart Show Open-High-Low-Close
      ulong             CHART_SHOW_GRID;//Chart Show Grid
      ulong             CHART_SHOW_VOLUMES;//Chart Show Volumes
      ulong             CHART_AUTOSCROLL;//Chart Auto Scroll
      double            CHART_SHIFT_SIZE;//Chart Shift Size
      ulong             CHART_SHIFT;//Chart Shift
      ulong             CHART_SHOW_ONE_CLICK;//Chart One Click Trading
     };
   ulong             ChartConfig[65];//Array To Store Chart Properties
   void              ChartSet();//Apply Chart format
   void              ChartConfigure();//Set Chart Values
   ChartFormat       Chart;//Variable of type ChartFormat

public:
                     CChartProperties(void);//Constructor
                    ~CChartProperties(void);//Destructor
   void              ChartRefresh() {ChartConfigure();}
  };

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CChartProperties::CChartProperties(void)//Class Constructor
  {
   for(int i=0;i<65;i++)//Iterating through ENUM_CHART_PROPERTY_INTEGER Elements
     {
      ChartGetInteger(0,(ENUM_CHART_PROPERTY_INTEGER)i,0,ChartConfig[i]);//Storing Chart values into ChartConfig array
     }
   ChartConfigure();
  }

//+------------------------------------------------------------------+
//|Destructor                                                        |
//+------------------------------------------------------------------+
CChartProperties::~CChartProperties(void)
  {
   for(int i=0;i<65;i++)//Iterating through ENUM_CHART_PROPERTY_INTEGER Elements
     {
      ChartSetInteger(0,(ENUM_CHART_PROPERTY_INTEGER)i,0,ChartConfig[i]);//Restoring Chart values from ChartConfig array
     }
  }

//+------------------------------------------------------------------+
//|Set Chart Properties                                              |
//+------------------------------------------------------------------+
void CChartProperties::ChartSet()
  {
   ChartSetInteger(0,CHART_MODE,Chart.CHART_MODE);//Set Chart Candle Mode
   ChartSetInteger(0,CHART_COLOR_BACKGROUND,Chart.CHART_COLOR_BACKGROUND);//Set Chart Background Color
   ChartSetInteger(0,CHART_COLOR_FOREGROUND,Chart.CHART_COLOR_FOREGROUND);//Set Chart Foreground Color
   ChartSetInteger(0,CHART_COLOR_CHART_LINE,Chart.CHART_COLOR_CHART_LINE);//Set Chart Line Color
   ChartSetInteger(0,CHART_COLOR_CANDLE_BEAR,Chart.CHART_COLOR_CANDLE_BEAR);//Set Chart Bear Candle Color
   ChartSetInteger(0,CHART_COLOR_CHART_DOWN,Chart.CHART_COLOR_CHART_DOWN);//Set Chart Down Candle Color
   ChartSetInteger(0,CHART_COLOR_CANDLE_BULL,Chart.CHART_COLOR_CANDLE_BULL);//Set Chart Bull Candle Color
   ChartSetInteger(0,CHART_COLOR_CHART_UP,Chart.CHART_COLOR_CHART_UP);//Set Chart Up Candle Color
   ChartSetInteger(0,CHART_COLOR_ASK,Chart.CHART_COLOR_ASK);//Set Chart Ask Color
   ChartSetInteger(0,CHART_COLOR_BID,Chart.CHART_COLOR_BID);//Set Chart Bid Color
   ChartSetInteger(0,CHART_COLOR_STOP_LEVEL,Chart.CHART_COLOR_STOP_LEVEL);//Set Chart Stop Level Color
   ChartSetInteger(0,CHART_FOREGROUND,Chart.CHART_FOREGROUND);//Set if Chart is in Foreground Visibility
   ChartSetInteger(0,CHART_SHOW_ASK_LINE,Chart.CHART_SHOW_ASK_LINE);//Set Chart Ask Line Visibility
   ChartSetInteger(0,CHART_SHOW_BID_LINE,Chart.CHART_SHOW_BID_LINE);//Set Chart Bid Line Visibility
   ChartSetInteger(0,CHART_SHOW_PERIOD_SEP,Chart.CHART_SHOW_PERIOD_SEP);//Set Chart Period Separator Visibility
   ChartSetInteger(0,CHART_SHOW_TRADE_LEVELS,Chart.CHART_SHOW_TRADE_LEVELS);//Set Chart Trade Levels Visibility
   ChartSetInteger(0,CHART_SHOW_OHLC,Chart.CHART_SHOW_OHLC);//Set Chart Open-High-Low-Close Visibility
   ChartSetInteger(0,CHART_SHOW_GRID,Chart.CHART_SHOW_GRID);//Set Chart Grid Visibility
   ChartSetInteger(0,CHART_SHOW_VOLUMES,Chart.CHART_SHOW_VOLUMES);//Set Chart Volumes Visibility
   ChartSetInteger(0,CHART_SCALE,Chart.CHART_SCALE);//Set Chart Scale Value
   ChartSetInteger(0,CHART_AUTOSCROLL,Chart.CHART_AUTOSCROLL);//Set Chart Auto Scroll Option
   ChartSetDouble(0,CHART_SHIFT_SIZE,Chart.CHART_SHIFT_SIZE);//Set Chart Shift Size Value
   ChartSetInteger(0,CHART_SHIFT,Chart.CHART_SHIFT);//Set Chart Shift Option
   ChartSetInteger(0,CHART_SHOW_ONE_CLICK,Chart.CHART_SHOW_ONE_CLICK);//Set Chart One Click Trading
  }

//+------------------------------------------------------------------+
//|Initialize Chart Properties                                       |
//+------------------------------------------------------------------+
void CChartProperties::ChartConfigure(void)
  {
   Chart.CHART_MODE=(ulong)CHART_CANDLES;//Assigning Chart Mode of CHART_CANDLES
   Chart.CHART_COLOR_BACKGROUND=ulong(SymbolBackground());//Assigning Chart Background Color of Symbol's Background color
   Chart.CHART_COLOR_FOREGROUND=(ulong)clrBlack;//Assigning Chart Foreground Color of clrBalck(Black color)
   Chart.CHART_COLOR_CHART_LINE=(ulong)clrBlack;//Assigning Chart Line Color of clrBlack(Black color)
   Chart.CHART_COLOR_CANDLE_BEAR=(ulong)clrBlack;//Assigning Chart Bear Candle Color of clrBlack(Black color)
   Chart.CHART_COLOR_CHART_DOWN=(ulong)clrBlack;//Assigning Chart Down Candle Color of clrBlack(Black color)
   Chart.CHART_COLOR_CANDLE_BULL=(ulong)clrWhite;//Assigning Chart Bull Candle Color of clrWhite(White color)
   Chart.CHART_COLOR_CHART_UP=(ulong)clrBlack;//Assigning Chart Up Candle Color of clrBlack(Black color)
   Chart.CHART_COLOR_ASK=(ulong)clrBlack;//Assigning Chart Ask Color of clrBlack(Black color)
   Chart.CHART_COLOR_BID=(ulong)clrBlack;//Assigning Chart Bid Color of clrBlack(Black color)
   Chart.CHART_COLOR_STOP_LEVEL=(ulong)clrBlack;//Assigning Chart Stop Level Color of clrBlack(Black color)
   Chart.CHART_FOREGROUND=(ulong)false;//Assigning Chart Foreground Boolean Value of 'false'
   Chart.CHART_SHOW_ASK_LINE=(ulong)true;//Assigning Chart Ask Line Boolean Value of 'true'
   Chart.CHART_SHOW_BID_LINE=(ulong)true;//Assigning Chart Bid Line Boolean Value of 'true'
   Chart.CHART_SHOW_PERIOD_SEP=(ulong)true;//Assigning Chart Period Separator Boolean Value of 'true'
   Chart.CHART_SHOW_TRADE_LEVELS=(ulong)true;//Assigning Chart Trade Levels Boolean Value of 'true'
   Chart.CHART_SHOW_OHLC=(ulong)false;//Assigning Chart Open-High-Low-Close Boolean Value of 'false'
   Chart.CHART_SHOW_GRID=(ulong)false;//Assigning Chart Grid Boolean Value of 'false'
   Chart.CHART_SHOW_VOLUMES=(ulong)false;//Assigning Chart Volumes Boolean Value of 'false'
   Chart.CHART_SCALE=(ulong)3;//Assigning Chart Scale Boolean Value of '3'
   Chart.CHART_AUTOSCROLL=(ulong)true;//Assigning Chart Auto Scroll Boolean Value of 'true'
   Chart.CHART_SHIFT_SIZE=30;//Assigning Chart Shift Size Value of '30'
   Chart.CHART_SHIFT=(ulong)true;//Assigning Chart Shift Boolean Value of 'true'
   Chart.CHART_SHOW_ONE_CLICK=ulong(false);//Assigning Chart One Click Trading a value of 'false'
   ChartSet();//Calling Function to set chart format
  }
//+------------------------------------------------------------------+

宣言した構造体ChartFormatには、EAが現在表示されているチャートで変更するさまざまなチャート変数が格納されます。

   struct ChartFormat
     {
      ulong             CHART_MODE;//Chart Candle Mode
      ulong             CHART_COLOR_BACKGROUND;//Chart Background Color
      ulong             CHART_COLOR_FOREGROUND;//Chart Foreground Color
      ulong             CHART_COLOR_CHART_LINE;//Chart Line Color
      ulong             CHART_COLOR_CANDLE_BEAR;//Chart Bear Candle Color
      ulong             CHART_COLOR_CHART_DOWN;//Chart Down Candle Color
      ulong             CHART_COLOR_CANDLE_BULL;//Chart Bull Candle Color
      ulong             CHART_COLOR_CHART_UP;//Chart Up Candle Color
      ulong             CHART_COLOR_ASK;//Chart Ask Color
      ulong             CHART_COLOR_BID;//Chart Bid Color
      ulong             CHART_COLOR_STOP_LEVEL;//Chart Stoplevel Color
      ulong             CHART_SHOW_PERIOD_SEP;//Chart Show Period Separator
      ulong             CHART_SCALE;//Chart Scale
      ulong             CHART_FOREGROUND;//Chart Show Foreground
      ulong             CHART_SHOW_ASK_LINE;//Chart Show Ask Line
      ulong             CHART_SHOW_BID_LINE;//Chart Show Bid Line
      ulong             CHART_SHOW_TRADE_LEVELS;//Chart Show Trade Levels
      ulong             CHART_SHOW_OHLC;//Chart Show Open-High-Low-Close
      ulong             CHART_SHOW_GRID;//Chart Show Grid
      ulong             CHART_SHOW_VOLUMES;//Chart Show Volumes
      ulong             CHART_AUTOSCROLL;//Chart Auto Scroll
      double            CHART_SHIFT_SIZE;//Chart Shift Size
      ulong             CHART_SHIFT;//Chart Shift
      ulong             CHART_SHOW_ONE_CLICK;//Chart One Click Trading
     };

ChartConfig配列には、チャートに変更を加える前にすべてのチャートプロパティが保存されます。

ulong             ChartConfig[65];//Array To Store Chart Properties

SetBackground関数では、現在の銘柄の気配値表示の背景色を取得します。

気配値表示

現在のグラフの背景色を次の色に設定します。

チャートの背景色

クラスコンストラクタでは、Integer型のすべてのチャートプロパティを取得し、ChartConfig配列に格納します。 

CChartProperties::CChartProperties(void)//Class Constructor
  {
   for(int i=0;i<65;i++)//Iterating through ENUM_CHART_PROPERTY_INTEGER Elements
     {
      ChartGetInteger(0,(ENUM_CHART_PROPERTY_INTEGER)i,0,ChartConfig[i]);//Storing Chart values into ChartConfig array
     }
   ChartConfigure();
  }

また、前述のChartFormat構造体型の変数Chartを初期化し、ChartConfigure関数で適切な値を指定して、チャートを好みに合わせてカスタマイズします。

void CChartProperties::ChartConfigure(void)
  {
   Chart.CHART_MODE=(ulong)CHART_CANDLES;//Assigning Chart Mode of CHART_CANDLES
   Chart.CHART_COLOR_BACKGROUND=ulong(SymbolBackground());//Assigning Chart Background Color of Symbol's Background color
   Chart.CHART_COLOR_FOREGROUND=(ulong)clrBlack;//Assigning Chart Foreground Color of clrBalck(Black color)
   Chart.CHART_COLOR_CHART_LINE=(ulong)clrBlack;//Assigning Chart Line Color of clrBlack(Black color)
   Chart.CHART_COLOR_CANDLE_BEAR=(ulong)clrBlack;//Assigning Chart Bear Candle Color of clrBlack(Black color)
   Chart.CHART_COLOR_CHART_DOWN=(ulong)clrBlack;//Assigning Chart Down Candle Color of clrBlack(Black color)
   Chart.CHART_COLOR_CANDLE_BULL=(ulong)clrWhite;//Assigning Chart Bull Candle Color of clrWhite(White color)
   Chart.CHART_COLOR_CHART_UP=(ulong)clrBlack;//Assigning Chart Up Candle Color of clrBlack(Black color)
   Chart.CHART_COLOR_ASK=(ulong)clrBlack;//Assigning Chart Ask Color of clrBlack(Black color)
   Chart.CHART_COLOR_BID=(ulong)clrBlack;//Assigning Chart Bid Color of clrBlack(Black color)
   Chart.CHART_COLOR_STOP_LEVEL=(ulong)clrBlack;//Assigning Chart Stop Level Color of clrBlack(Black color)
   Chart.CHART_FOREGROUND=(ulong)false;//Assigning Chart Foreground Boolean Value of 'false'
   Chart.CHART_SHOW_ASK_LINE=(ulong)true;//Assigning Chart Ask Line Boolean Value of 'true'
   Chart.CHART_SHOW_BID_LINE=(ulong)true;//Assigning Chart Bid Line Boolean Value of 'true'
   Chart.CHART_SHOW_PERIOD_SEP=(ulong)true;//Assigning Chart Period Separator Boolean Value of 'true'
   Chart.CHART_SHOW_TRADE_LEVELS=(ulong)true;//Assigning Chart Trade Levels Boolean Value of 'true'
   Chart.CHART_SHOW_OHLC=(ulong)false;//Assigning Chart Open-High-Low-Close Boolean Value of 'false'
   Chart.CHART_SHOW_GRID=(ulong)false;//Assigning Chart Grid Boolean Value of 'false'
   Chart.CHART_SHOW_VOLUMES=(ulong)false;//Assigning Chart Volumes Boolean Value of 'false'
   Chart.CHART_SCALE=(ulong)3;//Assigning Chart Scale Boolean Value of '3'
   Chart.CHART_AUTOSCROLL=(ulong)true;//Assigning Chart Auto Scroll Boolean Value of 'true'
   Chart.CHART_SHIFT_SIZE=30;//Assigning Chart Shift Size Value of '30'
   Chart.CHART_SHIFT=(ulong)true;//Assigning Chart Shift Boolean Value of 'true'
   Chart.CHART_SHOW_ONE_CLICK=ulong(false);//Assigning Chart One Click Trading a value of 'false'
   ChartSet();//Calling Function to set chart format
  }

ChartSet関数では、ChartFormat構造体型の変数Chartから選択されたチャートプロパティの値を設定します。

void CChartProperties::ChartSet()
  {
   ChartSetInteger(0,CHART_MODE,Chart.CHART_MODE);//Set Chart Candle Mode
   ChartSetInteger(0,CHART_COLOR_BACKGROUND,Chart.CHART_COLOR_BACKGROUND);//Set Chart Background Color
   ChartSetInteger(0,CHART_COLOR_FOREGROUND,Chart.CHART_COLOR_FOREGROUND);//Set Chart Foreground Color
   ChartSetInteger(0,CHART_COLOR_CHART_LINE,Chart.CHART_COLOR_CHART_LINE);//Set Chart Line Color
   ChartSetInteger(0,CHART_COLOR_CANDLE_BEAR,Chart.CHART_COLOR_CANDLE_BEAR);//Set Chart Bear Candle Color
   ChartSetInteger(0,CHART_COLOR_CHART_DOWN,Chart.CHART_COLOR_CHART_DOWN);//Set Chart Down Candle Color
   ChartSetInteger(0,CHART_COLOR_CANDLE_BULL,Chart.CHART_COLOR_CANDLE_BULL);//Set Chart Bull Candle Color
   ChartSetInteger(0,CHART_COLOR_CHART_UP,Chart.CHART_COLOR_CHART_UP);//Set Chart Up Candle Color
   ChartSetInteger(0,CHART_COLOR_ASK,Chart.CHART_COLOR_ASK);//Set Chart Ask Color
   ChartSetInteger(0,CHART_COLOR_BID,Chart.CHART_COLOR_BID);//Set Chart Bid Color
   ChartSetInteger(0,CHART_COLOR_STOP_LEVEL,Chart.CHART_COLOR_STOP_LEVEL);//Set Chart Stop Level Color
   ChartSetInteger(0,CHART_FOREGROUND,Chart.CHART_FOREGROUND);//Set if Chart is in Foreground Visibility
   ChartSetInteger(0,CHART_SHOW_ASK_LINE,Chart.CHART_SHOW_ASK_LINE);//Set Chart Ask Line Visibility
   ChartSetInteger(0,CHART_SHOW_BID_LINE,Chart.CHART_SHOW_BID_LINE);//Set Chart Bid Line Visibility
   ChartSetInteger(0,CHART_SHOW_PERIOD_SEP,Chart.CHART_SHOW_PERIOD_SEP);//Set Chart Period Separator Visibility
   ChartSetInteger(0,CHART_SHOW_TRADE_LEVELS,Chart.CHART_SHOW_TRADE_LEVELS);//Set Chart Trade Levels Visibility
   ChartSetInteger(0,CHART_SHOW_OHLC,Chart.CHART_SHOW_OHLC);//Set Chart Open-High-Low-Close Visibility
   ChartSetInteger(0,CHART_SHOW_GRID,Chart.CHART_SHOW_GRID);//Set Chart Grid Visibility
   ChartSetInteger(0,CHART_SHOW_VOLUMES,Chart.CHART_SHOW_VOLUMES);//Set Chart Volumes Visibility
   ChartSetInteger(0,CHART_SCALE,Chart.CHART_SCALE);//Set Chart Scale Value
   ChartSetInteger(0,CHART_AUTOSCROLL,Chart.CHART_AUTOSCROLL);//Set Chart Auto Scroll Option
   ChartSetDouble(0,CHART_SHIFT_SIZE,Chart.CHART_SHIFT_SIZE);//Set Chart Shift Size Value
   ChartSetInteger(0,CHART_SHIFT,Chart.CHART_SHIFT);//Set Chart Shift Option
   ChartSetInteger(0,CHART_SHOW_ONE_CLICK,Chart.CHART_SHOW_ONE_CLICK);//Set Chart One Click Trading
  }

デストラクタでは、前のチャートの整数値を復元します。

CChartProperties::~CChartProperties(void)
  {
   for(int i=0;i<65;i++)//Iterating through ENUM_CHART_PROPERTY_INTEGER Elements
     {
      ChartSetInteger(0,(ENUM_CHART_PROPERTY_INTEGER)i,0,ChartConfig[i]);//Restoring Chart values from ChartConfig array
     }
  }


ローソク足プロパティクラス

CCandlePropertiesクラスには、次のクラスからのマルチレベル継承があります。

  • CChartProperties
  • CSymbolProperties

CCandlePropertiesクラスには、CTimeManagementからのインクルードがあります。

CCandlePropertiesクラスには、次のクラスからの階層的継承があります。

  • CSymbolProperties
  • CSymbolInfo

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+
#include "TimeManagement.mqh"
#include "ChartProperties.mqh"
//+------------------------------------------------------------------+
//|CandleProperties class                                            |
//+------------------------------------------------------------------+
class CCandleProperties : public CChartProperties
  {
private:
   CTimeManagement   Time;

public:
   double            Open(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle Open-Price
   double            Close(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle Close-Price
   double            High(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle High-Price
   double            Low(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle Low-Price
   bool              IsLargerThanPreviousAndNext(datetime CandleTime,int Offset,string SYMBOL);//Determine if one candle is larger than two others
  };

//+------------------------------------------------------------------+
//|Retrieve Candle Open-Price                                        |
//+------------------------------------------------------------------+
double CCandleProperties::Open(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL)
  {
   return (SetSymbolName(SYMBOL))?iOpen(GetSymbolName(),Period,CandleIndex):0;//return candle open price
  }

//+------------------------------------------------------------------+
//|Retrieve Candle Close-Price                                       |
//+------------------------------------------------------------------+
double CCandleProperties::Close(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL)
  {
   return (SetSymbolName(SYMBOL))?iClose(GetSymbolName(),Period,CandleIndex):0;//return candle close price
  }

//+------------------------------------------------------------------+
//|Retrieve Candle High-Price                                        |
//+------------------------------------------------------------------+
double CCandleProperties::High(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL)
  {
   return (SetSymbolName(SYMBOL))?iHigh(GetSymbolName(),Period,CandleIndex):0;//return candle high price
  }

//+------------------------------------------------------------------+
//|Retrieve Candle Low-Price                                         |
//+------------------------------------------------------------------+
double CCandleProperties::Low(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL)
  {
   return (SetSymbolName(SYMBOL))?iLow(GetSymbolName(),Period,CandleIndex):0;//return candle low price
  }

//+------------------------------------------------------------------+
//|Determine if one candle is larger than two others                |
//+------------------------------------------------------------------+
bool CCandleProperties::IsLargerThanPreviousAndNext(datetime CandleTime,int Offset,string SYMBOL)
  {
   int CandleIndex = iBarShift(SYMBOL,PERIOD_M15,CandleTime);//Assign candle index of candletime
//--Assign candle index of candletime minus time offset
   int CandleIndexMinusOffset = iBarShift(SYMBOL,PERIOD_M15,Time.TimeMinusOffset(CandleTime,Offset));
//--Assign candle index of candletime plus time offset
   int CandleIndexPlusOffset = iBarShift(SYMBOL,PERIOD_M15,Time.TimePlusOffset(CandleTime,Offset));
//--Assign height of M15 candletime in pips
   double CandleHeight = High(CandleIndex,PERIOD_M15,SYMBOL)-Low(CandleIndex,PERIOD_M15,SYMBOL);
//--Assign height of M15 candletime  minus offset in Pips
   double CandleHeightMinusOffset = High(CandleIndexMinusOffset,PERIOD_M15,SYMBOL)-Low(CandleIndexMinusOffset,PERIOD_M15,SYMBOL);
//--Assign height of M15 candletime plus offset in Pips
   double CandleHeightPlusOffset = High(CandleIndexPlusOffset,PERIOD_M15,SYMBOL)-Low(CandleIndexPlusOffset,PERIOD_M15,SYMBOL);
//--Determine if candletime height is greater than candletime height minus offset and candletime height plus offset
   if(CandleHeight>CandleHeightMinusOffset&&CandleHeight>CandleHeightPlusOffset)
     {
      return true;//Candletime is likely when the news event occured
     }
   return false;//Candletime is unlikely when the real news data was released
  }
//+------------------------------------------------------------------+


オブジェクトプロパティクラス

このクラスは、チャートオブジェクトの作成と削除を担当します。

CObjectPropertiesクラスには、次のクラスからのマルチレベル継承があります。

  • CChartProperties
  • CSymbolProperties
CObjectPropertiesクラスには、次のクラスからの階層的継承があります。

  • CSymbolProperties
  • CSymbolInfo
//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+
#include "ChartProperties.mqh"
//+------------------------------------------------------------------+
//|ObjectProperties class                                            |
//+------------------------------------------------------------------+
class CObjectProperties:public CChartProperties
  {
private:
   //Simple  chart objects structure
   struct ObjStruct
     {
      long           ChartId;
      string         Name;
     } Objects[];//ObjStruct variable array

   //-- Add chart object to Objects array
   void              AddObj(long chart_id,string name)
     {
      ArrayResize(Objects,Objects.Size()+1,Objects.Size()+2);
      Objects[Objects.Size()-1].ChartId=chart_id;
      Objects[Objects.Size()-1].Name=name;
     }

public:
                     CObjectProperties(void) {}//Class constructor

   //-- Create Rectangle chart object
   void              Square(long chart_ID,string name,int x_coord,int y_coord,int width,int height,ENUM_ANCHOR_POINT Anchor);

   //-- Create text chart object
   void              TextObj(long chartID,string name,string text,int x_coord,int y_coord,
                             ENUM_BASE_CORNER Corner=CORNER_LEFT_UPPER,int fontsize=10);

   //-- Create Event object
   void               EventObj(long chartID,string name,string description,datetime eventdate);

   //-- Class destructor removes all chart objects created previously
                    ~CObjectProperties(void)
     {
      for(uint i=0;i<Objects.Size();i++)
        {
         ObjectDelete(Objects[i].ChartId,Objects[i].Name);
        }
     }
  };

//+------------------------------------------------------------------+
//|Create Rectangle chart object                                     |
//+------------------------------------------------------------------+
void CObjectProperties::Square(long chart_ID,string name,int x_coord,int y_coord,int width,int height,ENUM_ANCHOR_POINT Anchor)
  {
   const int              sub_window=0;             // subwindow index
   const int              x=x_coord;                // X coordinate
   const int              y=y_coord;                // Y coordinate
   const color            back_clr=clrBlack;        // background color
   const ENUM_BORDER_TYPE border=BORDER_SUNKEN;     // border type
   const color            clr=clrRed;               // flat border color (Flat)
   const ENUM_LINE_STYLE  style=STYLE_SOLID;        // flat border style
   const int              line_width=0;             // flat border width
   const bool             back=false;               // in the background
   const bool             selection=false;          // highlight to move
   const bool             hidden=true;              // hidden in the object list

   ObjectDelete(chart_ID,name);//Delete previous object with the same name and chart id
   if(ObjectCreate(chart_ID,name,OBJ_RECTANGLE_LABEL,sub_window,0,0))//create rectangle object label
     {
      AddObj(chart_ID,name);//Add object to array
      ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x);//Set x Distance/coordinate
      ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y);//Set y Distance/coordinate
      ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width);//Set object's width/x-size
      ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height);//Set object's height/y-size
      ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr);//Set object's background color
      ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_TYPE,border);//Set object's border type
      ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,Anchor);//Set objects anchor point
      ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);//Set object's color
      ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style);//Set object's style
      ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,line_width);//Set object's flat border width
      ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);//Set if object is in foreground or not
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);//Set if object is selectable/dragable
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);//Set if object is Selected
      ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden);//Set if object is hidden in object list
      ChartRedraw(chart_ID);
     }
   else
     {
      Print("Failed to create object: ",name);
     }
  }

//+------------------------------------------------------------------+
//|Create text chart object                                          |
//+------------------------------------------------------------------+
void CObjectProperties::TextObj(long chartID,string name,string text,int x_coord,int y_coord,
                                ENUM_BASE_CORNER Corner=CORNER_LEFT_UPPER,int fontsize=10)
  {
   ObjectDelete(chartID,name);//Delete previous object with the same name and chart id
   if(ObjectCreate(chartID,name,OBJ_LABEL,0,0,0))//Create object label
     {
      AddObj(chartID,name);//Add object to array
      ObjectSetInteger(chartID,name,OBJPROP_XDISTANCE,x_coord);//Set x Distance/coordinate
      ObjectSetInteger(chartID,name,OBJPROP_YDISTANCE,y_coord);//Set y Distance/coordinate
      ObjectSetInteger(chartID,name,OBJPROP_CORNER,Corner);//Set object's corner anchor
      ObjectSetString(chartID,name,OBJPROP_TEXT,text);//Set object's text
      ObjectSetInteger(chartID,name,OBJPROP_COLOR,SymbolBackground());//Set object's color
      ObjectSetInteger(chartID,name,OBJPROP_FONTSIZE,fontsize);//Set object's font-size
     }
   else
     {
      Print("Failed to create object: ",name);
     }
  }

//+------------------------------------------------------------------+
//|Create Event object                                               |
//+------------------------------------------------------------------+
void CObjectProperties::EventObj(long chartID,string name,string description,datetime eventdate)
  {
   ObjectDelete(chartID,name);//Delete previous object with the same name and chart id
   if(ObjectCreate(chartID,name,OBJ_EVENT,0,eventdate,0))//Create object event
     {
      AddObj(chartID,name);//Add object to array
      ObjectSetString(chartID,name,OBJPROP_TEXT,description);//Set object's text
      ObjectSetInteger(chartID,name,OBJPROP_COLOR,clrBlack);//Set object's color
     }
   else
     {
      Print("Failed to create object: ",name);
     }
  }
//+------------------------------------------------------------------

配列変数Objectsには、このクラスCObjectPropertiesで作成されたすべてのチャートオブジェクトが格納されます。

struct ObjStruct
     {
      long           ChartId;
      string         Name;
     } Objects[];//ObjStruct variable array

AddObj関数では、チャートオブジェクトのチャートIDとオブジェクト名をObjects配列に追加します。

   //-- Add chart object to Objects array
   void              AddObj(long chart_id,string name)
     {
      ArrayResize(Objects,Objects.Size()+1,Objects.Size()+2);
      Objects[Objects.Size()-1].ChartId=chart_id;
      Objects[Objects.Size()-1].Name=name;
     }

Square関数の目的は、カスタマイズを可能にするために特定のプロパティを持つRectangleオブジェクトを作成することです。 

void CObjectProperties::Square(long chart_ID,string name,int x_coord,int y_coord,int width,int height,ENUM_ANCHOR_POINT Anchor)
  {
   const int              sub_window=0;             // subwindow index
   const int              x=x_coord;                // X coordinate
   const int              y=y_coord;                // Y coordinate
   const color            back_clr=clrBlack;        // background color
   const ENUM_BORDER_TYPE border=BORDER_SUNKEN;     // border type
   const color            clr=clrRed;               // flat border color (Flat)
   const ENUM_LINE_STYLE  style=STYLE_SOLID;        // flat border style
   const int              line_width=0;             // flat border width
   const bool             back=false;               // in the background
   const bool             selection=false;          // highlight to move
   const bool             hidden=true;              // hidden in the object list

   ObjectDelete(chart_ID,name);//Delete previous object with the same name and chart id
   if(ObjectCreate(chart_ID,name,OBJ_RECTANGLE_LABEL,sub_window,0,0))//create rectangle object label
     {
      AddObj(chart_ID,name);//Add object to array
      ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x);//Set x Distance/coordinate
      ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y);//Set y Distance/coordinate
      ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width);//Set object's width/x-size
      ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height);//Set object's height/y-size
      ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr);//Set object's background color
      ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_TYPE,border);//Set object's border type
      ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,Anchor);//Set objects anchor point
      ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);//Set object's color
      ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style);//Set object's style
      ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,line_width);//Set object's flat border width
      ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);//Set if object is in foreground or not
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);//Set if object is selectable/dragable
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);//Set if object is Selected
      ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden);//Set if object is hidden in object list
      ChartRedraw(chart_ID);
     }
   else
     {
      Print("Failed to create object: ",name);
     }
  }

TextObj関数では、チャート上にテキストオブジェクトを作成します。 

void CObjectProperties::TextObj(long chartID,string name,string text,int x_coord,int y_coord,
                                ENUM_BASE_CORNER Corner=CORNER_LEFT_UPPER,int fontsize=10)
  {
   ObjectDelete(chartID,name);//Delete previous object with the same name and chart id
   if(ObjectCreate(chartID,name,OBJ_LABEL,0,0,0))//Create object label
     {
      AddObj(chartID,name);//Add object to array
      ObjectSetInteger(chartID,name,OBJPROP_XDISTANCE,x_coord);//Set x Distance/coordinate
      ObjectSetInteger(chartID,name,OBJPROP_YDISTANCE,y_coord);//Set y Distance/coordinate
      ObjectSetInteger(chartID,name,OBJPROP_CORNER,Corner);//Set object's corner anchor
      ObjectSetString(chartID,name,OBJPROP_TEXT,text);//Set object's text
      ObjectSetInteger(chartID,name,OBJPROP_COLOR,SymbolBackground());//Set object's color
      ObjectSetInteger(chartID,name,OBJPROP_FONTSIZE,fontsize);//Set object's font-size
     }
   else
     {
      Print("Failed to create object: ",name);
     }
  }

EventObj関数では、チャート上にイベントオブジェクトを作成し、発生している、または発生する予定の経済イベントを表示します。

void CObjectProperties::EventObj(long chartID,string name,string description,datetime eventdate)
  {
   ObjectDelete(chartID,name);//Delete previous object with the same name and chart id
   if(ObjectCreate(chartID,name,OBJ_EVENT,0,eventdate,0))//Create object event
     {
      AddObj(chartID,name);//Add object to array
      ObjectSetString(chartID,name,OBJPROP_TEXT,description);//Set object's text
      ObjectSetInteger(chartID,name,OBJPROP_COLOR,clrBlack);//Set object's color
     }
   else
     {
      Print("Failed to create object: ",name);
     }
  }


ニュースクラス

以下は、カレンダーテーブル(第1回版)です。

第1回ののデータベーステーブル

第1回のカレンダーデータベース

第1回の以前のデータベースでは、ファイルサイズが非常に大きく、すべてのニュースデータをデータベースに保存するのにかなり時間がかかりました。これはニュースデータが非効率的に保存されているためです。ファイルサイズとパフォーマンス低下の最大の原因は、類似のデータを繰り返し保存することです。

以下が表です。

  • Data_AU
  • Data_None
  • Data_UK
  • Data_US

同じニュースデータを時間データに変化を付けて保存します。

以下が新しいデザインです。

以下が、第2回版のカレンダーのコンテンツです。

データベースのコンテンツ

カレンダーデータベース第2回

同じニュースイベントデータを異なる時間で繰り返し保存する代わりに、 すべてのニュースイベントデータを格納するためのMQL5Calendarという単一のテーブルを作成します。また、各DSTスケジュール間の異なる時間データを格納するために、TimeScheduleという別のテーブルを用意します。したがって、このレイアウトにより、ファイルサイズが以前のカレンダーデータベースの半分以下に削減され、パフォーマンスが向上します。


新しいデータベース設計では、コンテンツは次のようになります。

  • AutoDST Table
  • Calendar_AU View
  • Calendar_NONE View
  • Calendar_UK View
  • Calendar_US View
  • MQL5Calendar Table
  • Record Table
  • TimeSchedule Table
  • OnlyOne_AutoDST Trigger
  • OnlyOne_Record Trigger

以前のデータベースのテーブルを正規化します。これらのテーブルは、Data_AU、Data_None、Data_UK、Data_USです。

データベースの正規化

データベースの正規化は、冗長性と依存性を最小限に抑えるためにデータベースをテーブルと列に編成するために使用されるデータベース設計のプロセスです。主な目標は、冗長なデータ(たとえば、複数のテーブルに同じデータを格納する)を排除し、データの依存関係が意味をなすようにすること(関連するデータのみをテーブルに格納する)です。このプロセスにより、テーブルのセットの保守が容易になり、データの異常が発生する可能性が減ります。

UM UM

また、AutoDSTテーブルとRecordテーブルに1つのレコードのみが保存されるようにするトリガーも作成します。さらに、各DSTスケジュールのビューを作成して、最終更新日のニュースイベントを表示します。これにより、何千ものエントリがあるテーブルでクエリを繰り返し実行することなく、どのニュースイベントが最新であるかを簡単に確認できるようになります。 


トリガー

SQLiteのトリガーは、特定のテーブル上の特定のイベントに応じて指定された一連のアクションを自動的に実行する特別な種類のストアドプロシージャです。これらのイベントは、テーブル内の行の挿入、更新、または削除です。 

ビュー

SQLiteのビューは、SELECTクエリの結果セットに基づいた仮想テーブルです。テーブルとは異なり、ビューはデータを物理的に保存せず、代わりに1つ以上のテーブルから特定の構造や形式でデータを表示するための手段を提供します。これにより、複雑なクエリを簡素化したり、データのセキュリティを向上させたりすることが可能です。

新しいテーブルを作成し、すでにデータ量が大きいデータベースに追加する前に、どのテーブルを削除し、どのテーブルを保持するかを判断する方法が必要です。簡単な解決策として、以前のデータベースに存在していることがわかっている各テーブル(例:Data_AU)を確認することが考えられます。しかし、どのテーブルをメモリから削除するかをハードコードすることはできません。プログラムは、不要になったテーブルを自動的に見つける必要があります。そのためには、データベースに存在するテーブルを確認し、削除対象のテーブルを反復処理し、保持するテーブルはスキップする必要があります。

SQLiteには、データベース内のすべてのオブジェクトに関する情報や、それらを定義するために使用されるSQL文などのメタデータを格納するSQLITE_MASTER(またはSQLITE_SCHEMA)というテーブルがあります。これはSQLiteで最も重要なシステムカタログです。以下のクエリを使用して、データベース内のすべての情報を取得できます。

SELECT * FROM SQLITE_MASTER;

 以下がデータベース出力です。

type    name		    		tbl_name        rootpage        sql
table   Data_None       		Data_None       2       	CREATE TABLE Data_None(ID INT NOT NULL,EVENTID  INT   NOT NULL,COUNTRY  STRING   NOT NULL,EVENTNAME   STRING   NOT NULL,EVENTTYPE   STRING   NOT NULL,EVENTIMPORTANCE   STRING   NOT NULL,EVENTDATE   STRING   NOT NULL,EVENTCURRENCY  STRING   NOT NULL,EVENTCODE   STRING   NOT NULL,EVENTSECTOR STRING   NOT NULL,EVENTFORECAST  STRING   NOT NULL,EVENTPREVALUE  STRING   NOT NULL,EVENTIMPACT STRING   NOT NULL,EVENTFREQUENCY STRING   NOT NULL,PRIMARY KEY(ID))
index   sqlite_autoindex_Data_None_1    Data_None       3       
table   Data_US 			Data_US 	4       	CREATE TABLE Data_US(ID INT NOT NULL,EVENTID  INT   NOT NULL,COUNTRY  STRING   NOT NULL,EVENTNAME   STRING   NOT NULL,EVENTTYPE   STRING   NOT NULL,EVENTIMPORTANCE   STRING   NOT NULL,EVENTDATE   STRING   NOT NULL,EVENTCURRENCY  STRING   NOT NULL,EVENTCODE   STRING   NOT NULL,EVENTSECTOR STRING   NOT NULL,EVENTFORECAST  STRING   NOT NULL,EVENTPREVALUE  STRING   NOT NULL,EVENTIMPACT STRING   NOT NULL,EVENTFREQUENCY STRING   NOT NULL,PRIMARY KEY(ID))
index   sqlite_autoindex_Data_US_1      Data_US 	5       
table   Data_UK 			Data_UK 	6       	CREATE TABLE Data_UK(ID INT NOT NULL,EVENTID  INT   NOT NULL,COUNTRY  STRING   NOT NULL,EVENTNAME   STRING   NOT NULL,EVENTTYPE   STRING   NOT NULL,EVENTIMPORTANCE   STRING   NOT NULL,EVENTDATE   STRING   NOT NULL,EVENTCURRENCY  STRING   NOT NULL,EVENTCODE   STRING   NOT NULL,EVENTSECTOR STRING   NOT NULL,EVENTFORECAST  STRING   NOT NULL,EVENTPREVALUE  STRING   NOT NULL,EVENTIMPACT STRING   NOT NULL,EVENTFREQUENCY STRING   NOT NULL,PRIMARY KEY(ID))
index   sqlite_autoindex_Data_UK_1      Data_UK 	7       
table   Data_AU 			Data_AU 	8       	CREATE TABLE Data_AU(ID INT NOT NULL,EVENTID  INT   NOT NULL,COUNTRY  STRING   NOT NULL,EVENTNAME   STRING   NOT NULL,EVENTTYPE   STRING   NOT NULL,EVENTIMPORTANCE   STRING   NOT NULL,EVENTDATE   STRING   NOT NULL,EVENTCURRENCY  STRING   NOT NULL,EVENTCODE   STRING   NOT NULL,EVENTSECTOR STRING   NOT NULL,EVENTFORECAST  STRING   NOT NULL,EVENTPREVALUE  STRING   NOT NULL,EVENTIMPACT STRING   NOT NULL,EVENTFREQUENCY STRING   NOT NULL,PRIMARY KEY(ID))
index   sqlite_autoindex_Data_AU_1      Data_AU 	9       
table   Records 			Records 	38774   	CREATE TABLE Records(RECORDEDTIME INT NOT NULL)
table   AutoDST 			AutoDST 	38775   	CREATE TABLE AutoDST(DST STRING NOT NULL)

データベースの出力からわかるように、これまで作成していなかったインデックスと呼ばれるものがあります。

インデックス

SQLiteのインデックスは、テーブルからレコードを素早く取得することでクエリ操作のパフォーマンスを向上させるデータベースオブジェクトです。インデックスは、データベースエンジンが行を効率的に見つけられるようにソートされたデータ構造(通常は二進木)を作成するため、検索、並び替え、結合操作を高速化するのに特に役立ちます。

では、なぜ以前作成していなかったインデックスが作成されたのでしょうか。

SQLiteでは、テーブルに主キーが設定されている場合、そのテーブルに対してインデックスが自動的に作成されます。

以前のデータベース出力で、データベース内のすべてのオブジェクトとメタデータを取得しました。これで不要なテーブルを特定して削除できるようになりました。この処理をおこなうために、必要なテーブルとそれらを作成するためのSQLステートメントの配列を準備し、これらをデータベース出力と比較して一致しないものを削除します。

CNewsクラスには、次のクラスからのマルチレベル継承があります。

  • CCandleProperties
  • CChartProperties
  • CSymbolProperties

CNewsクラスには、次のクラスからのインクルードがあります。

  • CDaylightSavings_UK
  • CDaylightSavings_US
  • CDaylightSavings_AU

CNewsクラスには、ヘッダーファイルCommonVariables.mqhからのインクルードがあります。

CNewsクラスには、次のクラスからの階層的継承があります。

  • CSymbolProperties
  • CSymbolInfo
  • CCandleProperties
  • CTimeManagement
//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+
#include "CommonVariables.mqh"
#include "DayLightSavings/DaylightSavings_UK.mqh"
#include "DayLightSavings/DaylightSavings_US.mqh"
#include "DayLightSavings/DaylightSavings_AU.mqh"
#include "CandleProperties.mqh"

//+------------------------------------------------------------------+
//|News class                                                        |
//+------------------------------------------------------------------+
class CNews : private CCandleProperties
  {
   //Private Declarations Only accessable by this class/header file
private:

   //-- To keep track of what is in our database
   enum CalendarComponents
     {
      AutoDST_Table,//AutoDST Table
      CalendarAU_View,//View for DST_AU
      CalendarNONE_View,//View for DST_NONE
      CalendarUK_View,//View for DST_UK
      CalendarUS_View,//View for DST_US
      Record_Table,// Record Table
      TimeSchedule_Table,//TimeSchedule Table
      MQL5Calendar_Table,//MQL5Calendar Table
      AutoDST_Trigger,//Table Trigger for AutoDST
      Record_Trigger//Table Trigger for Record
     };

   //-- structure to retrieve all the objects in the database
   struct SQLiteMaster
     {
      string         type;//will store object's type
      string         name;//will store object's name
      string         tbl_name;//will store table name
      int            rootpage;//will store rootpage
      string         sql;//Will store the sql create statement
     } DBContents[];//Array of type SQLiteMaster

   //--  MQL5CalendarContents inherits from SQLiteMaster structure
   struct MQL5CalendarContents:SQLiteMaster
     {
      CalendarComponents  Content;
      string         insert;//Will store the sql insert statement
     } CalendarContents[10];//Array to Store objects in our database

   CTimeManagement   Time;//TimeManagement Object declaration
   CDaylightSavings_UK  Savings_UK;//DaylightSavings Object for the UK and EU
   CDaylightSavings_US  Savings_US;//DaylightSavings Object for the US
   CDaylightSavings_AU  Savings_AU;//DaylightSavings Object for the AU

   bool              AutoDetectDST(DST_type &dstType);//Function will determine Broker DST
   DST_type          DSTType;//variable of DST_type enumeration declared in the CommonVariables class/header file
   bool              InsertIntoTables(int db,Calendar &Evalues[]);//Function for inserting Economic Data in to a database's table
   void              CreateAutoDST(int db);//Function for creating and inserting Recommend DST for the Broker into a table
   bool              CreateCalendarTable(int db,bool &tableExists);//Function for creating a table in a database
   bool              CreateTimeTable(int db,bool &tableExists);//Function for creating a table in a database
   void              CreateCalendarViews(int db);//Function for creating a view in a database
   void              CreateRecordTable(int db);//Creates a table to store the record of when last the Calendar database was updated/created
   bool              UpdateRecords();//Checks if the main Calendar database needs an update or not
   void              EconomicDetails(Calendar &NewsTime[]);//Gets values from the MQL5 economic Calendar
   string            DropRequest;//Variable for dropping tables in the database

   //-- Function for retrieving the MQL5CalendarContents structure for the enumartion type CalendarComponents
   MQL5CalendarContents CalendarStruct(CalendarComponents Content)
     {
      MQL5CalendarContents Calendar;
      for(uint i=0;i<CalendarContents.Size();i++)
        {
         if(CalendarContents[i].Content==Content)
           {
            return CalendarContents[i];
           }
        }
      return Calendar;
     }

   //Public declarations accessable via a class's Object
public:
                     CNews(void);
                    ~CNews(void);//Deletes a text file created when the Calendar database is being worked on
   void              CreateEconomicDatabase();//Creates the Calendar database for a specific Broker
   datetime          GetLatestNewsDate();//Gets the lastest/newest date in the Calendar database
  };

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CNews::CNews(void):DropRequest("PRAGMA foreign_keys = OFF; "
                                  "PRAGMA secure_delete = ON; "
                                  "Drop %s IF EXISTS %s; "
                                  "Vacuum; "
                                  "PRAGMA foreign_keys = ON;")//Sql drop statement
  {
//-- initializing properties for the AutoDST table
   CalendarContents[0].Content = AutoDST_Table;
   CalendarContents[0].name = "AutoDST";
   CalendarContents[0].sql = "CREATE TABLE AutoDST(DST TEXT NOT NULL DEFAULT 'DST_NONE')STRICT;";
   CalendarContents[0].tbl_name = "AutoDST";
   CalendarContents[0].type = "table";
   CalendarContents[0].insert = "INSERT INTO 'AutoDST'(DST) VALUES ('%s');";

   string views[] = {"UK","US","AU","NONE"};
   string view_sql = "CREATE VIEW IF NOT EXISTS Calendar_%s "
                     "AS "
                     "SELECT C.Eventid,C.Eventname,C.Country,T.DST_%s as Time,C.EventCurrency,C.Eventcode from MQL5Calendar C,Record R "
                     "Inner join TimeSchedule T on C.ID=T.ID "
                     "Where DATE(REPLACE(T.DST_%s,'.','-'))=R.Date "
                     "Order by T.DST_%s Asc;";

//-- Sql statements for creating the table views
   for(uint i=1;i<=views.Size();i++)
     {
      CalendarContents[i].Content = (CalendarComponents)i;
      CalendarContents[i].name = StringFormat("Calendar_%s",views[i-1]);
      CalendarContents[i].sql = StringFormat(view_sql,views[i-1],views[i-1],views[i-1],views[i-1]);
      CalendarContents[i].tbl_name = StringFormat("Calendar_%s",views[i-1]);
      CalendarContents[i].type = "view";
     }

//-- initializing properties for the Record table
   CalendarContents[5].Content = Record_Table;
   CalendarContents[5].name = "Record";
   CalendarContents[5].sql = "CREATE TABLE Record(Date TEXT NOT NULL)STRICT;";
   CalendarContents[5].tbl_name="Record";
   CalendarContents[5].type = "table";
   CalendarContents[5].insert = "INSERT INTO 'Record'(Date) VALUES (Date(REPLACE('%s','.','-')));";

//-- initializing properties for the TimeSchedule table
   CalendarContents[6].Content = TimeSchedule_Table;
   CalendarContents[6].name = "TimeSchedule";
   CalendarContents[6].sql = "CREATE TABLE TimeSchedule(ID INT NOT NULL,DST_UK   TEXT   NOT NULL,DST_US   TEXT   NOT NULL,"
                             "DST_AU   TEXT   NOT NULL,DST_NONE   TEXT   NOT NULL,FOREIGN KEY (ID) REFERENCES MQL5Calendar (ID))STRICT;";
   CalendarContents[6].tbl_name="TimeSchedule";
   CalendarContents[6].type = "table";
   CalendarContents[6].insert = "INSERT INTO 'TimeSchedule'(ID,DST_UK,DST_US,DST_AU,DST_NONE) "
                                "VALUES (%d,'%s','%s', '%s', '%s');";

//-- initializing properties for the MQL5Calendar table
   CalendarContents[7].Content = MQL5Calendar_Table;
   CalendarContents[7].name = "MQL5Calendar";
   CalendarContents[7].sql = "CREATE TABLE MQL5Calendar(ID INT NOT NULL,EVENTID  INT   NOT NULL,COUNTRY  TEXT   NOT NULL,"
                             "EVENTNAME   TEXT   NOT NULL,EVENTTYPE   TEXT   NOT NULL,EVENTIMPORTANCE   TEXT   NOT NULL,"
                             "EVENTCURRENCY  TEXT   NOT NULL,EVENTCODE   TEXT   NOT NULL,EVENTSECTOR TEXT   NOT NULL,"
                             "EVENTFORECAST  TEXT   NOT NULL,EVENTPREVALUE  TEXT   NOT NULL,EVENTIMPACT TEXT   NOT NULL,"
                             "EVENTFREQUENCY TEXT   NOT NULL,PRIMARY KEY(ID))STRICT;";
   CalendarContents[7].tbl_name="MQL5Calendar";
   CalendarContents[7].type = "table";
   CalendarContents[7].insert = "INSERT INTO 'MQL5Calendar'(ID,EVENTID,COUNTRY,EVENTNAME,EVENTTYPE,EVENTIMPORTANCE,EVENTCURRENCY,EVENTCODE,"
                                "EVENTSECTOR,EVENTFORECAST,EVENTPREVALUE,EVENTIMPACT,EVENTFREQUENCY) "
                                "VALUES (%d,%d,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s');";

//-- Sql statement for creating the AutoDST table's trigger
   CalendarContents[8].Content = AutoDST_Trigger;
   CalendarContents[8].name = "OnlyOne_AutoDST";
   CalendarContents[8].sql = "CREATE TRIGGER IF NOT EXISTS OnlyOne_AutoDST "
                             "BEFORE INSERT ON AutoDST "
                             "BEGIN "
                             "Delete from AutoDST; "
                             "END;";
   CalendarContents[8].tbl_name="AutoDST";
   CalendarContents[8].type = "trigger";

//-- Sql statement for creating the Record table's trigger
   CalendarContents[9].Content = Record_Trigger;
   CalendarContents[9].name = "OnlyOne_Record";
   CalendarContents[9].sql = "CREATE TRIGGER IF NOT EXISTS OnlyOne_Record "
                             "BEFORE INSERT ON Record "
                             "BEGIN "
                             "Delete from Record; "
                             "END;";
   CalendarContents[9].tbl_name="Record";
   CalendarContents[9].type = "trigger";
  }

//+------------------------------------------------------------------+
//|Destructor                                                        |
//+------------------------------------------------------------------+
CNews::~CNews(void)
  {
   if(FileIsExist(NEWS_TEXT_FILE,FILE_COMMON))//Check if the news database open text file exists
     {
      FileDelete(NEWS_TEXT_FILE,FILE_COMMON);
     }
  }

//+------------------------------------------------------------------+
//|Gets values from the MQL5 economic Calendar                       |
//+------------------------------------------------------------------+
void CNews::EconomicDetails(Calendar &NewsTime[])
  {
   int Size=0;//to keep track of the size of the events in the NewsTime array
   MqlCalendarCountry countries[];
   string Country_code="";

   for(int i=0,count=CalendarCountries(countries); i<count; i++)
     {
      MqlCalendarValue values[];
      datetime date_from=0;//Get date from the beginning
      datetime date_to=(datetime)(Time.MonthsS()+iTime(Symbol(),PERIOD_D1,0));//Date of the next month from the current day
      if(CalendarValueHistory(values,date_from,date_to,countries[i].code))
        {
         for(int x=0; x<(int)ArraySize(values); x++)
           {
            MqlCalendarEvent event;
            ulong event_id=values[x].event_id;//Get the event id
            if(CalendarEventById(event_id,event))
              {
               ArrayResize(NewsTime,Size+1,Size+2);//Readjust the size of the array to +1 of the array size
               StringReplace(event.name,"'","");//Removing or replacing single quotes(') from event name with an empty string
               NewsTime[Size].CountryName = countries[i].name;//storing the country's name from the specific event
               NewsTime[Size].EventName = event.name;//storing the event's name
               NewsTime[Size].EventType = EnumToString(event.type);//storing the event type from (ENUM_CALENDAR_EVENT_TYPE) to a string
               //-- storing the event importance from (ENUM_CALENDAR_EVENT_IMPORTANCE) to a string
               NewsTime[Size].EventImportance = EnumToString(event.importance);
               NewsTime[Size].EventId = event.id;//storing the event id
               NewsTime[Size].EventDate = TimeToString(values[x].time);//storing normal event time
               NewsTime[Size].EventCurrency = countries[i].currency;//storing event currency
               NewsTime[Size].EventCode = countries[i].code;//storing event code
               NewsTime[Size].EventSector = EnumToString(event.sector);//storing event sector from (ENUM_CALENDAR_EVENT_SECTOR) to a string
               if(values[x].HasForecastValue())//Checks if the event has a forecast value
                 {
                  NewsTime[Size].EventForecast = (string)values[x].forecast_value;//storing the forecast value into a string
                 }
               else
                 {
                  NewsTime[Size].EventForecast = "None";//storing 'None' as the forecast value
                 }

               if(values[x].HasPreviousValue())//Checks if the event has a previous value
                 {
                  NewsTime[Size].EventPreval = (string)values[x].prev_value;//storing the previous value into a string
                 }
               else
                 {
                  NewsTime[Size].EventPreval = "None";//storing 'None' as the previous value
                 }
               //-- storing the event impact from (ENUM_CALENDAR_EVENT_IMPACT) to a string
               NewsTime[Size].EventImpact =  EnumToString(values[x].impact_type);
               //-- storing the event frequency from (ENUM_CALENDAR_EVENT_FREQUENCY) to a string
               NewsTime[Size].EventFrequency =  EnumToString(event.frequency);
               Size++;//incrementing the Calendar array NewsTime
              }
           }
        }
     }
  }

//+------------------------------------------------------------------+
//|Checks if the main Calendar database needs an update or not       |
//+------------------------------------------------------------------+
bool CNews::UpdateRecords()
  {
//initialize variable to true
   bool perform_update=true;
//--- open/create
//-- try to open database Calendar
   int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE| DATABASE_OPEN_COMMON);
   if(db==INVALID_HANDLE)//Checks if the database was able to be opened
     {
      //if opening the database failed
      if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Checks if the database Calendar exists in the common folder
        {
         return perform_update;//Returns true when the database was failed to be opened and the file doesn't exist in the common folder
        }
     }

   int MasterRequest = DatabasePrepare(db,"select * from sqlite_master where type<>'index';");
   if(MasterRequest==INVALID_HANDLE)
     {
      Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
     }
   else
     {
      SQLiteMaster ReadContents;
      //Assigning values from the sql query into DBContents array
      for(int i=0; DatabaseReadBind(MasterRequest,ReadContents); i++)
        {
         ArrayResize(DBContents,i+1,i+2);
         DBContents[i].type = ReadContents.type;
         DBContents[i].name = ReadContents.name;
         DBContents[i].tbl_name = ReadContents.tbl_name;
         DBContents[i].rootpage = ReadContents.rootpage;
         /*Check if the end of the sql string has a character ';' if not add this character to the string*/
         DBContents[i].sql = (StringFind(ReadContents.sql,";",StringLen(ReadContents.sql)-1)==
                              (StringLen(ReadContents.sql)-1))?ReadContents.sql:ReadContents.sql+";";;
        }

      uint contents_exists = 0;
      for(uint i=0;i<DBContents.Size();i++)
        {
         bool isCalendarContents = false;
         for(uint x=0;x<CalendarContents.Size();x++)
           {
            /*Store Sql query from CalendarContents without string ' IF NOT EXISTS'*/
            string CalendarSql=CalendarContents[x].sql;
            StringReplace(CalendarSql," IF NOT EXISTS","");
            //-- Check if the Db object is in our list
            if(DBContents[i].name==CalendarContents[x].name&&
               (DBContents[i].sql==CalendarSql||
                DBContents[i].sql==CalendarContents[x].sql)&&
               CalendarContents[x].type==DBContents[i].type&&
               CalendarContents[x].tbl_name==DBContents[i].tbl_name)
              {
               contents_exists++;
               isCalendarContents = true;
              }
           }
         if(!isCalendarContents)
           {
            //-- Print DBcontent's name if it does not match with CalendarContents
            PrintFormat("DBContent: %s is not needed!",DBContents[i].name);
            //-- We will drop the table if it is not neccessary
            DatabaseExecute(db,StringFormat(DropRequest,DBContents[i].type,DBContents[i].name));
            Print("Attempting To Clean Database...");
           }
        }
      /*If not all the CalendarContents exist in the Calendar Database before an update */
      if(contents_exists!=CalendarContents.Size())
        {
         return perform_update;
        }
     }
   if(!DatabaseTableExists(db,CalendarStruct(Record_Table).name))//If the database table 'Record' doesn't exist
     {
      DatabaseClose(db);
      return perform_update;
     }

//-- Sql query to determine the lastest or maximum date recorded
   /* If the last recorded date data in the 'Record' table is not equal to the current day, perform an update! */
   string request_text=StringFormat("SELECT Date FROM %s where Date=Date(REPLACE('%s','.','-'))",
                                    CalendarStruct(Record_Table).name,TimeToString(TimeTradeServer()));
   int request=DatabasePrepare(db,request_text);//Creates a handle of a request, which can then be executed using DatabaseRead()
   if(request==INVALID_HANDLE)//Checks if the request failed to be completed
     {
      Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
      DatabaseClose(db);
      return perform_update;
     }

   if(DatabaseRead(request))//Will be true if there are results from the sql query/request
     {
      DatabaseFinalize(request);//Removes a request created in DatabasePrepare()
      DatabaseClose(db);//Closes the database
      perform_update=false;
      return perform_update;
     }
   else
     {
      DatabaseFinalize(request);//Removes a request created in DatabasePrepare()
      DatabaseClose(db);//Closes the database
      return perform_update;
     }
  }

//+------------------------------------------------------------------+
//|Creates the Calendar database for a specific Broker               |
//+------------------------------------------------------------------+
void CNews::CreateEconomicDatabase()
  {
   if(FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Check if the database exists
     {
      if(!UpdateRecords())//Check if the database is up to date
        {
         return;//will terminate execution of the rest of the code below
        }
     }
   if(FileIsExist(NEWS_TEXT_FILE,FILE_COMMON))//Check if the database is open
     {
      return;//will terminate execution of the rest of the code below
     }

   Calendar Evalues[];//Creating a Calendar array variable
   bool failed=false,tableExists=false;
   int file=INVALID_HANDLE;
//--- open/create the database 'Calendar'
//-- will try to open/create in the common folder
   int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE| DATABASE_OPEN_COMMON);
   if(db==INVALID_HANDLE)//Checks if the database 'Calendar' failed to open/create
     {
      Print("DB: ",NEWS_DATABASE_FILE, " open failed with code ", GetLastError());
      return;//will terminate execution of the rest of the code below
     }
   else
     {
      //-- try to create a text file 'NewsDatabaseOpen' in common folder
      file=FileOpen(NEWS_TEXT_FILE,FILE_WRITE|FILE_ANSI|FILE_TXT|FILE_COMMON);
      if(file==INVALID_HANDLE)
        {
         DatabaseClose(db);//Closes the database 'Calendar' if the News text file failed to be created
         return;//will terminate execution of the rest of the code below
        }
     }

   DatabaseTransactionBegin(db);//Starts transaction execution
   Print("Please wait...");

//-- attempt to create the MQL5Calendar and TimeSchedule tables
   if(!CreateCalendarTable(db,tableExists)||!CreateTimeTable(db,tableExists))
     {
      FileClose(file);//Closing the file 'NewsDatabaseOpen.txt'
      FileDelete(NEWS_TEXT_FILE,FILE_COMMON);//Deleting the file 'NewsDatabaseOpen.txt'
      return;//will terminate execution of the rest of the code below
     }

   EconomicDetails(Evalues);//Retrieving the data from the Economic Calendar
   if(tableExists)//Checks if there is an existing table within the Calendar Database
     {
      //if there is an existing table we will notify the user that we are updating the table.
      PrintFormat("Updating %s",NEWS_DATABASE_FILE);
     }
   else
     {
      //if there isn't an existing table we will notify the user that we about to create one
      PrintFormat("Creating %s",NEWS_DATABASE_FILE);
     }

//-- attempt to insert economic event data into the calendar tables
   if(!InsertIntoTables(db,Evalues))
     {
      //-- Will assign true if inserting economic vaules failed in the MQL5Calendar and TimeSchedule tables
      failed=true;
     }

   if(failed)
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(db);
      PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
      FileClose(file);//Close the text file 'NEWS_TEXT_FILE'
      FileDelete(NEWS_TEXT_FILE,FILE_COMMON);//Delete the text file, as we are reverted/rolled-back the database
      ArrayRemove(Evalues,0,WHOLE_ARRAY);//Removes the values in the array
     }
   else
     {
      CreateCalendarViews(db);
      CreateRecordTable(db);//Will create the 'Record' table and insert the  current time
      CreateAutoDST(db);//Will create the 'AutoDST' table and insert the broker's DST schedule
      FileClose(file);//Close the text file 'NEWS_TEXT_FILE'
      FileDelete(NEWS_TEXT_FILE,FILE_COMMON);//Delete the text file, as we are about to close the database
      ArrayRemove(Evalues,0,WHOLE_ARRAY);//Removes the values in the array
      if(tableExists)
        {
         //Let the user/trader know that the database was updated
         PrintFormat("%s Updated",NEWS_DATABASE_FILE);
        }
      else
        {
         //Let the user/trader know that the database was created
         PrintFormat("%s Created",NEWS_DATABASE_FILE);
        }
     }
//--- all transactions have been performed successfully - record changes and unlock the database
   DatabaseTransactionCommit(db);
   DatabaseClose(db);//Close the database
  }


//+------------------------------------------------------------------+
//|Function for creating a table in a database                       |
//+------------------------------------------------------------------+
bool CNews::CreateCalendarTable(int db,bool &tableExists)
  {
//-- Checks if a table 'MQL5Calendar' exists
   if(DatabaseTableExists(db,CalendarStruct(MQL5Calendar_Table).name))
     {
      tableExists=true;//Assigns true to tableExists variable
      //-- Checks if a table 'TimeSchedule' exists in the database 'Calendar'
      if(DatabaseTableExists(db,CalendarStruct(TimeSchedule_Table).name))
        {
         //-- We will drop the table if the table already exists
         if(!DatabaseExecute(db,StringFormat("Drop Table %s",CalendarStruct(TimeSchedule_Table).name)))
           {
            //If the table failed to be dropped/deleted
            PrintFormat("Failed to drop table %s with code %d",CalendarStruct(TimeSchedule_Table).name,GetLastError());
            DatabaseClose(db);//Close the database
            return false;//will terminate execution of the rest of the code below and return false, when the table cannot be dropped
           }
        }
      //--We will drop the table if the table already exists
      if(!DatabaseExecute(db,StringFormat("Drop Table %s",CalendarStruct(MQL5Calendar_Table).name)))
        {
         //If the table failed to be dropped/deleted
         PrintFormat("Failed to drop table %s with code %d",CalendarStruct(MQL5Calendar_Table).name,GetLastError());
         DatabaseClose(db);//Close the database
         return false;//will terminate execution of the rest of the code below and return false, when the table cannot be dropped
        }
     }
//-- If the database table 'MQL5Calendar' doesn't exist
   if(!DatabaseTableExists(db,CalendarStruct(MQL5Calendar_Table).name))
     {
      //--- create the table 'MQL5Calendar'
      if(!DatabaseExecute(db,CalendarStruct(MQL5Calendar_Table).sql))//Checks if the table was successfully created
        {
         Print("DB: create the Calendar table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return false;//Function returns false if creating the table failed
        }
     }
   return true;//Function returns true if creating the table was successful
  }

//+------------------------------------------------------------------+
//|Function for creating a table in a database                       |
//+------------------------------------------------------------------+
bool CNews::CreateTimeTable(int db,bool &tableExists)
  {
//-- If the database table 'TimeSchedule' doesn't exist
   if(!DatabaseTableExists(db,CalendarStruct(TimeSchedule_Table).name))
     {
      //--- create the table 'TimeSchedule'
      if(!DatabaseExecute(db,CalendarStruct(TimeSchedule_Table).sql))//Checks if the table was successfully created
        {
         Print("DB: create the Calendar table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return false;//Function returns false if creating the table failed
        }
     }
   return true;//Function returns true if creating the table was successful
  }

//+------------------------------------------------------------------+
//|Function for creating views in a database                         |
//+------------------------------------------------------------------+
void CNews::CreateCalendarViews(int db)
  {
   for(uint i=1;i<=4;i++)
     {
      if(!DatabaseExecute(db,CalendarStruct((CalendarComponents)i).sql))//Checks if the view was successfully created
        {
         Print("DB: create the Calendar view failed with code ", GetLastError());
        }
     }
  }

//+------------------------------------------------------------------+
//|Function for inserting Economic Data in to a database's table     |
//+------------------------------------------------------------------+
bool CNews::InsertIntoTables(int db,Calendar &Evalues[])
  {
   for(uint i=0; i<Evalues.Size(); i++)//Looping through all the Economic Events
     {
      string request_insert_into_calendar =
         StringFormat(CalendarStruct(MQL5Calendar_Table).insert,
                      i,
                      Evalues[i].EventId,
                      Evalues[i].CountryName,
                      Evalues[i].EventName,
                      Evalues[i].EventType,
                      Evalues[i].EventImportance,
                      Evalues[i].EventCurrency,
                      Evalues[i].EventCode,
                      Evalues[i].EventSector,
                      Evalues[i].EventForecast,
                      Evalues[i].EventPreval,
                      Evalues[i].EventImpact,
                      Evalues[i].EventFrequency);//Inserting all the columns for each event record
      if(DatabaseExecute(db,request_insert_into_calendar))//Check if insert query into calendar was successful
        {
         string request_insert_into_time =
            StringFormat(CalendarStruct(TimeSchedule_Table).insert,
                         i,
                         //-- Economic EventDate adjusted for UK DST(Daylight Savings Time)
                         Savings_UK.adjustDaylightSavings(StringToTime(Evalues[i].EventDate)),
                         //-- Economic EventDate adjusted for US DST(Daylight Savings Time)
                         Savings_US.adjustDaylightSavings(StringToTime(Evalues[i].EventDate)),
                         //-- Economic EventDate adjusted for AU DST(Daylight Savings Time)
                         Savings_AU.adjustDaylightSavings(StringToTime(Evalues[i].EventDate)),
                         Evalues[i].EventDate//normal Economic EventDate
                        );//Inserting all the columns for each event record
         if(!DatabaseExecute(db,request_insert_into_time))
           {
            Print(GetLastError());
            //-- Will print the sql query to check for any errors or possible defaults in the query/request
            Print(request_insert_into_time);
            return false;//Will end the loop and return false, as values failed to be inserted into the table
           }
        }
      else
        {
         Print(GetLastError());
         //-- Will print the sql query to check for any errors or possible defaults in the query/request
         Print(request_insert_into_calendar);
         return false;//Will end the loop and return false, as values failed to be inserted into the table
        }
     }
   return true;//Will return true, all values were inserted into the table successfully
  }

//+------------------------------------------------------------------+
//|Creates a table to store the record of when last the Calendar     |
//|database was updated/created                                      |
//+------------------------------------------------------------------+
void CNews::CreateRecordTable(int db)
  {
   bool failed=false;
   if(!DatabaseTableExists(db,CalendarStruct(Record_Table).name))//Checks if the table 'Record' exists in the databse 'Calendar'
     {
      //--- create the table
      if(!DatabaseExecute(db,CalendarStruct(Record_Table).sql))//Will attempt to create the table 'Record'
        {
         Print("DB: create the Records table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return;//Exits the function if creating the table failed
        }
      else//If Table was created Successfully then Create Trigger
        {
         DatabaseExecute(db,CalendarStruct(Record_Trigger).sql);
        }
     }
   else
     {
      DatabaseExecute(db,CalendarStruct(Record_Trigger).sql);
     }
//Sql query/request to insert the current time into the 'Date' column in the table 'Record'
   string request_text=StringFormat(CalendarStruct(Record_Table).insert,TimeToString(TimeTradeServer()));
   if(!DatabaseExecute(db, request_text))//Will attempt to run this sql request/query
     {
      Print(GetLastError());
      PrintFormat(CalendarStruct(Record_Table).insert,TimeToString(TimeTradeServer()));
      failed=true;//assign true if the request failed
     }
   if(failed)
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(db);
      PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
     }
  }

//+------------------------------------------------------------------+
//|Function for creating and inserting Recommend DST for the Broker  |
//|into a table                                                      |
//+------------------------------------------------------------------+
void CNews::CreateAutoDST(int db)
  {
   bool failed=false;//boolean variable
   if(!AutoDetectDST(DSTType))//Check if AutoDetectDST went through all the right procedures
     {
      return;//will terminate execution of the rest of the code below
     }

   if(!DatabaseTableExists(db,CalendarStruct(AutoDST_Table).name))//Checks if the table 'AutoDST' exists in the databse 'Calendar'
     {
      //--- create the table AutoDST
      if(!DatabaseExecute(db,CalendarStruct(AutoDST_Table).sql))//Will attempt to create the table 'AutoDST'
        {
         Print("DB: create the AutoDST table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return;//Exits the function if creating the table failed
        }
      else//If Table was created Successfully then Create Trigger
        {
         DatabaseExecute(db,CalendarStruct(AutoDST_Trigger).sql);
        }
     }
   else
     {
      //Create trigger if AutoDST table exists
      DatabaseExecute(db,CalendarStruct(AutoDST_Trigger).sql);
     }
//Sql query/request to insert the recommend DST for the Broker using the DSTType variable to determine which string data to insert
   string request_text=StringFormat(CalendarStruct(AutoDST_Table).insert,EnumToString(DSTType));
   if(!DatabaseExecute(db, request_text))//Will attempt to run this sql request/query
     {
      Print(GetLastError());
      PrintFormat(CalendarStruct(AutoDST_Table).insert,EnumToString(DSTType));//Will print the sql query if failed
      failed=true;//assign true if the request failed
     }
   if(failed)
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(db);
      PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
     }
  }

//+------------------------------------------------------------------+
//|Gets the latest/newest date in the Calendar database              |
//+------------------------------------------------------------------+
datetime CNews::GetLatestNewsDate()
  {
//--- open the database 'Calendar' in the common folder
   int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READONLY|DATABASE_OPEN_COMMON);

   if(db==INVALID_HANDLE)//Checks if 'Calendar' failed to be opened
     {
      if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Checks if 'Calendar' database exists
        {
         Print("Could not find Database!");
         return 0;//Will return the earliest date which is 1970.01.01 00:00:00
        }
     }
   string latest_record="1970.01.01";//string variable with the first/earliest possible date in MQL5
//Sql query to determine the lastest or maximum recorded time from which the database was updated.
   string request_text="SELECT REPLACE(Date,'-','.') FROM 'Record'";
   int request=DatabasePrepare(db,request_text);
   if(request==INVALID_HANDLE)
     {
      Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
      DatabaseClose(db);//Close Database
      return 0;
     }
   if(DatabaseRead(request))//Will read the one record in the 'Record' table
     {
      //-- Will assign the first column(column 0) value to the variable 'latest_record'
      if(!DatabaseColumnText(request,0,latest_record))
        {
         Print("DatabaseRead() failed with code ", GetLastError());
         DatabaseFinalize(request);//Finalize request
         DatabaseClose(db);//Closes the database 'Calendar'
         return D'1970.01.01';//Will end the for loop and will return the earliest date which is 1970.01.01 00:00:00
        }
     }
   DatabaseFinalize(request);
   DatabaseClose(db);//Closes the database 'Calendar'
   return (datetime)latest_record;//Returns the string latest_record converted to datetime
  }

//+------------------------------------------------------------------+
//|Function will determine Broker DST                                |
//+------------------------------------------------------------------+
bool CNews::AutoDetectDST(DST_type &dstType)
  {
   MqlCalendarValue values[];//Single array of MqlCalendarValue type
   string eventtime[];//Single string array variable to store NFP(Nonfarm Payrolls) dates for the 'United States' from the previous year
//-- Will store the previous year into an integer
   int lastyear = Time.ReturnYear(Time.TimeMinusOffset(iTime(Symbol(),PERIOD_CURRENT,0),Time.YearsS()));
//-- Will store the start date for the previous year
   datetime lastyearstart = StringToTime(StringFormat("%s.01.01 00:00:00",(string)lastyear));
//-- Will store the end date for the previous year
   datetime lastyearend = StringToTime(StringFormat("%s.12.31 23:59:59",(string)lastyear));
//-- Getting last year's calendar values for CountryCode = 'US'
   if(CalendarValueHistory(values,lastyearstart,lastyearend,"US"))
     {
      for(int x=0; x<(int)ArraySize(values); x++)
        {
         if(values[x].event_id==840030016)//Get only NFP Event Dates
           {
            ArrayResize(eventtime,eventtime.Size()+1,eventtime.Size()+2);//Increasing the size of eventtime array by 1
            eventtime[eventtime.Size()-1] = TimeToString(values[x].time);//Storing the dates in an array of type string
           }
        }
     }
//-- datetime variables to store the broker's timezone shift(change)
   datetime ShiftStart=D'1970.01.01 00:00:00',ShiftEnd=D'1970.01.01 00:00:00';
   string   EURUSD="";//String variables declarations for working with EURUSD
   bool     EurusdIsFound=false;//Boolean variables declarations for working with EURUSD
   for(int i=0;i<SymbolsTotal(true);i++)//Will loop through all the Symbols inside the Market Watch
     {
      string SymName = SymbolName(i,true);//Assign the Symbol Name of index 'i' from the list of Symbols inside the Market Watch
      //-- Check if the Symbol outside the Market Watch has a SYMBOL_CURRENCY_BASE of EUR
      //-- and a SYMBOL_CURRENCY_PROFIT of USD, and this Symbol is not a Custom Symbol(Is not from the broker)
      if(((CurrencyBase(SymName)=="EUR"&&CurrencyProfit(SymName)=="USD")||
          (StringFind(SymName,"EUR")>-1&&CurrencyProfit(SymName)=="USD"))&&!Custom(SymName))
        {
         EURUSD = SymName;//Assigning the name of the EURUSD Symbol found inside the Market Watch
         EurusdIsFound = true;//EURUSD Symbol was found in the Trading Terminal for your Broker
         break;//Will end the for loop
        }
     }
   if(!EurusdIsFound)//Check if EURUSD Symbol was already Found in the Market Watch
     {
      for(int i=0; i<SymbolsTotal(false); i++)//Will loop through all the available Symbols outside the Market Watch
        {
         string SymName = SymbolName(i,false);//Assign the Symbol Name of index 'i' from the list of Symbols outside the Market Watch
         //-- Check if the Symbol outside the Market Watch has a SYMBOL_CURRENCY_BASE of EUR
         //-- and a SYMBOL_CURRENCY_PROFIT of USD, and this Symbol is not a Custom Symbol(Is not from the broker)
         if(((CurrencyBase(SymName)=="EUR"&&CurrencyProfit(SymName)=="USD")||
             (StringFind(SymName,"EUR")>-1&&CurrencyProfit(SymName)=="USD"))&&!Custom(SymName))
           {
            EURUSD = SymName;//Assigning the name of the EURUSD Symbol found outside the Market Watch
            EurusdIsFound = true;//EURUSD Symbol was found in the Trading Terminal for your Broker
            break;//Will end the for loop
           }
        }
     }
   if(!EurusdIsFound)//Check if EURUSD Symbol was Found in the Trading Terminal for your Broker
     {
      Print("Cannot Find EURUSD!");
      Print("Cannot Create Database!");
      Print("Server DST Cannot be Detected!");
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }

   struct DST
     {
      bool           result;
      datetime       date;
     } previousresult,currentresult;

   bool timeIsShifted;//Boolean variable declaration will be used to determine if the broker changes it's timezone
   for(uint i=0;i<eventtime.Size();i++)
     {
      //-- Store the result of if the eventdate is the larger candlestick
      currentresult.result = IsLargerThanPreviousAndNext((datetime)eventtime[i],Time.HoursS(),EURUSD);
      currentresult.date = (datetime)eventtime[i];//Store the eventdate from eventtime[i]
      //-- Check if there is a difference between the previous result and the current result
      timeIsShifted = ((currentresult.result!=previousresult.result&&i>0)?true:false);

      //-- Check if the Larger candle has shifted from the previous event date to the current event date in eventtime[i] array
      if(timeIsShifted)
        {
         if(ShiftStart==D'1970.01.01 00:00:00')//Check if the ShiftStart variable has not been assigned a relevant value yet
           {
            ShiftStart=currentresult.date;//Store the eventdate for when the timeshift began
           }
         ShiftEnd=previousresult.date;//Store the eventdate timeshift
        }
      previousresult.result = currentresult.result;//Store the previous result of if the eventdate is the larger candlestick
      previousresult.date = currentresult.date;//Store the eventdate from eventtime[i]
     }
//-- Check if the ShiftStart variable has not been assigned a relevant value and the eventdates are more than zero
   if(ShiftStart==D'1970.01.01 00:00:00'&&eventtime.Size()>0)
     {
      Print("Broker ServerTime unchanged!");
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return true;//Returning True, Broker's DST schedule was found successfully
     }

   datetime DaylightStart,DaylightEnd;//Datetime variables declarations for start and end dates for DaylightSavings
   if(Savings_AU.DaylightSavings(lastyear,DaylightStart,DaylightEnd))
     {
      if(Time.DateIsInRange(DaylightStart,DaylightEnd,ShiftStart,ShiftEnd))
        {
         Print("Broker ServerTime Adjusted For AU DST");
         dstType = DST_AU;//Assigning enumeration value AU_DST, Broker has AU DST(Daylight Savings Time)
         return true;//Returning True, Broker's DST schedule was found successfully
        }
     }
   else
     {
      Print("Something went wrong!");
      Print("Cannot Find Daylight-Savings Date For AU");
      Print("Year: %d Cannot Be Found!",lastyear);
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }

   if(Savings_UK.DaylightSavings(lastyear,DaylightStart,DaylightEnd))
     {
      if(Time.DateIsInRange(DaylightStart,DaylightEnd,ShiftStart,ShiftEnd))
        {
         Print("Broker ServerTime Adjusted For UK DST");
         dstType = DST_UK;//Assigning enumeration value UK_DST, Broker has UK/EU DST(Daylight Savings Time)
         return true;//Returning True, Broker's DST schedule was found successfully
        }
     }
   else
     {
      Print("Something went wrong!");
      Print("Cannot Find Daylight-Savings Date For UK");
      Print("Year: %d Cannot Be Found!",lastyear);
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }

   if(Savings_US.DaylightSavings(lastyear,DaylightStart,DaylightEnd))
     {
      if(Time.DateIsInRange(DaylightStart,DaylightEnd,ShiftStart,ShiftEnd))
        {
         Print("Broker ServerTime Adjusted For US DST");
         dstType = DST_US;//Assigning enumeration value US_DST, Broker has US DST(Daylight Savings Time)
         return true;//Returning True, Broker's DST schedule was found successfully
        }
     }
   else
     {
      Print("Something went wrong!");
      Print("Cannot Find Daylight-Savings Date For US");
      Print("Year: %d Cannot Be Found!",lastyear);
      dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
      return false;//Returning False, Broker's DST schedule was not found
     }
   Print("Cannot Detect Broker ServerTime Configuration!");
   dstType = DST_NONE;//Assigning enumeration value DST_NONE, Broker has no DST(Daylight Savings Time)
   return false;//Returning False, Broker's DST schedule was not found
  }
//+------------------------------------------------------------------+

目的のデータベース設計内のすべてのコンポーネントに、IDの形式として列挙値を割り当てます。

 enum CalendarComponents
     {
      AutoDST_Table,//AutoDST Table
      CalendarAU_View,//View for DST_AU
      CalendarNONE_View,//View for DST_NONE
      CalendarUK_View,//View for DST_UK
      CalendarUS_View,//View for DST_US
      Record_Table,// Record Table
      TimeSchedule_Table,//TimeSchedule Table
      MQL5Calendar_Table,//MQL5Calendar Table
      AutoDST_Trigger,//Table Trigger for AutoDST
      Record_Trigger//Table Trigger for Record
     };

SQLiteMaster構造体の目的は、現在のデータベースオブジェクトの型、名前などのプロパティを保存することです。そうすれば、DBContents配列内のすべてのオブジェクトを追跡することができます。

//-- structure to retrieve all the objects in the database
   struct SQLiteMaster
     {
      string         type;//will store object type
      string         name;//will store object's name
      string         tbl_name;//will store table name
      int            rootpage;//will store rootpage
      string         sql;//Will store the sql create statement
     } DBContents[];//Array of type SQLiteMaster

MQL5CalendarContents構造体に、Contentinsert変数である追加のプロパティを保存します。

文字列変数insertには、SQLオブジェクトのSQLinsertion文が格納されます。一方、CalendarComponentsの変数Contentには、SQLオブジェクトの列挙値がID形式で格納されます。これにより、すべてのオブジェクトプロパティがCalendarContents構造体配列に保存されると、それぞれのSQLオブジェクトがどのエントリに対応するかを判別できます。

//--  MQL5CalendarContents inherits from SQLiteMaster structure
   struct MQL5CalendarContents:SQLiteMaster
     {
      CalendarComponents  Content;
      string         insert;//Will store the sql insert statement
     } CalendarContents[10];//Array to Store objects in our database

CalendarStruct関数では、ContentパラメータがCalendarContents配列構造体内の変数Contentの列挙値と等しい場合に、MQL5CalendarContents構造体型の値を返します。

//-- Function for retrieving the MQL5CalendarContents structure for the enumartion type CalendarComponents
   MQL5CalendarContents CalendarStruct(CalendarComponents Content)
     {
      MQL5CalendarContents Calendar;
      for(uint i=0;i<CalendarContents.Size();i++)
        {
         if(CalendarContents[i].Content==Content)
           {
            return CalendarContents[i];
           }
        }
      return Calendar;
     }

文字列変数DropRequestは、不要になったデータベースオブジェクトを削除する役割を果たします。SQLクエリでは、PRAGMA文を使用します。

PRAGMA文

SQLiteのPRAGMA文は、SQLiteライブラリの操作を変更したり、データベースエンジンの内部状態を照会したりするために使用される特別なコマンドです。PRAGMAは標準SQLの一部ではなく、SQLiteに固有のものです。さまざまな環境設定とデータベースの動作を制御する方法を提供します。


PRAGMA文の目的

  • 構成:外部キー制約の有効化または無効化、ジャーナルモードの設定、メモリ使用量パラメータの調整など、データベース環境を構成します。
  • 診断:データベースの整合性の確認、現在の設定の取得、SQLiteエンジンの状態の表示など、データベースに関する情報を取得します。
  • 最適化:キャッシュサイズ、ロックモード、同期設定などのパラメータを調整することで、データベースパフォーマンスを最適化するのに役立ちます。
  • メンテナンス:インデックスの再構築、テーブルの分析、自動バキューム設定の管理などのメンテナンスタスクに役立ちます。

最初のPRAGMA文では、外部キー制約を持つテーブルを削除できないようにする外部キー制約を無効にします。

2番目のPRAGMA文では、secure_deleteを有効にします。これは、削除されたコンテンツをデータベースファイルから削除する前に0にするかどうかを制御します。この場合、データベースは、I/Oの量が増加しない場合にのみ、削除されたコンテンツを0で上書きします。

3番目のPRAGMA文では、SQLオブジェクトが存在する場合はそれを削除します。次に、Vacuumコマンドを使用してデータベースファイルを再構築し、最小限のディスク領域に再パックします。このプロセスは、データベースのパフォーマンスを最適化し、未使用のスペースを再利用するのに役立ちます。

最後に、外部キー制約を再度有効にします。

CNews::CNews(void):DropRequest("PRAGMA foreign_keys = OFF; "
                                  "PRAGMA secure_delete = ON; "
                                  "Drop %s IF EXISTS %s; "
                                  "Vacuum; "
                                  "PRAGMA foreign_keys = ON;")//Sql drop statement

AutoDSTテーブルのプロパティをCalendarContents配列の最初のインデックスに保存します。ここで、Content変数にAutoDST_Tableの列挙値を割り当てます。

次に、名前テーブル名タイプinsert文を割り当てます。テーブルを作成するためのSQL文では、列DSTにデフォルト値DST_NONEを指定し、文をSTRICTキーワードで終了します。

キーワードSTRICTは、列に挿入されるデータがその列の宣言された型と一致することを強制します。これにより、保存されるデータの種類の予測可能性と一貫性が向上します。

一方、キーワードSTRICTがない場合、基本的に任意のデータ型をテーブルに挿入でき、宣言された列のデータ型は要件ではなく推奨事項として扱われます。 

//-- initializing properties for the AutoDST table
   CalendarContents[0].Content = AutoDST_Table;
   CalendarContents[0].name = "AutoDST";
   CalendarContents[0].sql = "CREATE TABLE AutoDST(DST TEXT NOT NULL DEFAULT 'DST_NONE')STRICT;";
   CalendarContents[0].tbl_name = "AutoDST";
   CalendarContents[0].type = "table";
   CalendarContents[0].insert = "INSERT INTO 'AutoDST'(DST) VALUES ('%s');";

ここで、UK、US、AU、NONEのカレンダービューのプロパティを初期化します。 

view_sql変数には、個々のビューを作成するためのSQL文が格納されます。SQL文では、MQL5CalendarテーブルからEventidEventnameCountryEventCurrencyEventcodeを選択します。

ID      EVENTID 	COUNTRY 	EVENTNAME       				EVENTTYPE       	EVENTIMPORTANCE 		EVENTCURRENCY   EVENTCODE       EVENTSECTOR     		EVENTFORECAST   EVENTPREVALUE   EVENTIMPACT     		EVENTFREQUENCY
18742   999020002       European Union  Eurogroup Meeting       			CALENDAR_TYPE_EVENT     CALENDAR_IMPORTANCE_MODERATE    EUR     	EU      	CALENDAR_SECTOR_GOVERNMENT      None    	None    	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
18746   999010020       European Union  ECB Executive Board Member Lane Speech  	CALENDAR_TYPE_EVENT     CALENDAR_IMPORTANCE_MODERATE    EUR     	EU      	CALENDAR_SECTOR_MONEY   	None    	None    	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
34896   392010004       Japan   	Coincident Index        			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	JPY     	JP      	CALENDAR_SECTOR_BUSINESS        113900000       113900000       CALENDAR_IMPACT_NEGATIVE        CALENDAR_FREQUENCY_MONTH
34897   392010005       Japan   	Leading Index   				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	JPY     	JP      	CALENDAR_SECTOR_BUSINESS        111400000       111400000       CALENDAR_IMPACT_POSITIVE        CALENDAR_FREQUENCY_MONTH
34898   392010011       Japan   	Coincident Index m/m    			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	JPY     	JP      	CALENDAR_SECTOR_BUSINESS        None    	2400000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
34899   392010012       Japan   	Leading Index m/m       			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	JPY     	JP      	CALENDAR_SECTOR_BUSINESS        None    	-700000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
55462   156010014       China   	Industrial Profit YTD y/y       		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	CNY     	CN      	CALENDAR_SECTOR_BUSINESS        None    	4300000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
72568   276030001       Germany 	Ifo Business Expectations       		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE    EUR     	DE      	CALENDAR_SECTOR_BUSINESS        92000000        89900000        CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
72569   276030002       Germany 	Ifo Current Business Situation  		CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE    EUR     	DE      	CALENDAR_SECTOR_BUSINESS        88800000        88900000        CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
72570   276030003       Germany 	Ifo Business Climate    			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH        EUR     	DE      	CALENDAR_SECTOR_BUSINESS        89900000        89400000        CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
72571   276050007       Germany 	Bbk Executive Board Member Mauderer Speech      CALENDAR_TYPE_EVENT     CALENDAR_IMPORTANCE_MODERATE    EUR     	DE      	CALENDAR_SECTOR_MONEY   	None    	None    	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
78850   250020001       France  	3-Month BTF Auction     			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	EUR     	FR      	CALENDAR_SECTOR_MARKET  	None    	3746000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
78851   250020002       France  	6-Month BTF Auction     			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	EUR     	FR      	CALENDAR_SECTOR_MARKET  	None    	3657000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
78852   250020003       France  	12-Month BTF Auction    			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	EUR     	FR      	CALENDAR_SECTOR_MARKET  	None    	3467000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
84771   76020007        Brazil  	BCB Bank Lending m/m    			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	BRL     	BR      	CALENDAR_SECTOR_MONEY   	400000  	1200000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
84772   76020001        Brazil  	BCB Focus Market Report 			CALENDAR_TYPE_EVENT     CALENDAR_IMPORTANCE_MODERATE    BRL     	BR      	CALENDAR_SECTOR_MONEY   	None    	None    	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
94938   344020004       Hong Kong       Exports y/y     				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	HKD     	HK      	CALENDAR_SECTOR_TRADE   	18100000        4700000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
94939   344020005       Hong Kong       Imports y/y     				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	HKD     	HK      	CALENDAR_SECTOR_TRADE   	15000000        5300000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
94940   344020006       Hong Kong       Trade Balance   				CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE    HKD     	HK      	CALENDAR_SECTOR_TRADE   	-29054000       -45000000       CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
102731  578020001       Norway  	Unemployment Rate       			CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE    NOK     	NO      	CALENDAR_SECTOR_JOBS    	3700000 	4000000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
102732  578020020       Norway  	General Public Domestic Loan Debt y/y   	CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 	NOK     	NO      	CALENDAR_SECTOR_MONEY   	3300000 	3500000 	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_MONTH
147163  840031004       United States   Memorial Day    				CALENDAR_TYPE_HOLIDAY   CALENDAR_IMPORTANCE_NONE        USD     	US      	CALENDAR_SECTOR_HOLIDAYS        None    	None    	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE
162245  826090005       United Kingdom  Spring Bank Holiday     			CALENDAR_TYPE_HOLIDAY   CALENDAR_IMPORTANCE_NONE        GBP     	GB      	CALENDAR_SECTOR_HOLIDAYS        None    	None    	CALENDAR_IMPACT_NA      	CALENDAR_FREQUENCY_NONE

次に、TimeScheduleからそれぞれのスケジュールのDST列を選択します。

ID      DST_UK  		DST_US  		DST_AU  		DST_NONE
18742   2024.05.27 02:00        2024.05.27 02:00        2024.05.27 02:00        2024.05.27 02:00
18746   2024.05.27 14:00        2024.05.27 14:00        2024.05.27 14:00        2024.05.27 14:00
34896   2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00
34897   2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00
34898   2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00
34899   2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00        2024.05.27 07:00
55462   2024.05.27 03:30        2024.05.27 03:30        2024.05.27 03:30        2024.05.27 03:30
72568   2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30
72569   2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30
72570   2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30
72571   2024.05.27 15:30        2024.05.27 15:30        2024.05.27 15:30        2024.05.27 15:30
78850   2024.05.27 14:50        2024.05.27 14:50        2024.05.27 14:50        2024.05.27 14:50
78851   2024.05.27 14:50        2024.05.27 14:50        2024.05.27 14:50        2024.05.27 14:50
78852   2024.05.27 14:50        2024.05.27 14:50        2024.05.27 14:50        2024.05.27 14:50
84771   2024.05.27 13:30        2024.05.27 13:30        2024.05.27 13:30        2024.05.27 13:30
84772   2024.05.27 13:30        2024.05.27 13:30        2024.05.27 13:30        2024.05.27 13:30
94938   2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30
94939   2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30
94940   2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30        2024.05.27 10:30
102731  2024.05.27 08:00        2024.05.27 08:00        2024.05.27 08:00        2024.05.27 08:00
102732  2024.05.27 08:00        2024.05.27 08:00        2024.05.27 08:00        2024.05.27 08:00
147163  2024.05.27 02:00        2024.05.27 02:00        2024.05.27 02:00        2024.05.27 02:00
162245  2024.05.27 02:00        2024.05.27 02:00        2024.05.27 02:00        2024.05.27 02:00

そして、同じIDで2つのテーブルMQL5CalendarTimeScheduleを結合します。

このリストは、RecordテーブルのDateでフィルタされます。

Date
27-05-2024

クエリから結果を取得すると、結果はTimeScheduleのそれぞれのDST時間に基づいて昇順に並べ替えられます。

   string views[] = {"UK","US","AU","NONE"};
   string view_sql = "CREATE VIEW IF NOT EXISTS Calendar_%s "
                     "AS "
                     "SELECT C.Eventid,C.Eventname,C.Country,T.DST_%s as Time,C.EventCurrency,C.Eventcode from MQL5Calendar C,Record R "
                     "Inner join TimeSchedule T on C.ID=T.ID "
                     "Where DATE(REPLACE(T.DST_%s,'.','-'))=R.Date "
                     "Order by T.DST_%s Asc;";

//-- Sql statements for creating the table views
   for(uint i=1;i<=views.Size();i++)
     {
      CalendarContents[i].Content = (CalendarComponents)i;
      CalendarContents[i].name = StringFormat("Calendar_%s",views[i-1]);
      CalendarContents[i].sql = StringFormat(view_sql,views[i-1],views[i-1],views[i-1],views[i-1]);
      CalendarContents[i].tbl_name = StringFormat("Calendar_%s",views[i-1]);
      CalendarContents[i].type = "view";
     }

ビューの1つを見て、クエリ結果が何を生成するかを確認します。

SELECT * FROM 'Calendar_UK';

以下が出力です。

EVENTID 	EVENTNAME       				COUNTRY 	Time    		EVENTCURRENCY   EVENTCODE
999020002       Eurogroup Meeting       			European Union  2024.05.27 02:00        EUR     	EU
840031004       Memorial Day    				United States   2024.05.27 02:00        USD     	US
826090005       Spring Bank Holiday     			United Kingdom  2024.05.27 02:00        GBP     	GB
156010014       Industrial Profit YTD y/y       		China   	2024.05.27 03:30        CNY     	CN
392010004       Coincident Index        			Japan   	2024.05.27 07:00        JPY     	JP
392010005       Leading Index   				Japan   	2024.05.27 07:00        JPY     	JP
392010011       Coincident Index m/m    			Japan   	2024.05.27 07:00        JPY     	JP
392010012       Leading Index m/m       			Japan   	2024.05.27 07:00        JPY     	JP
578020001       Unemployment Rate       			Norway  	2024.05.27 08:00        NOK     	NO
578020020       General Public Domestic Loan Debt y/y   	Norway  	2024.05.27 08:00        NOK     	NO
276030001       Ifo Business Expectations       		Germany 	2024.05.27 10:30        EUR     	DE
276030002       Ifo Current Business Situation  		Germany 	2024.05.27 10:30        EUR     	DE
276030003       Ifo Business Climate    			Germany 	2024.05.27 10:30        EUR     	DE
344020004       Exports y/y     				Hong Kong       2024.05.27 10:30        HKD     	HK
344020005       Imports y/y     				Hong Kong       2024.05.27 10:30        HKD     	HK
344020006       Trade Balance   				Hong Kong       2024.05.27 10:30        HKD     	HK
76020007        BCB Bank Lending m/m    			Brazil  	2024.05.27 13:30        BRL     	BR
76020001        BCB Focus Market Report 			Brazil  	2024.05.27 13:30        BRL     	BR
999010020       ECB Executive Board Member Lane Speech  	European Union  2024.05.27 14:00        EUR     	EU
250020001       3-Month BTF Auction     			France  	2024.05.27 14:50        EUR     	FR
250020002       6-Month BTF Auction     			France  	2024.05.27 14:50        EUR     	FR
250020003       12-Month BTF Auction    			France  	2024.05.27 14:50        EUR     	FR
276050007       Bbk Executive Board Member Mauderer Speech      Germany 	2024.05.27 15:30        EUR     	DE

技術的には、Recordと呼ばれる新しいテーブルがあります。このテーブルは、レコードを1つだけ保存するようになったため、以前のRecordsテーブルを置き換えます。このテーブルのデータ型はTEXTで、列名はDateです。SQLiteのDate関数と混同しないでください。

//-- initializing properties for the Record table
   CalendarContents[5].Content = Record_Table;
   CalendarContents[5].name = "Record";
   CalendarContents[5].sql = "CREATE TABLE Record(Date TEXT NOT NULL)STRICT;";
   CalendarContents[5].tbl_name="Record";
   CalendarContents[5].type = "table";
   CalendarContents[5].insert = "INSERT INTO 'Record'(Date) VALUES (Date(REPLACE('%s','.','-')));";

TimeScheduleテーブルには、すべての個別のイベントの時間データが保存され、外部キー参照「ID」を使用してテーブルをMQL5Calendarテーブルにリンク(関係を作成)します。

//-- initializing properties for the TimeSchedule table
   CalendarContents[6].Content = TimeSchedule_Table;
   CalendarContents[6].name = "TimeSchedule";
   CalendarContents[6].sql = "CREATE TABLE TimeSchedule(ID INT NOT NULL,DST_UK   TEXT   NOT NULL,DST_US   TEXT   NOT NULL,"
                             "DST_AU   TEXT   NOT NULL,DST_NONE   TEXT   NOT NULL,FOREIGN KEY (ID) REFERENCES MQL5Calendar (ID))STRICT;";
   CalendarContents[6].tbl_name="TimeSchedule";
   CalendarContents[6].type = "table";
   CalendarContents[6].insert = "INSERT INTO 'TimeSchedule'(ID,DST_UK,DST_US,DST_AU,DST_NONE) "
                                "VALUES (%d,'%s','%s', '%s', '%s');";

MQL5Calendarテーブルには、「ID」と呼ばれる主キーがあり、これはテーブル内の各ニュースイベントレコードに対して一意になります。

//-- initializing properties for the MQL5Calendar table
   CalendarContents[7].Content = MQL5Calendar_Table;
   CalendarContents[7].name = "MQL5Calendar";
   CalendarContents[7].sql = "CREATE TABLE MQL5Calendar(ID INT NOT NULL,EVENTID  INT   NOT NULL,COUNTRY  TEXT   NOT NULL,"
                             "EVENTNAME   TEXT   NOT NULL,EVENTTYPE   TEXT   NOT NULL,EVENTIMPORTANCE   TEXT   NOT NULL,"
                             "EVENTCURRENCY  TEXT   NOT NULL,EVENTCODE   TEXT   NOT NULL,EVENTSECTOR TEXT   NOT NULL,"
                             "EVENTFORECAST  TEXT   NOT NULL,EVENTPREVALUE  TEXT   NOT NULL,EVENTIMPACT TEXT   NOT NULL,"
                             "EVENTFREQUENCY TEXT   NOT NULL,PRIMARY KEY(ID))STRICT;";
   CalendarContents[7].tbl_name="MQL5Calendar";
   CalendarContents[7].type = "table";
   CalendarContents[7].insert = "INSERT INTO 'MQL5Calendar'(ID,EVENTID,COUNTRY,EVENTNAME,EVENTTYPE,EVENTIMPORTANCE,EVENTCURRENCY,EVENTCODE,"
                                "EVENTSECTOR,EVENTFORECAST,EVENTPREVALUE,EVENTIMPACT,EVENTFREQUENCY) "
                                "VALUES (%d,%d,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s');";

OnlyOne_AutoDSTというトリガーを作成します。このトリガーは、AutoDSTに値が挿入される際に開始され、新しいレコードが挿入される前にAutoDSTからすべてのレコードを削除します。

//-- Sql statement for creating the AutoDST table's trigger
   CalendarContents[8].Content = AutoDST_Trigger;
   CalendarContents[8].name = "OnlyOne_AutoDST";
   CalendarContents[8].sql = "CREATE TRIGGER IF NOT EXISTS OnlyOne_AutoDST "
                             "BEFORE INSERT ON AutoDST "
                             "BEGIN "
                             "Delete from AutoDST; "
                             "END;";
   CalendarContents[8].tbl_name="AutoDST";
   CalendarContents[8].type = "trigger";

OnlyOne_Recordについても同じことが言えますが、このトリガーはRecordテーブルに関連しています。

//-- Sql statement for creating the Record table's trigger
   CalendarContents[9].Content = Record_Trigger;
   CalendarContents[9].name = "OnlyOne_Record";
   CalendarContents[9].sql = "CREATE TRIGGER IF NOT EXISTS OnlyOne_Record "
                             "BEFORE INSERT ON Record "
                             "BEGIN "
                             "Delete from Record; "
                             "END;";
   CalendarContents[9].tbl_name="Record";
   CalendarContents[9].type = "trigger";

ここで、UpdateRecords関数で、カレンダーデータベースを更新する必要があるかどうかを判断します。

この関数に対する前回(第1回)からの変更点は次のとおりです。

1. SQLクエリ「select * from sqlite_master where type<>'index' ;」を使用して、データベース内に存在するインデックスではないすべてのオブジェクトを読み取ります。

2. すべてのオブジェクトの属性を配列DBContentsに保存し、SQL文の最後にセミコロンがない場合はセミコロンを追加します。

3. データベースで見つかったオブジェクトと配列CalendarContentsで初期化したオブジェクトを比較します。CalendarContents sqlから 'IF NOT EXISTS'を削除します。

4. DBContentsとCalendarContentsの間に一致が見つからない場合は、DBcontentsインデックス内のオブジェクトを削除します。

5. SQLオブジェクトの一致がCalendarContentsのサイズと等しくない場合は、更新を実行します。 

bool CNews::UpdateRecords()
  {
//initialize variable to true
   bool perform_update=true;
//--- open/create
//-- try to open database Calendar
   int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE| DATABASE_OPEN_COMMON);
   if(db==INVALID_HANDLE)//Checks if the database was able to be opened
     {
      //if opening the database failed
      if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Checks if the database Calendar exists in the common folder
        {
         return perform_update;//Returns true when the database was failed to be opened and the file doesn't exist in the common folder
        }
     }

   int MasterRequest = DatabasePrepare(db,"select * from sqlite_master where type<>'index';");
   if(MasterRequest==INVALID_HANDLE)
     {
      Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
     }
   else
     {
      SQLiteMaster ReadContents;
      //Assigning values from the sql query into DBContents array
      for(int i=0; DatabaseReadBind(MasterRequest,ReadContents); i++)
        {
         ArrayResize(DBContents,i+1,i+2);
         DBContents[i].type = ReadContents.type;
         DBContents[i].name = ReadContents.name;
         DBContents[i].tbl_name = ReadContents.tbl_name;
         DBContents[i].rootpage = ReadContents.rootpage;
         /*Check if the end of the sql string has a character ';' if not add this character to the string*/
         DBContents[i].sql = (StringFind(ReadContents.sql,";",StringLen(ReadContents.sql)-1)==
                              (StringLen(ReadContents.sql)-1))?ReadContents.sql:ReadContents.sql+";";;
        }

      uint contents_exists = 0;
      for(uint i=0;i<DBContents.Size();i++)
        {
         bool isCalendarContents = false;
         for(uint x=0;x<CalendarContents.Size();x++)
           {
            /*Store Sql query from CalendarContents without string ' IF NOT EXISTS'*/
            string CalendarSql=CalendarContents[x].sql;
            StringReplace(CalendarSql," IF NOT EXISTS","");
            //-- Check if the Db object is in our list
            if(DBContents[i].name==CalendarContents[x].name&&
               (DBContents[i].sql==CalendarSql||
                DBContents[i].sql==CalendarContents[x].sql)&&
               CalendarContents[x].type==DBContents[i].type&&
               CalendarContents[x].tbl_name==DBContents[i].tbl_name)
              {
               contents_exists++;
               isCalendarContents = true;
              }
           }
         if(!isCalendarContents)
           {
            //-- Print DBcontent's name if it does not match with CalendarContents
            PrintFormat("DBContent: %s is not needed!",DBContents[i].name);
            //-- We will drop the table if it is not neccessary
            DatabaseExecute(db,StringFormat(DropRequest,DBContents[i].type,DBContents[i].name));
            Print("Attempting To Clean Database...");
           }
        }
      /*If not all the CalendarContents exist in the Calendar Database before an update */
      if(contents_exists!=CalendarContents.Size())
        {
         return perform_update;
        }
     }
   if(!DatabaseTableExists(db,CalendarStruct(Record_Table).name))//If the database table 'Record' doesn't exist
     {
      DatabaseClose(db);
      return perform_update;
     }

//-- Sql query to determine the lastest or maximum date recorded
   /* If the last recorded date data in the 'Record' table is not equal to the current day, perform an update! */
   string request_text=StringFormat("SELECT Date FROM %s where Date=Date(REPLACE('%s','.','-'))",
                                    CalendarStruct(Record_Table).name,TimeToString(TimeTradeServer()));
   int request=DatabasePrepare(db,request_text);//Creates a handle of a request, which can then be executed using DatabaseRead()
   if(request==INVALID_HANDLE)//Checks if the request failed to be completed
     {
      Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError());
      DatabaseClose(db);
      return perform_update;
     }

   if(DatabaseRead(request))//Will be true if there are results from the sql query/request
     {
      DatabaseFinalize(request);//Removes a request created in DatabasePrepare()
      DatabaseClose(db);//Closes the database
      perform_update=false;
      return perform_update;
     }
   else
     {
      DatabaseFinalize(request);//Removes a request created in DatabasePrepare()
      DatabaseClose(db);//Closes the database
      return perform_update;
     }
  }

CreateCalendarTable関数では、カレンダーデータベースにMQL5Calendarテーブルが既に存在するかどうかを確認し、TimeScheduleテーブルが既に存在するかどうかも確認し、存在する場合は各テーブルを削除しようとします。TimeScheduleにはMQL5Calendarが必要なので、まずTimeScheduleを削除せずにMQL5Calendarを削除することはできません。 

MQL5Calendarが存在しない場合は、そのテーブルを作成します。

bool CNews::CreateCalendarTable(int db,bool &tableExists)
  {
//-- Checks if a table 'MQL5Calendar' exists
   if(DatabaseTableExists(db,CalendarStruct(MQL5Calendar_Table).name))
     {
      tableExists=true;//Assigns true to tableExists variable
      //-- Checks if a table 'TimeSchedule' exists in the database 'Calendar'
      if(DatabaseTableExists(db,CalendarStruct(TimeSchedule_Table).name))
        {
         //-- We will drop the table if the table already exists
         if(!DatabaseExecute(db,StringFormat("Drop Table %s",CalendarStruct(TimeSchedule_Table).name)))
           {
            //If the table failed to be dropped/deleted
            PrintFormat("Failed to drop table %s with code %d",CalendarStruct(TimeSchedule_Table).name,GetLastError());
            DatabaseClose(db);//Close the database
            return false;//will terminate execution of the rest of the code below and return false, when the table cannot be dropped
           }
        }
      //--We will drop the table if the table already exists
      if(!DatabaseExecute(db,StringFormat("Drop Table %s",CalendarStruct(MQL5Calendar_Table).name)))
        {
         //If the table failed to be dropped/deleted
         PrintFormat("Failed to drop table %s with code %d",CalendarStruct(MQL5Calendar_Table).name,GetLastError());
         DatabaseClose(db);//Close the database
         return false;//will terminate execution of the rest of the code below and return false, when the table cannot be dropped
        }
     }
//-- If the database table 'MQL5Calendar' doesn't exist
   if(!DatabaseTableExists(db,CalendarStruct(MQL5Calendar_Table).name))
     {
      //--- create the table 'MQL5Calendar'
      if(!DatabaseExecute(db,CalendarStruct(MQL5Calendar_Table).sql))//Checks if the table was successfully created
        {
         Print("DB: create the Calendar table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return false;//Function returns false if creating the table failed
        }
     }
   return true;//Function returns true if creating the table was successful
  }

CreateTimeTable関数では、Calendarデータベースにテーブルが存在するかどうかを確認し、存在しない場合はテーブルを作成します。 

bool CNews::CreateTimeTable(int db,bool &tableExists)
  {
//-- If the database table 'TimeSchedule' doesn't exist
   if(!DatabaseTableExists(db,CalendarStruct(TimeSchedule_Table).name))
     {
      //--- create the table 'TimeSchedule'
      if(!DatabaseExecute(db,CalendarStruct(TimeSchedule_Table).sql))//Checks if the table was successfully created
        {
         Print("DB: create the Calendar table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return false;//Function returns false if creating the table failed
        }
     }
   return true;//Function returns true if creating the table was successful
  }

CreateCalendarViews関数では、CalendarComponentsを使用してすべてのビューを作成し、各ビューのID(列挙値)を見つけて作成します。

void CNews::CreateCalendarViews(int db)
  {
   for(uint i=1;i<=4;i++)
     {
      if(!DatabaseExecute(db,CalendarStruct((CalendarComponents)i).sql))//Checks if the view was successfully created
        {
         Print("DB: create the Calendar view failed with code ", GetLastError());
        }
     }
  }

InsertIntoTables関数では、Evalues配列のすべてのレコードをそれぞれMQL5CalendarテーブルとTimeScheduleテーブルの両方に挿入します。イベントの日付は、TimeScheduleのさまざまなDSTスケジュールに合わせて調整されます。

bool CNews::InsertIntoTables(int db,Calendar &Evalues[])
  {
   for(uint i=0; i<Evalues.Size(); i++)//Looping through all the Economic Events
     {
      string request_insert_into_calendar =
         StringFormat(CalendarStruct(MQL5Calendar_Table).insert,
                      i,
                      Evalues[i].EventId,
                      Evalues[i].CountryName,
                      Evalues[i].EventName,
                      Evalues[i].EventType,
                      Evalues[i].EventImportance,
                      Evalues[i].EventCurrency,
                      Evalues[i].EventCode,
                      Evalues[i].EventSector,
                      Evalues[i].EventForecast,
                      Evalues[i].EventPreval,
                      Evalues[i].EventImpact,
                      Evalues[i].EventFrequency);//Inserting all the columns for each event record
      if(DatabaseExecute(db,request_insert_into_calendar))//Check if insert query into calendar was successful
        {
         string request_insert_into_time =
            StringFormat(CalendarStruct(TimeSchedule_Table).insert,
                         i,
                         //-- Economic EventDate adjusted for UK DST(Daylight Savings Time)
                         Savings_UK.adjustDaylightSavings(StringToTime(Evalues[i].EventDate)),
                         //-- Economic EventDate adjusted for US DST(Daylight Savings Time)
                         Savings_US.adjustDaylightSavings(StringToTime(Evalues[i].EventDate)),
                         //-- Economic EventDate adjusted for AU DST(Daylight Savings Time)
                         Savings_AU.adjustDaylightSavings(StringToTime(Evalues[i].EventDate)),
                         Evalues[i].EventDate//normal Economic EventDate
                        );//Inserting all the columns for each event record
         if(!DatabaseExecute(db,request_insert_into_time))
           {
            Print(GetLastError());
            //-- Will print the sql query to check for any errors or possible defaults in the query/request
            Print(request_insert_into_time);
            return false;//Will end the loop and return false, as values failed to be inserted into the table
           }
        }
      else
        {
         Print(GetLastError());
         //-- Will print the sql query to check for any errors or possible defaults in the query/request
         Print(request_insert_into_calendar);
         return false;//Will end the loop and return false, as values failed to be inserted into the table
        }
     }
   return true;//Will return true, all values were inserted into the table successfully
  }

CreateRecordTable関数では、レコードテーブルがすでに存在するかどうかを確認し、存在しない場合はテーブルを作成します。レコードテーブルが存在したら、そのトリガーを作成します。次に、現在のサーバーの日付を入力します。


TimeCurrentではなくTimeTradeServerを使用する理由

TimeCurrentを使用すると、現在のチャート銘柄の時間データが取得されます。この時間データは新しいティックごとに更新されるため、銘柄によって時間が異なる場合があります。もし銘柄の取引時間が異なっていたり、現在のチャート銘柄が閉じられている場合、新しいティックが受信されないため、TimeCurrentが実際の日付より1日以上遅れた日付を返す可能性があり、これは潜在的に問題となります。一方、TimeTradeServerは銘柄の種類に関係なく常に更新されます。

void CNews::CreateRecordTable(int db)
  {
   bool failed=false;
   if(!DatabaseTableExists(db,CalendarStruct(Record_Table).name))//Checks if the table 'Record' exists in the databse 'Calendar'
     {
      //--- create the table
      if(!DatabaseExecute(db,CalendarStruct(Record_Table).sql))//Will attempt to create the table 'Record'
        {
         Print("DB: create the Records table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return;//Exits the function if creating the table failed
        }
      else//If Table was created Successfully then Create Trigger
        {
         DatabaseExecute(db,CalendarStruct(Record_Trigger).sql);
        }
     }
   else
     {
      DatabaseExecute(db,CalendarStruct(Record_Trigger).sql);
     }
//Sql query/request to insert the current time into the 'Date' column in the table 'Record'
   string request_text=StringFormat(CalendarStruct(Record_Table).insert,TimeToString(TimeTradeServer()));
   if(!DatabaseExecute(db, request_text))//Will attempt to run this sql request/query
     {
      Print(GetLastError());
      PrintFormat(CalendarStruct(Record_Table).insert,TimeToString(TimeTradeServer()));
      failed=true;//assign true if the request failed
     }
   if(failed)
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(db);
      PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
     }
  }
CreateAutoDST関数では、ブローカーのDSTスケジュールを確認し、DSTスケジュールが正常に取得できた場合、カレンダーデータベースにAutoDSTテーブルが存在するかどうかをチェックします。AutoDSTテーブルが存在しない場合は新たに作成されます。AutoDSTテーブルが既に存在する場合、トリガーが作成され、列挙から文字列に変換されたDSTスケジュールが挿入されます。 
void CNews::CreateAutoDST(int db)
  {
   bool failed=false;//boolean variable
   if(!AutoDetectDST(DSTType))//Check if AutoDetectDST went through all the right procedures
     {
      return;//will terminate execution of the rest of the code below
     }

   if(!DatabaseTableExists(db,CalendarStruct(AutoDST_Table).name))//Checks if the table 'AutoDST' exists in the databse 'Calendar'
     {
      //--- create the table AutoDST
      if(!DatabaseExecute(db,CalendarStruct(AutoDST_Table).sql))//Will attempt to create the table 'AutoDST'
        {
         Print("DB: create the AutoDST table failed with code ", GetLastError());
         DatabaseClose(db);//Close the database
         return;//Exits the function if creating the table failed
        }
      else//If Table was created Successfully then Create Trigger
        {
         DatabaseExecute(db,CalendarStruct(AutoDST_Trigger).sql);
        }
     }
   else
     {
      //Create trigger if AutoDST table exists
      DatabaseExecute(db,CalendarStruct(AutoDST_Trigger).sql);
     }
//Sql query/request to insert the recommend DST for the Broker using the DSTType variable to determine which string data to insert
   string request_text=StringFormat(CalendarStruct(AutoDST_Table).insert,EnumToString(DSTType));
   if(!DatabaseExecute(db, request_text))//Will attempt to run this sql request/query
     {
      Print(GetLastError());
      PrintFormat(CalendarStruct(AutoDST_Table).insert,EnumToString(DSTType));//Will print the sql query if failed
      failed=true;//assign true if the request failed
     }
   if(failed)
     {
      //--- roll back all transactions and unlock the database
      DatabaseTransactionRollback(db);
      PrintFormat("%s: DatabaseExecute() failed with code %d", __FUNCTION__, GetLastError());
     }
  }


リスク管理クラス

リスク管理は取引を成功させる上で重要な要素です。 リスク管理の主な目的は、取引資本を保護することです。資本がなければ、トレーダーは取引を続けることができません。損失を制限する戦略を実行することで、トレーダーは市場に長く留まることができ、挫折から立ち直り、全体的な収益性を達成する機会が増えます。この場合、ユーザーにさまざまなリスクプロファイルを提供し、その中から選択して最適なオプションを見つけられるようにします。

免責条項:ここでは、ロット、ロットサイズ、取引量を同じ意味で参照します。リスク管理の観点からは、これらを同様に考えてください。 


リスクプロファイルのリスト

  • 最小ロットサイズ
  • 最大ロットサイズ
  • 残高の割合
  • 余剰証拠金の割合
  • 残高当たりのリスク額
  • 余剰証拠金あたりのリスク額
  • 残高ごとのロットサイズ
  • 余剰証拠金あたりのロットサイズ
  • カスタムロットサイズ
  • リスクの割合

最小ロットサイズ

このリスクオプションでは、現在の銘柄に対して許可されている最小ロットサイズを使用します。

最大ロットサイズ

このリスクオプションでは、現在の銘柄に許可されている最大ロットサイズを使用します。

残高の割合

このリスクオプションでは、まずリスクの量を取得します。

amount_of_risk = Balance*Percent;

Percent = 5%とし、口座残高は10,000です。

amount_of_risk = 10000*(5/100);
amount_of_risk = 500;

最小ロットサイズを使用する場合、特定の取引の最小リスクを計算するには、始値と終値が必要になります。

OrderCalcProfit(ORDER_TYPE,Symbol(),Minimum_lotsize,OpenPrice,ClosePrice,Minimum_risk);

Minimum_riskが返されたら、次の式を使用してamount_of_riskに必要なロットサイズを取得します。

required_lotsize = (amount_of_risk/Minimum_risk)*Minimum_lotsize;

Minimum_risk = 100、Minimum_lotsize = 0.1とします。

required_lotsize = (500/100)*0.1;
required_lotsize = 5*0.1;
required_lotsize = 0.5;


余剰証拠金の割合

このリスクオプションは、残高のパーセンテージに似ています。しかし、このリスクオプションの利点は、トレーダー口座に取引がある場合に発揮されます。

一方、残高が同じであれば、未決済取引に関係なく、残高の割合でリスクは同じままです。余剰証拠金の割合により、オープントレードの利益が変動するにつれてリスクも変化します。これにより、現在の口座の状態に対するリスク計算がより正確になります。


残高当たりのリスク額

このリスクオプションでは、まずとは、リスク額(除数)と残高(配当)の間の商です

risk = Balance/Risk_in_Amount;

次に、Balance = 10,000、Risk_in_Amount = 800とします。

この場合、基本的にはトレーダーの口座残高10,000ドルごとに1回の取引につき800ドルのリスクを負うことになります。

risk = 10000/800;
risk = 12.5;

次に、リスクを実際の口座残高で割ってリスク額を算出します。

amount_of_risk = AccountBalance/risk;

AccountBalance = 5000とします。

amount_of_risk = 5000/12.5;
amount_of_risk = 400;

これで、トレーダーがこの特定の取引に400ドルのリスクを負うことを望んでいることがわかります。


余剰証拠金あたりのリスク額

このリスクオプションは、残高あたりの金額のリスクに似ていますが、別の例を見てみましょう。

risk = FreeMargin/Risk_in_Amount;

FreeMargin = 150、Risk_in_Amount = 1とします。

この場合、FreeMarginの150ドルごとに1ドルのリスクを負うことになります。

risk = 150/1;
risk = 150;

amount_of_risk = AccountFreeMargin/risk;

//-- Let AccountFreeMargin = 750

amount_of_risk = 750/150;
amount_of_risk = 5;

リスク額を取得した後、5ドルのリスクに対応するために、その特定の取引に必要なロットサイズを計算します。


残高あたりのロットサイズ

このリスクオプションでは、トレーダーは特定の口座残高に対してリスクを負いたいロットサイズを指定します。

required_lotsize = (AccountBalance/Balance)*lotsize;

AccountBalanceはトレーダーの実際の口座残高、Balanceとlotsizeはトレーダーが提供する入力値です。

AccountBalance = 10,000、Balance = 350、lotsize = 0.01とします。

この場合、トレーダーは口座残高350ドルごとに0.01ロットのリスクを負いたいと考えています。 

required_lotsize = (10000/350)*0.01;
required_lotsize = 0.285;

required_lotsizeは0.285ですが、実際の値はもっと長くなります。取引を開始したい特定の銘柄の取引量ステップが0.01であると仮定します。取引量ステップが0.01の場合に0.285ロットサイズで取引を開こうとすると、エラーが発生します。

これを防ぐために、ロットサイズを正規化し、基本的には取引量ステップに従ってロットサイズをフォーマットします。

required_lotsize = Volume_Step*MathFloor(0.285/Volume_Step);
requred_lotsize = 0.01*MathFloor(0.285/0.01);
required_lotsize = 0.01*MathFloor(28.5);
required_lotsize = 0.01*28;
required_lotsize = 0.28;


余剰証拠金あたりのロットサイズ

このリスクオプションは残高ごとのロットサイズに似ていますが、別の例を示します。

required_lotsize = (AccountFreeMargin/FreeMargin)*lotsize;

AccountFreeMargin = 134,560、FreeMargin = 1622、lot-size = 0.0056とします。

この場合、次のようになります。

required_lotsize = (134560/1622)*0.0056;
required_lotsize = 0.464;

取引量ステップが0.02であると仮定します。

Volumeステップに応じてrequired_lotsizeを正規化します。

//-- normalize for Volume Step
required_lotsize = Volume_Step*MathFloor(0.464/Volume_Step);
requred_lotsize = 0.02*MathFloor(0.464/0.02);
required_lotsize = 0.02*MathFloor(23.2);
required_lotsize = 0.02*23;
required_lotsize = 0.46;


カスタムロットサイズ

このリスクオプションでは、トレーダーが提供する入力ロットサイズを利用します。


リスクの割合

このリスクオプションでは、特定の銘柄の余剰証拠金と証拠金要件を使用して、現在の銘柄の最大許容リスクのパーセンテージを計算します。

CRiskManagementクラスには、次のクラスからのマルチレベル継承があります。

  • CSymbolProperties
  • CChartProperties

CRiskManagementクラスには、CAccountInfoクラスからのインクルードがあります。

CRiskManagementクラスには、次のクラスからの階層的継承があります。

  • CSymbolProperties
  • CSymbolInfo
//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+
#include "ChartProperties.mqh"
#include <Trade/AccountInfo.mqh>
CAccountInfo      Account;

//-- Enumeration declaration for Risk options
enum RiskOptions
  {
   MINIMUM_LOT,//MINIMUM LOTSIZE
   MAXIMUM_LOT,//MAXIMUM LOTSIZE
   PERCENTAGE_OF_BALANCE,//PERCENTAGE OF BALANCE
   PERCENTAGE_OF_FREEMARGIN,//PERCENTAGE OF FREE-MARGIN
   AMOUNT_PER_BALANCE,//AMOUNT PER BALANCE
   AMOUNT_PER_FREEMARGIN,//AMOUNT PER FREE-MARGIN
   LOTSIZE_PER_BALANCE,//LOTSIZE PER BALANCE
   LOTSIZE_PER_FREEMARGIN,//LOTSIZE PER FREE-MARGIN
   CUSTOM_LOT,//CUSTOM LOTSIZE
   PERCENTAGE_OF_MAXRISK//PERCENTAGE OF MAX-RISK
  } RiskProfileOption;//variable for Risk options

//-- Enumeration declaration for Risk floor
enum RiskFloor
  {
   RiskFloorMin,//MINIMUM LOTSIZE
   RiskFloorMax,//MAX-RISK
   RiskFloorNone//NONE
  } RiskFloorOption;//variable for Risk floor

//-- Enumeration declaration for Risk ceiling(Maximum allowable risk in terms of lot-size)
enum RiskCeil
  {
   RiskCeilMax,//MAX LOTSIZE
   RiskCeilMax2,//MAX LOTSIZE(x2)
   RiskCeilMax3,//MAX LOTSIZE(x3)
   RiskCeilMax4,//MAX LOTSIZE(x4)
   RiskCeilMax5,//MAX LOTSIZE(x5)
  } RiskCeilOption;//variable for Risk ceiling

//-- Structure declaration for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)
struct RISK_AMOUNT
  {
   double            RiskAmountBoF;//store Balance or Free-Margin
   double            RiskAmount;//store risk amount
  } Risk_Profile_2;//variable for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)

//-- Structure declaration for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)
struct RISK_LOT
  {
   double            RiskLotBoF;//store Balance or Free-Margin
   double            RiskLot;//store lot-size
  } Risk_Profile_3;//variable for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)


double            RiskFloorPercentage;//variable for RiskFloorMax
double            Risk_Profile_1;//variable for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)
double            Risk_Profile_4;//variable for Risk option (CUSTOM LOTSIZE)
double            Risk_Profile_5;//variable for Risk option (PERCENTAGE OF MAX-RISK)

//+------------------------------------------------------------------+
//|RiskManagement class                                              |
//+------------------------------------------------------------------+
class CRiskManagement : public CChartProperties
  {

private:
   double            Medium;//variable to store actual Account (Balance or Free-Margin)
   double            RiskAmount,MinimumAmount;
   double            Lots;//variable to store Lot-size to open trade
   const double      max_percent;//variable to store percentage for Maximum risk

   //-- enumeration for dealing with account balance/free-margin
   enum RiskMedium
     {
      BALANCE,
      MARGIN
     };

   //-- calculations for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)
   double              RiskProfile1(const RiskMedium R_Medium);
   //-- calculations for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)
   double              RiskProfile2(const RiskMedium R_Medium);
   //-- calculations for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)
   double              RiskProfile3(const RiskMedium R_Medium);
   //-- calculations for Maximum allowable Risk
   double              MaxRisk(const double percent);
   //-- Store Trade's Open-price
   double              OpenPrice;
   //-- Store Trade's Close-price
   double              ClosePrice;
   //-- Store Ordertype between (ORDER_TYPE_BUY or ORDER_TYPE_SELL) for risk calaculations
   ENUM_ORDER_TYPE     ORDERTYPE;
   //-- Set Medium variable value
   void                SetMedium(const RiskMedium R_Medium) {Medium = (R_Medium==BALANCE)?Account.Balance():Account.FreeMargin();}
   //-- Get Minimum Risk for a Trade using Minimum Lot-size
   bool                GetMinimumRisk()
     {
      return OrderCalcProfit(ORDERTYPE,Symbol(),LotsMin(),OpenPrice,ClosePrice,MinimumAmount);
     }
   //-- Retrieve Risk amount based on Risk inputs
   double            GetRisk(double Amount)
     {
      if(!GetMinimumRisk()||Amount==0)
         return 0.0;
      return ((Amount/MinimumAmount)*LotsMin());
     }

protected:
   //-- Application of Lot-size limits
   void              ValidateLotsize(double &Lotsize);
   //-- Set ORDERTYPE variable to (ORDER_TYPE_BUY or ORDER_TYPE_SELL) respectively
   void              SetOrderType(ENUM_ORDER_TYPE Type)
     {
      if(Type==ORDER_TYPE_BUY||Type==ORDER_TYPE_BUY_LIMIT||Type==ORDER_TYPE_BUY_STOP)
        {
         ORDERTYPE = ORDER_TYPE_BUY;
        }
      else
         if(Type==ORDER_TYPE_SELL||Type==ORDER_TYPE_SELL_LIMIT||Type==ORDER_TYPE_SELL_STOP)
           {
            ORDERTYPE = ORDER_TYPE_SELL;
           }
     }

public:

                     CRiskManagement();//Class's constructor
   //-- Retrieve user's Risk option
   string            GetRiskOption()
     {
      switch(RiskProfileOption)
        {
         case  MINIMUM_LOT://MINIMUM LOTSIZE - Risk Option
            return "MINIMUM LOTSIZE";
            break;
         case MAXIMUM_LOT://MAXIMUM LOTSIZE - Risk Option
            return "MAXIMUM LOTSIZE";
            break;
         case PERCENTAGE_OF_BALANCE://PERCENTAGE OF BALANCE - Risk Option
            return "PERCENTAGE OF BALANCE";
            break;
         case PERCENTAGE_OF_FREEMARGIN://PERCENTAGE OF FREE-MARGIN - Risk Option
            return "PERCENTAGE OF FREE-MARGIN";
            break;
         case AMOUNT_PER_BALANCE://AMOUNT PER BALANCE - Risk Option
            return "AMOUNT PER BALANCE";
            break;
         case AMOUNT_PER_FREEMARGIN://AMOUNT PER FREE-MARGIN - Risk Option
            return "AMOUNT PER FREE-MARGIN";
            break;
         case LOTSIZE_PER_BALANCE://LOTSIZE PER BALANCE - Risk Option
            return "LOTSIZE PER BALANCE";
            break;
         case LOTSIZE_PER_FREEMARGIN://LOTSIZE PER FREE-MARGIN - Risk Option
            return "LOTSIZE PER FREE-MARGIN";
            break;
         case CUSTOM_LOT://CUSTOM LOTSIZE - Risk Option
            return "CUSTOM LOTSIZE";
            break;
         case PERCENTAGE_OF_MAXRISK://PERCENTAGE OF MAX-RISK - Risk Option
            return "PERCENTAGE OF MAX-RISK";
            break;
         default:
            return "";
            break;
        }
     }
   //-- Retrieve user's Risk Floor Option
   string            GetRiskFloor()
     {
      switch(RiskFloorOption)
        {
         case RiskFloorMin://MINIMUM LOTSIZE for Risk floor options
            return "MINIMUM LOTSIZE";
            break;
         case RiskFloorMax://MAX-RISK for Risk floor options
            return "MAX-RISK";
            break;
         case RiskFloorNone://NONE for Risk floor options
            return "NONE";
            break;
         default:
            return "";
            break;
        }
     }
   //-- Retrieve user's Risk Ceiling option
   string            GetRiskCeil()
     {
      switch(RiskCeilOption)
        {
         case  RiskCeilMax://MAX LOTSIZE for Risk ceiling options
            return "MAX LOTSIZE";
            break;
         case RiskCeilMax2://MAX LOTSIZE(x2) for Risk ceiling options
            return "MAX LOTSIZE(x2)";
            break;
         case RiskCeilMax3://MAX LOTSIZE(x3) for Risk ceiling options
            return "MAX LOTSIZE(x3)";
            break;
         case RiskCeilMax4://MAX LOTSIZE(x4) for Risk ceiling options
            return "MAX LOTSIZE(x4)";
            break;
         case RiskCeilMax5://MAX LOTSIZE(x5) for Risk ceiling options
            return "MAX LOTSIZE(x5)";
            break;
         default:
            return "";
            break;
        }
     }

   double            Volume();//Get risk in Volume
   //Apply fixes to lot-size where applicable
   void              NormalizeLotsize(double &Lotsize);
  };

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
//Initialize values
CRiskManagement::CRiskManagement(void):Lots(0.0),max_percent(100),
   ORDERTYPE(ORDER_TYPE_BUY),OpenPrice(Ask()),
   ClosePrice(NormalizePrice(Ask()+Ask()*0.01))

  {
  }

//+------------------------------------------------------------------+
//|Get risk in Volume                                                |
//+------------------------------------------------------------------+
double CRiskManagement::Volume()
  {
   switch(RiskProfileOption)
     {
      case  MINIMUM_LOT://MINIMUM LOTSIZE - Risk Option
         return LotsMin();
         break;
      case MAXIMUM_LOT://MAXIMUM LOTSIZE - Risk Option
         Lots = LotsMax();
         break;
      case PERCENTAGE_OF_BALANCE://PERCENTAGE OF BALANCE - Risk Option
         Lots = RiskProfile1(BALANCE);
         break;
      case PERCENTAGE_OF_FREEMARGIN://PERCENTAGE OF FREE-MARGIN - Risk Option
         Lots = RiskProfile1(MARGIN);
         break;
      case AMOUNT_PER_BALANCE://AMOUNT PER BALANCE - Risk Option
         Lots = RiskProfile2(BALANCE);
         break;
      case AMOUNT_PER_FREEMARGIN://AMOUNT PER FREE-MARGIN - Risk Option
         Lots = RiskProfile2(MARGIN);
         break;
      case LOTSIZE_PER_BALANCE://LOTSIZE PER BALANCE - Risk Option
         Lots =  RiskProfile3(BALANCE);
         break;
      case LOTSIZE_PER_FREEMARGIN://LOTSIZE PER FREE-MARGIN - Risk Option
         Lots = RiskProfile3(MARGIN);
         break;
      case CUSTOM_LOT://CUSTOM LOTSIZE - Risk Option
         Lots = Risk_Profile_4;
         break;
      case PERCENTAGE_OF_MAXRISK://PERCENTAGE OF MAX-RISK - Risk Option
         Lots = MaxRisk(Risk_Profile_5);
         break;
      default:
         Lots = 0.0;
         break;
     }
   ValidateLotsize(Lots);//Check/Adjust Lotsize Limits
   NormalizeLotsize(Lots);//Normalize Lotsize
   return Lots;
  }

//+------------------------------------------------------------------+
//|calculations for Risk options                                     |
//|(PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)             |
//+------------------------------------------------------------------+
//-- calculations for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)
double CRiskManagement::RiskProfile1(const RiskMedium R_Medium)
  {
   SetMedium(R_Medium);
   RiskAmount = Medium*(Risk_Profile_1/100);
   return GetRisk(RiskAmount);
  }

//+------------------------------------------------------------------+
//|calculations for Risk options                                     |
//|(AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)                   |
//+------------------------------------------------------------------+
//-- calculations for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)
double CRiskManagement::RiskProfile2(const RiskMedium R_Medium)
  {
   SetMedium(R_Medium);
   double risk = (Risk_Profile_2.RiskAmountBoF/Risk_Profile_2.RiskAmount);
   risk = (risk<1)?1:risk;

   if(Medium<=0)
      return 0.0;

   RiskAmount = Medium/risk;
   return GetRisk(RiskAmount);
  }

//+------------------------------------------------------------------+
//|calculations for Risk options                                     |
//|(LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)                 |
//+------------------------------------------------------------------+
//-- calculations for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)
double CRiskManagement::RiskProfile3(const RiskMedium R_Medium)
  {
   SetMedium(R_Medium);
   return (Medium>0)?((Medium/Risk_Profile_3.RiskLotBoF)*Risk_Profile_3.RiskLot):0.0;
  }

//+------------------------------------------------------------------+
//|calculations for Maximum allowable Risk                           |
//+------------------------------------------------------------------+
//-- calculations for Maximum allowable Risk
double CRiskManagement::MaxRisk(const double percent)
  {
   double margin=0.0,max_risk=0.0;
//--- checks
   if(percent<0.01 || percent>100)
     {
      Print(__FUNCTION__," invalid parameters");
      return(0.0);
     }
//--- calculate margin requirements for 1 lot
   if(!OrderCalcMargin(ORDERTYPE,Symbol(),1.0,OpenPrice,margin) || margin<0.0)
     {
      Print(__FUNCTION__," margin calculation failed");
      return(0.0);
     }
//--- calculate maximum volume
   max_risk=Account.FreeMargin()*(percent/100.0)/margin;
//--- return volume
   return(max_risk);
  }

//+------------------------------------------------------------------+
//|Apply fixes to lot-size where applicable                          |
//+------------------------------------------------------------------+
void CRiskManagement::NormalizeLotsize(double &Lotsize)
  {
   if(Lotsize<=0.0)
      return;

//-- Check if the is a Volume limit for the current Symbol
   if(LotsLimit()>0.0)
     {
      if((Lots+PositionsVolume()+OrdersVolume())>LotsLimit())
        {
         //-- calculation of available lotsize remaining
         double remaining_avail_lots = (LotsLimit()-(PositionsVolume()+OrdersVolume()));
         if(remaining_avail_lots>=LotsMin())
           {
            if(RiskFloorOption==RiskFloorMin)//Check if Risk floor option is MINIMUM LOTSIZE
              {
               Print("Warning: Volume Limit Reached, minimum Lotsize selected.");
               Lotsize = LotsMin();
              }
            else
               if(RiskFloorOption==RiskFloorMax)//Check if Risk floor option is MAX-RISK
                 {
                  Print("Warning: Volume Limit Reached, Lotsize Reduced.");
                  Lotsize = ((remaining_avail_lots*(RiskFloorPercentage/100))>LotsMin())?
                            (remaining_avail_lots*(RiskFloorPercentage/100)):LotsMin();
                 }
           }
         else
           {
            Print("Volume Limit Reached!");
            Lotsize=0.0;
            return;
           }
        }
     }

//Check if there is a valid Volume Step for the current Symbol
   if(LotsStep()>0.0)
      Lotsize=LotsStep()*MathFloor(Lotsize/LotsStep());
  }

//+------------------------------------------------------------------+
//|Application of Lot-size limits                                    |
//+------------------------------------------------------------------+
void CRiskManagement::ValidateLotsize(double &Lotsize)
  {
   switch(RiskFloorOption)
     {
      case RiskFloorMin://MINIMUM LOTSIZE for Risk floor options
         //-- Check if lot-size is not less than Minimum lot or more than maximum allowable risk
         if(Lotsize<LotsMin()||Lotsize>MaxRisk(max_percent))
           {
            Lotsize=LotsMin();
           }
         break;
      case RiskFloorMax://MAX-RISK for Risk floor options
         //-- Check if lot-size is more the maximum allowable risk
         if(Lotsize>MaxRisk(max_percent))
           {
            Lotsize=(MaxRisk(RiskFloorPercentage)>LotsMin())?MaxRisk(RiskFloorPercentage):LotsMin();
           }
         else
            if(Lotsize<LotsMin())//Check if lot-size is less than Minimum lot
              {
               Lotsize=LotsMin();
              }
         break;
      case RiskFloorNone://NONE for Risk floor options
         //Check if lot-size is less than Minimum lot
         if(Lotsize<LotsMin())
           {
            Lotsize=0.0;
           }
         break;
      default:
         Lotsize=0.0;
         break;
     }

   switch(RiskCeilOption)
     {
      case  RiskCeilMax://MAX LOTSIZE for Risk ceiling options
         //Check if lot-size is more than Maximum lot
         if(Lotsize>LotsMax())
            Lotsize=LotsMax();
         break;
      case RiskCeilMax2://MAX LOTSIZE(x2) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times two
         if(Lotsize>(LotsMax()*2))
            Lotsize=(LotsMax()*2);
         break;
      case RiskCeilMax3://MAX LOTSIZE(x3) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times three
         if(Lotsize>(LotsMax()*3))
            Lotsize=(LotsMax()*3);
         break;
      case RiskCeilMax4://MAX LOTSIZE(x4) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times four
         if(Lotsize>(LotsMax()*4))
            Lotsize=(LotsMax()*4);
         break;
      case RiskCeilMax5://MAX LOTSIZE(x5) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times five
         if(Lotsize>(LotsMax()*5))
            Lotsize=(LotsMax()*5);
         break;
      default:
         break;
     }
  }
//+------------------------------------------------------------------+

RiskOptions列挙型の変数RiskProfileOptionには、EAへの入力となるユーザー/トレーダーのリスクプロファイルオプションが格納されます。  

//-- Enumeration declaration for Risk options
enum RiskOptions
  {
   MINIMUM_LOT,//MINIMUM LOTSIZE
   MAXIMUM_LOT,//MAXIMUM LOTSIZE
   PERCENTAGE_OF_BALANCE,//PERCENTAGE OF BALANCE
   PERCENTAGE_OF_FREEMARGIN,//PERCENTAGE OF FREE-MARGIN
   AMOUNT_PER_BALANCE,//AMOUNT PER BALANCE
   AMOUNT_PER_FREEMARGIN,//AMOUNT PER FREE-MARGIN
   LOTSIZE_PER_BALANCE,//LOTSIZE PER BALANCE
   LOTSIZE_PER_FREEMARGIN,//LOTSIZE PER FREE-MARGIN
   CUSTOM_LOT,//CUSTOM LOTSIZE
   PERCENTAGE_OF_MAXRISK//PERCENTAGE OF MAX-RISK
  } RiskProfileOption;//variable for Risk options

RiskFloor列挙型の変数RiskFloorOptionには、EAの入力となるユーザー/トレーダーの最小リスク設定が格納されます。

//-- Enumeration declaration for Risk floor
enum RiskFloor
  {
   RiskFloorMin,//MINIMUM LOTSIZE
   RiskFloorMax,//MAX-RISK
   RiskFloorNone//NONE
  } RiskFloorOption;//variable for Risk floor

列挙型RiskCeilの変数RiskCeilOptionには、EAへの入力となるユーザー/トレーダーの最大リスク設定が格納されます。

//-- Enumeration declaration for Risk ceiling(Maximum allowable risk in terms of lot-size)
enum RiskCeil
  {
   RiskCeilMax,//MAX LOTSIZE
   RiskCeilMax2,//MAX LOTSIZE(x2)
   RiskCeilMax3,//MAX LOTSIZE(x3)
   RiskCeilMax4,//MAX LOTSIZE(x4)
   RiskCeilMax5,//MAX LOTSIZE(x5)
  } RiskCeilOption;//variable for Risk ceiling

ユーザー/トレーダーの(口座残高または余剰証拠金)は、double型変数RiskAmountBoFに格納され、double型変数RiskAmountにはリスク額の値が格納されます。Risk_Profile_2は、リスクプロファイルのAMOUNT PER BALANCEプロパティとAMOUNT PER FREE-MARGINプロパティを保存するために使用されます。

//-- Structure declaration for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)
struct RISK_AMOUNT
  {
   double            RiskAmountBoF;//store Balance or Free-Margin
   double            RiskAmount;//store risk amount
  } Risk_Profile_2;//variable for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)

RISK_LOT構造体型の変数Risk_Profile_3には、リスクプロファイルのLOTSIZE PER BALANCE プロパティとLOTSIZE PER FREE-MARGINプロパティが格納されます。

//-- Structure declaration for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)
struct RISK_LOT
  {
   double            RiskLotBoF;//store Balance or Free-Margin
   double            RiskLot;//store lot-size
  } Risk_Profile_3;//variable for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)

変数RiskFloorPercentageには、RiskfloorオプションRiskFloorMaxの最大リスクのパーセンテージが格納されます。

double            RiskFloorPercentage;//variable for RiskFloorMax

変数Risk_Profile_1には、リスクオプションPERCENTAGE OF BALANCEまたはPERCENTAGE OF FREE-MARGINのリスクパーセンテージが格納されます。

double            Risk_Profile_1;//variable for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)

変数Risk_Profile_4には、リスクオプションCUSTOM LOTSIZEのカスタムロットサイズが格納されます。

double            Risk_Profile_4;//variable for Risk option (CUSTOM LOTSIZE)

変数Risk_Profile_5には、リスクオプションPERCENTAGE OF MAX-RISKの最大リスクのパーセンテージが格納されます。

double            Risk_Profile_5;//variable for Risk option (PERCENTAGE OF MAX-RISK)

GetRiskOption関数では、ユーザー/トレーダーのリスクオプションを文字列データ型で取得します。

   //-- Retrieve user's Risk option
   string            GetRiskOption()
     {
      switch(RiskProfileOption)
        {
         case  MINIMUM_LOT://MINIMUM LOTSIZE - Risk Option
            return "MINIMUM LOTSIZE";
            break;
         case MAXIMUM_LOT://MAXIMUM LOTSIZE - Risk Option
            return "MAXIMUM LOTSIZE";
            break;
         case PERCENTAGE_OF_BALANCE://PERCENTAGE OF BALANCE - Risk Option
            return "PERCENTAGE OF BALANCE";
            break;
         case PERCENTAGE_OF_FREEMARGIN://PERCENTAGE OF FREE-MARGIN - Risk Option
            return "PERCENTAGE OF FREE-MARGIN";
            break;
         case AMOUNT_PER_BALANCE://AMOUNT PER BALANCE - Risk Option
            return "AMOUNT PER BALANCE";
            break;
         case AMOUNT_PER_FREEMARGIN://AMOUNT PER FREE-MARGIN - Risk Option
            return "AMOUNT PER FREE-MARGIN";
            break;
         case LOTSIZE_PER_BALANCE://LOTSIZE PER BALANCE - Risk Option
            return "LOTSIZE PER BALANCE";
            break;
         case LOTSIZE_PER_FREEMARGIN://LOTSIZE PER FREE-MARGIN - Risk Option
            return "LOTSIZE PER FREE-MARGIN";
            break;
         case CUSTOM_LOT://CUSTOM LOTSIZE - Risk Option
            return "CUSTOM LOTSIZE";
            break;
         case PERCENTAGE_OF_MAXRISK://PERCENTAGE OF MAX-RISK - Risk Option
            return "PERCENTAGE OF MAX-RISK";
            break;
         default:
            return "";
            break;
        }
     }

GetRiskFloor関数では、ユーザー/トレーダーのリスク下限オプションを文字列データ型で取得します。

   //-- Retrieve user's Risk Floor Option
   string            GetRiskFloor()
     {
      switch(RiskFloorOption)
        {
         case RiskFloorMin://MINIMUM LOTSIZE for Risk floor options
            return "MINIMUM LOTSIZE";
            break;
         case RiskFloorMax://MAX-RISK for Risk floor options
            return "MAX-RISK";
            break;
         case RiskFloorNone://NONE for Risk floor options
            return "NONE";
            break;
         default:
            return "";
            break;
        }
     }

GetRiskCeil関数では、ユーザー/トレーダーのリスク上限オプションを文字列データ型で取得します。

   //-- Retrieve user's Risk Ceiling option
   string            GetRiskCeil()
     {
      switch(RiskCeilOption)
        {
         case  RiskCeilMax://MAX LOTSIZE for Risk ceiling options
            return "MAX LOTSIZE";
            break;
         case RiskCeilMax2://MAX LOTSIZE(x2) for Risk ceiling options
            return "MAX LOTSIZE(x2)";
            break;
         case RiskCeilMax3://MAX LOTSIZE(x3) for Risk ceiling options
            return "MAX LOTSIZE(x3)";
            break;
         case RiskCeilMax4://MAX LOTSIZE(x4) for Risk ceiling options
            return "MAX LOTSIZE(x4)";
            break;
         case RiskCeilMax5://MAX LOTSIZE(x5) for Risk ceiling options
            return "MAX LOTSIZE(x5)";
            break;
         default:
            return "";
            break;
        }
     }

リスク管理クラスのコンストラクタでは、以前に宣言した変数をデフォルト値で初期化します。変数ORDERTYPEのデフォルト値はORDER_TYPE_BUYです。したがって、リスクを計算するために注文タイプを必要とするリスクオプションの場合、注文タイプはこの変数によって設定され、取引開始のリスク計算をシミュレートするために使用されます。デフォルトの始値は変数OpenPriceに格納され、ORDERTYPEの銘柄のAsk価格になります。デフォルトの終値は、変数ClosePriceに格納されている売値からの1%の価格偏差になります。

//Initialize values
CRiskManagement::CRiskManagement(void):Lots(0.0),max_percent(100),
   ORDERTYPE(ORDER_TYPE_BUY),OpenPrice(Ask()),
   ClosePrice(NormalizePrice(Ask()+Ask()*0.01))

  {
  }

Volume関数では、ユーザー/トレーダーのリスクプロファイルオプションのロットサイズを取得し、ユーザー/トレーダーが選択したリスク上限オプションとリスク下限オプションに従って、選択したリスクオプションからロットサイズを調整します。

その後、ロットサイズが正規化され、特定のロットサイズで実際の取引を開始できるようになります。

double CRiskManagement::Volume()
  {
   switch(RiskProfileOption)
     {
      case  MINIMUM_LOT://MINIMUM LOTSIZE - Risk Option
         return LotsMin();
         break;
      case MAXIMUM_LOT://MAXIMUM LOTSIZE - Risk Option
         Lots = LotsMax();
         break;
      case PERCENTAGE_OF_BALANCE://PERCENTAGE OF BALANCE - Risk Option
         Lots = RiskProfile1(BALANCE);
         break;
      case PERCENTAGE_OF_FREEMARGIN://PERCENTAGE OF FREE-MARGIN - Risk Option
         Lots = RiskProfile1(MARGIN);
         break;
      case AMOUNT_PER_BALANCE://AMOUNT PER BALANCE - Risk Option
         Lots = RiskProfile2(BALANCE);
         break;
      case AMOUNT_PER_FREEMARGIN://AMOUNT PER FREE-MARGIN - Risk Option
         Lots = RiskProfile2(MARGIN);
         break;
      case LOTSIZE_PER_BALANCE://LOTSIZE PER BALANCE - Risk Option
         Lots =  RiskProfile3(BALANCE);
         break;
      case LOTSIZE_PER_FREEMARGIN://LOTSIZE PER FREE-MARGIN - Risk Option
         Lots = RiskProfile3(MARGIN);
         break;
      case CUSTOM_LOT://CUSTOM LOTSIZE - Risk Option
         Lots = Risk_Profile_4;
         break;
      case PERCENTAGE_OF_MAXRISK://PERCENTAGE OF MAX-RISK - Risk Option
         Lots = MaxRisk(Risk_Profile_5);
         break;
      default:
         Lots = 0.0;
         break;
     }
   ValidateLotsize(Lots);//Check/Adjust Lotsize Limits
   NormalizeLotsize(Lots);//Normalize Lotsize
   return Lots;
  }

SetMedium関数では、列挙変数R_Mediumに基づいて、ユーザー/トレーダーの口座残高または口座の余剰証拠金の値をdouble型変数Mediumに割り当てます。

//-- Set Medium variable value
   void                SetMedium(const RiskMedium R_Medium) {Medium = (R_Medium==BALANCE)?Account.Balance():Account.FreeMargin();}

GetMinimumRisk関数では、最小ロットサイズでの特定の取引に必要な最小リスクをMinimumAmount変数に割り当てます。

//-- Get Minimum Risk for a Trade using Minimum Lot-size
   bool                GetMinimumRisk()
     {
      return OrderCalcProfit(ORDERTYPE,Symbol(),LotsMin(),OpenPrice,ClosePrice,MinimumAmount);
     }

GetRisk関数では、引数変数Amountで指定されたリスク量に必要なロットサイズを取得します。MinimumAmount(特定の取引の最小リスク額)が設定されている場合。AmountをMinimumAmountで割って、最小ロットサイズを掛けた商を取得し、Amountに必要なロットサイズを取得します。 

//-- Retrieve Risk amount based on Risk inputs
   double            GetRisk(double Amount)
     {
      if(!GetMinimumRisk()||Amount==0)
         return 0.0;
      return ((Amount/MinimumAmount)*LotsMin());
     }

RiskProfile1関数では、リスクオプションのPERCENTAGE OF BALANCEまたはPERCENTAGE OF FREE-MARGINのロットサイズを計算して返します。

//-- calculations for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)
double CRiskManagement::RiskProfile1(const RiskMedium R_Medium)
  {
   SetMedium(R_Medium);
   RiskAmount = Medium*(Risk_Profile_1/100);
   return GetRisk(RiskAmount);
  }

RiskProfile2関数では、リスクオプションAMOUNT PER BALANCEまたはAMOUNT PER FREE-MARGINのロットサイズを計算して返します。

//-- calculations for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)
double CRiskManagement::RiskProfile2(const RiskMedium R_Medium)
  {
   SetMedium(R_Medium);
   double risk = (Risk_Profile_2.RiskAmountBoF/Risk_Profile_2.RiskAmount);
   risk = (risk<1)?1:risk;

   if(Medium<=0)
      return 0.0;

   RiskAmount = Medium/risk;
   return GetRisk(RiskAmount);
  }

RiskProfile3関数では、リスクオプションLOTSIZE PER BALANCEまたはLOTSIZE PER FREE-MARGINのロットサイズを計算して返します。

//-- calculations for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)
double CRiskManagement::RiskProfile3(const RiskMedium R_Medium)
  {
   SetMedium(R_Medium);
   return (Medium>0)?((Medium/Risk_Profile_3.RiskLotBoF)*Risk_Profile_3.RiskLot):0.0;
  }

ValidateLotsize関数では、参照によって渡される変数Lotsizeに対して調整がおこなわれます。 

最初のSwitch式RiskFloorOptionでは、

  • RiskFloorMinの場合:変数Lotsizeが制限外であるかどうかを確認し、現在の銘柄の最小ロットサイズを割り当てます。変数値が最小ロットサイズより小さいかどうかの下限値をチェックします。上限は、Lotsize変数が最大リスクを超えたときです。
  • RiskFloorMaxの場合:まず、Lotsize変数が最大リスクより大きいかどうかを確認します。大きい場合は、次に最大希望最小リスクが最小ロットサイズより大きいかどうかを確認します。大きい場合は、その取引の最大希望最小リスクをLotsizeに割り当て、そうでない場合は最小ロットサイズを割り当てます。Lotsizeが当初、最大リスクよりも小さく、最小ロットサイズよりも小さかった場合は、最小ロットサイズを割り当てます。 
2番目のSwitch式RiskCeilOptionでは、各ケースで、Lotsizeがロットサイズ値に基づく最大リスクを超えているかどうかを確認し、達した場合はLotsize値をロットサイズ制限に設定します。

void CRiskManagement::ValidateLotsize(double &Lotsize)
  {
   switch(RiskFloorOption)
     {
      case RiskFloorMin://MINIMUM LOTSIZE for Risk floor options
         //-- Check if lot-size is not less than Minimum lot or more than maximum allowable risk
         if(Lotsize<LotsMin()||Lotsize>MaxRisk(max_percent))
           {
            Lotsize=LotsMin();
           }
         break;
      case RiskFloorMax://MAX-RISK for Risk floor options
         //-- Check if lot-size is more the maximum allowable risk
         if(Lotsize>MaxRisk(max_percent))
           {
            Lotsize=(MaxRisk(RiskFloorPercentage)>LotsMin())?MaxRisk(RiskFloorPercentage):LotsMin();
           }
         else
            if(Lotsize<LotsMin())//Check if lot-size is less than Minimum lot
              {
               Lotsize=LotsMin();
              }
         break;
      case RiskFloorNone://NONE for Risk floor options
         //Check if lot-size is less than Minimum lot
         if(Lotsize<LotsMin())
           {
            Lotsize=0.0;
           }
         break;
      default:
         Lotsize=0.0;
         break;
     }

   switch(RiskCeilOption)
     {
      case  RiskCeilMax://MAX LOTSIZE for Risk ceiling options
         //Check if lot-size is more than Maximum lot
         if(Lotsize>LotsMax())
            Lotsize=LotsMax();
         break;
      case RiskCeilMax2://MAX LOTSIZE(x2) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times two
         if(Lotsize>(LotsMax()*2))
            Lotsize=(LotsMax()*2);
         break;
      case RiskCeilMax3://MAX LOTSIZE(x3) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times three
         if(Lotsize>(LotsMax()*3))
            Lotsize=(LotsMax()*3);
         break;
      case RiskCeilMax4://MAX LOTSIZE(x4) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times four
         if(Lotsize>(LotsMax()*4))
            Lotsize=(LotsMax()*4);
         break;
      case RiskCeilMax5://MAX LOTSIZE(x5) for Risk ceiling options
         //Check if lot-size is more than Maximum lot times five
         if(Lotsize>(LotsMax()*5))
            Lotsize=(LotsMax()*5);
         break;
      default:
         break;
     }
  }

NormalizeLotsize関数の目的は、ロットサイズが銘柄の取引量制限内にあるかどうか、およびロットサイズが取引量ステップに従っているかどうかを確認することです。

ロットサイズが銘柄の数量制限に違反する場合数量制限に達する前に残りの利用可能なロットサイズを計算しますその後、残りのロットサイズが現在の銘柄の最小ロットサイズ以上であるかどうかを確認します

  • RiskFloorMinLotsize変数を最小ロットサイズに設定します。
  • RiskFloorMax:残りのロットサイズのRiskFloorPercentageが最小ロットサイズより大きい場合は、Lotsize変数をこの値に設定します。残りのロットサイズのRiskFloorPercentageが最小ロットサイズ以下の場合は、Lotsize変数を最小ロットサイズに設定します。 

void CRiskManagement::NormalizeLotsize(double &Lotsize)
  {
   if(Lotsize<=0.0)
      return;

//-- Check if the is a Volume limit for the current Symbol
   if(LotsLimit()>0.0)
     {
      if((Lots+PositionsVolume()+OrdersVolume())>LotsLimit())
        {
         //-- calculation of available lotsize remaining
         double remaining_avail_lots = (LotsLimit()-(PositionsVolume()+OrdersVolume()));
         if(remaining_avail_lots>=LotsMin())
           {
            if(RiskFloorOption==RiskFloorMin)//Check if Risk floor option is MINIMUM LOTSIZE
              {
               Print("Warning: Volume Limit Reached, minimum Lotsize selected.");
               Lotsize = LotsMin();
              }
            else
               if(RiskFloorOption==RiskFloorMax)//Check if Risk floor option is MAX-RISK
                 {
                  Print("Warning: Volume Limit Reached, Lotsize Reduced.");
                  Lotsize = ((remaining_avail_lots*(RiskFloorPercentage/100))>LotsMin())?
                            (remaining_avail_lots*(RiskFloorPercentage/100)):LotsMin();
                 }
           }
         else
           {
            Print("Volume Limit Reached!");
            Lotsize=0.0;
            return;
           }
        }
     }

//Check if there is a valid Volume Step for the current Symbol
   if(LotsStep()>0.0)
      Lotsize=LotsStep()*MathFloor(Lotsize/LotsStep());
  }


共通グラフィックスクラス

共通グラフィッククラスには、現在の銘柄の一般的なプロパティと、トレーダーによって設定されたリスクオプションの一部が表示されます。

CommonGraphicsクラスには、次のクラスからのマルチレベル継承があります。

  • CObjectProperties
  • CChartProperties
  • CSymbolProperties

CommonGraphicsクラスには、CRiskManagementクラスからのインクルードがあります。

CommonGraphicクラスには、次のクラスからの階層的継承があります。

  • CSymbolProperties
  • CSymbolInfo

//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+
#include "ObjectProperties.mqh"
#include "RiskManagement.mqh"
//+------------------------------------------------------------------+
//|CommonGraphics class                                              |
//+------------------------------------------------------------------+
class CCommonGraphics:CObjectProperties
  {
private:
   CRiskManagement   CRisk;//Risk management class object

public:
                     CCommonGraphics(void);//class constructor
                    ~CCommonGraphics(void) {}//class destructor
   void              GraphicsRefresh();//will create the chart objects
  };

//+------------------------------------------------------------------+
//|Constructor                                                       |
//+------------------------------------------------------------------+
CCommonGraphics::CCommonGraphics(void)
  {
   GraphicsRefresh();//calling GraphicsRefresh function
  }

//+------------------------------------------------------------------+
//|Specify Chart Objects                                             |
//+------------------------------------------------------------------+
void CCommonGraphics::GraphicsRefresh()
  {
//-- Will create the rectangle object
   Square(0,"Symbol Properties",2,20,330,183,ANCHOR_LEFT_UPPER);
//-- Will create the text object for the Symbol's name
   TextObj(0,"Symbol Name",Symbol(),5,23);
//-- Will create the text object for the contract size
   TextObj(0,"Symbol Contract Size","Contract Size: "+string(ContractSize()),5,40,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Minimum lotsize
   TextObj(0,"Symbol MinLot","Minimum Lot: "+string(LotsMin()),5,60,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Maximum lotsize
   TextObj(0,"Symbol MaxLot","Max Lot: "+string(LotsMax()),5,80,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Volume Step
   TextObj(0,"Symbol Volume Step","Volume Step: "+string(LotsStep()),5,100,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Volume Limit
   TextObj(0,"Symbol Volume Limit","Volume Limit: "+string(LotsLimit()),5,120,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the trader's Risk Option
   TextObj(0,"Risk Option","Risk Option: "+CRisk.GetRiskOption(),5,140,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the trader's Risk Floor
   TextObj(0,"Risk Floor","Risk Floor: "+CRisk.GetRiskFloor(),5,160,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the trader's Risk Ceiling
   TextObj(0,"Risk Ceil","Risk Ceiling: "+CRisk.GetRiskCeil(),5,180,CORNER_LEFT_UPPER,9);
  }
//+------------------------------------------------------------------+

GraphicsRefresh関数では、グラフィカルチャートオブジェクトのプロパティを設定します。

アクセス可能なチャートオブジェクト

チャートオブジェクト

void CCommonGraphics::GraphicsRefresh()
  {
//-- Will create the rectangle object
   Square(0,"Symbol Properties",2,20,330,183,ANCHOR_LEFT_UPPER);
//-- Will create the text object for the Symbol's name
   TextObj(0,"Symbol Name",Symbol(),5,23);
//-- Will create the text object for the contract size
   TextObj(0,"Symbol Contract Size","Contract Size: "+string(ContractSize()),5,40,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Minimum lotsize
   TextObj(0,"Symbol MinLot","Minimum Lot: "+string(LotsMin()),5,60,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Maximum lotsize
   TextObj(0,"Symbol MaxLot","Max Lot: "+string(LotsMax()),5,80,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Volume Step
   TextObj(0,"Symbol Volume Step","Volume Step: "+string(LotsStep()),5,100,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the Symbol's Volume Limit
   TextObj(0,"Symbol Volume Limit","Volume Limit: "+string(LotsLimit()),5,120,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the trader's Risk Option
   TextObj(0,"Risk Option","Risk Option: "+CRisk.GetRiskOption(),5,140,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the trader's Risk Floor
   TextObj(0,"Risk Floor","Risk Floor: "+CRisk.GetRiskFloor(),5,160,CORNER_LEFT_UPPER,9);
//-- Will create the text object for the trader's Risk Ceiling
   TextObj(0,"Risk Ceil","Risk Ceiling: "+CRisk.GetRiskCeil(),5,180,CORNER_LEFT_UPPER,9);
  }


EA

繰り返しますが、この記事では取引を開始することはありません。
//+------------------------------------------------------------------+
//|                                                      NewsTrading |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                            https://www.mql5.com/ja/users/kaaiblo |
//+------------------------------------------------------------------+
//--- width and height of the canvas (used for drawing)
#define IMG_WIDTH  200
#define IMG_HEIGHT 100
//--- enable to set color format
ENUM_COLOR_FORMAT clr_format=COLOR_FORMAT_XRGB_NOALPHA;
//--- drawing array (buffer)
uint ExtImg[IMG_WIDTH*IMG_HEIGHT];

#include "News.mqh"
CNews NewsObject;//Class CNews Object 'NewsObject'
#include "TimeManagement.mqh"
CTimeManagement CTM;//Class CTimeManagement Object 'CTM'
#include "WorkingWithFolders.mqh"
CFolders Folder();//Calling Class's Constructor
#include "ChartProperties.mqh"
CChartProperties CChart;//Class CChartProperties Object 'CChart'
#include "RiskManagement.mqh"
CRiskManagement CRisk;//Class CRiskManagement Object 'CRisk'
#include "CommonGraphics.mqh"
CCommonGraphics CGraphics();//Calling Class's Constructor

enum iSeparator
  {
   Delimiter//__________________________
  };

sinput group "+--------| RISK MANAGEMENT |--------+";
input RiskOptions RISK_Type=MINIMUM_LOT;//SELECT RISK OPTION
input RiskFloor RISK_Mini=RiskFloorMin;//RISK FLOOR
input double RISK_Mini_Percent=75;//MAX-RISK [100<-->0.01]%
input RiskCeil  RISK_Maxi=RiskCeilMax;//RISK CEILING
sinput iSeparator iRisk_1=Delimiter;//__________________________
sinput iSeparator iRisk_1L=Delimiter;//PERCENTAGE OF [BALANCE | FREE-MARGIN]
input double Risk_1_PERCENTAGE=3;//[100<-->0.01]%
sinput iSeparator iRisk_2=Delimiter;//__________________________
sinput iSeparator iRisk_2L=Delimiter;//AMOUNT PER [BALANCE | FREE-MARGIN]
input double Risk_2_VALUE=1000;//[BALANCE | FREE-MARGIN]
input double Risk_2_AMOUNT=10;//EACH AMOUNT
sinput iSeparator iRisk_3=Delimiter;//__________________________
sinput iSeparator iRisk_3L=Delimiter;//LOTSIZE PER [BALANCE | FREE-MARGIN]
input double Risk_3_VALUE=1000;//[BALANCE | FREE-MARGIN]
input double Risk_3_LOTSIZE=0.1;//EACH LOTS(VOLUME)
sinput iSeparator iRisk_4=Delimiter;//__________________________
sinput iSeparator iRisk_4L=Delimiter;//CUSTOM LOTSIZE
input double Risk_4_LOTSIZE=0.01;//LOTS(VOLUME)
sinput iSeparator iRisk_5=Delimiter;//__________________________
sinput iSeparator iRisk_5L=Delimiter;//PERCENTAGE OF MAX-RISK
input double Risk_5_PERCENTAGE=1;//[100<-->0.01]%

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

//Initializing CRiskManagement variable for Risk options
   RiskProfileOption = RISK_Type;
//Initializing CRiskManagement variable for Risk floor
   RiskFloorOption = RISK_Mini;
//Initializing CRiskManagement variable for RiskFloorMax
   RiskFloorPercentage = (RISK_Mini_Percent>100)?100:
                         (RISK_Mini_Percent<0.01)?0.01:RISK_Mini_Percent;//Percentage cannot be more than 100% or less than 0.01%
//Initializing CRiskManagement variable for Risk ceiling
   RiskCeilOption = RISK_Maxi;
//Initializing CRiskManagement variable for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN)
   Risk_Profile_1 = (Risk_1_PERCENTAGE>100)?100:
                    (Risk_1_PERCENTAGE<0.01)?0.01:Risk_1_PERCENTAGE;//Percentage cannot be more than 100% or less than 0.01%
//Initializing CRiskManagement variables for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN)
   Risk_Profile_2.RiskAmountBoF = Risk_2_VALUE;
   Risk_Profile_2.RiskAmount = Risk_2_AMOUNT;
//Initializing CRiskManagement variables for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN)
   Risk_Profile_3.RiskLotBoF = Risk_3_VALUE;
   Risk_Profile_3.RiskLot = Risk_3_LOTSIZE;
//Initializing CRiskManagement variable for Risk option (CUSTOM LOTSIZE)
   Risk_Profile_4 = Risk_4_LOTSIZE;
//Initializing CRiskManagement variable for Risk option (PERCENTAGE OF MAX-RISK)
   Risk_Profile_5 = (Risk_5_PERCENTAGE>100)?100:
                    (Risk_5_PERCENTAGE<0.01)?0.01:Risk_5_PERCENTAGE;//Percentage cannot be more than 100% or less than 0.01%

   CChart.ChartRefresh();//Load chart configurations
   CGraphics.GraphicsRefresh();//-- Create/Re-create chart objects

   if(!MQLInfoInteger(MQL_TESTER))//Checks whether the program is in the strategy tester
     {
      //--- create OBJ_BITMAP_LABEL object for drawing
      ObjectCreate(0,"STATUS",OBJ_BITMAP_LABEL,0,0,0);
      ObjectSetInteger(0,"STATUS",OBJPROP_XDISTANCE,5);
      ObjectSetInteger(0,"STATUS",OBJPROP_YDISTANCE,22);
      //--- specify the name of the graphical resource
      ObjectSetString(0,"STATUS",OBJPROP_BMPFILE,"::PROGRESS");
      uint   w,h;          // variables for receiving text string sizes
      uint    x,y;          // variables for calculation of the current coordinates of text string anchor points

      /*
      In the Do while loop below, the code will check if the terminal is connected to the internet.
      If the the program is stopped the loop will break, if the program is not stopped and the terminal
      is connected to the internet the function CreateEconomicDatabase will be called from the News.mqh header file's
      object called NewsObject and the loop will break once called.
      */
      bool done=false;
      do
        {
         //--- clear the drawing buffer array
         ArrayFill(ExtImg,0,IMG_WIDTH*IMG_HEIGHT,0);

         if(!TerminalInfoInteger(TERMINAL_CONNECTED))
           {
            //-- integer dots used as a loading animation
            static int dots=0;
            //--- set the font
            TextSetFont("Arial",-150,FW_EXTRABOLD,0);
            TextGetSize("Waiting",w,h);//get text width and height values
            //--- calculate the coordinates of the 'Waiting' text
            x=10;//horizontal alignment
            y=IMG_HEIGHT/2-(h/2);//alignment for the text to be centered vertically
            //--- output the 'Waiting' text to ExtImg[] buffer
            TextOut("Waiting",x,y,TA_LEFT|TA_TOP,ExtImg,IMG_WIDTH,IMG_HEIGHT,ColorToARGB(CChart.SymbolBackground()),clr_format);
            //--- calculate the coordinates for the dots after the 'Waiting' text
            x=w+13;//horizontal alignment
            y=IMG_HEIGHT/2-(h/2);//alignment for the text to be centered vertically
            TextSetFont("Arial",-160,FW_EXTRABOLD,0);
            //--- output of dots to ExtImg[] buffer
            TextOut(StringSubstr("...",0,dots),x,y,TA_LEFT|TA_TOP,ExtImg,IMG_WIDTH,IMG_HEIGHT,ColorToARGB(CChart.SymbolBackground()),clr_format);
            //--- update the graphical resource
            ResourceCreate("::PROGRESS",ExtImg,IMG_WIDTH,IMG_HEIGHT,0,0,IMG_WIDTH,clr_format);
            //--- force chart update
            ChartRedraw();
            dots=(dots==3)?0:dots+1;
            //-- Notify user that program is waiting for connection
            Print("Waiting for connection...");
            Sleep(500);
            continue;
           }
         else
           {
            //--- set the font
            TextSetFont("Arial",-120,FW_EXTRABOLD,0);
            TextGetSize("Getting Ready",w,h);//get text width and height values
            x=20;//horizontal alignment
            y=IMG_HEIGHT/2-(h/2);//alignment for the text to be centered vertically
            //--- output the text 'Getting Ready...' to ExtImg[] buffer
            TextOut("Getting Ready...",x,y,TA_LEFT|TA_TOP,ExtImg,IMG_WIDTH,IMG_HEIGHT,ColorToARGB(CChart.SymbolBackground()),clr_format);
            //--- update the graphical resource
            ResourceCreate("::PROGRESS",ExtImg,IMG_WIDTH,IMG_HEIGHT,0,0,IMG_WIDTH,clr_format);
            //--- force chart update
            ChartRedraw();
            //-- Notify user that connection is successful
            Print("Connection Successful!");
            NewsObject.CreateEconomicDatabase();//calling the database create function
            done=true;
           }
        }
      while(!done&&!IsStopped());
      //-- Delete chart object
      ObjectDelete(0,"STATUS");
      //-- force chart to update
      ChartRedraw();
     }
   else
     {
      //Checks whether the database file exists
      if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))
        {
         Print("Necessary Files Do not Exist!");
         Print("Run Program outside of the Strategy Tester");
         Print("Necessary Files Should be Created First");
         return(INIT_FAILED);
        }
      else
        {
         //Checks whether the lastest database date includes the time and date being tested
         datetime latestdate = CTM.TimePlusOffset(NewsObject.GetLatestNewsDate(),CTM.DaysS());//Day after the lastest recorded time in the database
         if(latestdate<TimeCurrent())
           {
            Print("Necessary Files outdated!");
            Print("To Update Files: Run Program outside of the Strategy Tester");
           }
         Print("Database Dates End at: ",latestdate);
         PrintFormat("Dates after %s will not be available for backtest",TimeToString(latestdate));
        }
     }
//-- the volume calculations and the risk type set by the trader
   Print("Lots: ",CRisk.Volume()," || Risk type: ",CRisk.GetRiskOption());
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+

プロジェクトファイル


EA


すべてがコンパイルされたら、EAがチャートに配置されたときに実行されるいくつかの手順を実行します。

US30チャート

どの銘柄チャートウィンドウを開くかを決定すると、EAを添付する前のチャートは上記のチャートのようになります。

次に、EAを接続します。

EAをアタッチする

まずリスク管理の入力変数を設定します。最初はデフォルト値のままにしておきます。

EAの入力

NewsTrading 2.00を初めて実行する場合、以前にブローカーでNewsTrading 1.00を実行したことがなく、カレンダーデータベースが共通フォルダーに存在しない場合、

チャートはこのように表示されますが、チャートの色はブローカーごとに異なる場合があります。

EAチャート#1

下のメッセージに見られるように、ロットは設定で選択されたリスクオプションに従って表示されます。

EAチャート#1メッセージ


端末が接続を見つけられない場合、次のようになります。


NewsTrading 2.00 EAを初めて実行し、カレンダーデータベースがNewsTrading 1.00 EAから作成された場合、

NewsTrading 2.00はNewsTrading 1.00からすべてのテーブルを削除します。

以前のバージョンからEAが添付され、カレンダーDDが存在します


結論

この記事では、継承の仕組みを説明するために例を挙げました。まず、さまざまなDSTスケジュールクラスの親として機能する新しい夏時間クラスを作成しました。また、継承されるすべてのクラスから銘柄プロパティを取得するための銘柄プロパティクラスも作成しました。さらに、チャートをカスタマイズするためのチャートプロパティクラスも作成しました。加えて、さまざまなSQLite手法と、データベース全体の効率を向上させる簡単な方法についても検討しました。チャートオブジェクトの作成と削除を行うためのオブジェクトプロパティクラス、さまざまなリスクプロファイルやトレーダーに対応するリスク管理クラスを作成しました。そして最後に、トレーダーのリスクオプションとともに銘柄プロパティをチャートに表示する共通グラフィッククラスを作成しました。次回の「ニュース取引が簡単に(第3回):取引の実行」では、ついに取引の実行に着手します。妥当なタイムラインでの完了が待ち遠しいです。


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

添付されたファイル |
古典的な戦略を再構築する(第7回):USDJPYにおける外国為替市場とソブリン債務分析 古典的な戦略を再構築する(第7回):USDJPYにおける外国為替市場とソブリン債務分析
本日の記事では、今後の為替レートと国債の関係を分析します。債券は、最も人気のある固定利付証券の1つであり、今回の議論の焦点となります。AIを使用して従来の戦略を改善できるかどうかを一緒に検討しましょう。
チャート上で取引を視覚化する(第1回):分析期間の選択 チャート上で取引を視覚化する(第1回):分析期間の選択
ここでは、取引エントリを分析するために取引の印刷画面のアンロードを簡素化するスクリプトをゼロから開発します。単一の取引に関するすべての必要な情報は、異なる時間枠を描画する機能を備えた1つのチャートに便利に表示されます。
知っておくべきMQL5ウィザードのテクニック(第36回):マルコフ連鎖を用いたQ学習 知っておくべきMQL5ウィザードのテクニック(第36回):マルコフ連鎖を用いたQ学習
強化学習は、教師あり学習、教師なし学習と並んで、機械学習における3つの主要な考え方の1つです。そのため、最適制御、つまり目的関数に最も適した長期的な方針を学習することに関心があります。このような背景から、ウィザードが作成したEAのMLPの学習プロセスにおいて、MLPがどのような役割を果たす可能性があるのかを探ります。
PythonとMQL5でロボットを開発する(第2回):モデルの選択、作成、訓練、Pythonカスタムテスター PythonとMQL5でロボットを開発する(第2回):モデルの選択、作成、訓練、Pythonカスタムテスター
PythonとMQL5で自動売買ロボットを開発する連載を続けます。今日は、モデルの選択と訓練、テスト、交差検証、グリッドサーチ、モデルアンサンブルの問題を解決します。