
ニュース取引が簡単に(第6回):取引の実施(III)
はじめに
この記事では、ストレージデータベースを改良し、MQL5経済指標カレンダー内の各固有イベントの最後のニュースイベントまたは次のニュースイベントの日付を表示するなど、データを表示するための新しいビューを追加します。これにより、将来や過去のイベントに対する認識が深まり、プログラムを使用する際のユーザーエクスペリエンスが向上します。さらに、エキスパートアドバイザー(EA)入力メニューは、ニュースのフィルターや逆指値注文の入力方法に対応するために拡張されます。
また、以前の記事「ニュース取引が簡単に(第4回):パフォーマンス向上」で書かれたコードを活用し、ストラテジーテスターでの実行時間を短縮するようにエキスパートアドバイザー(EA)を更新します。さらに「ニュース取引が簡単に(第5回):取引の実行(II)」で紹介されたコードを使用して、スリッページの管理と逆指値注文の処理もおこないます。
ニュース設定入力
- SELECT NEWS OPTION:このオプションの目的は、さまざまなニュースプロファイルを許可することです。以下のプロファイルがあります。
- NEWS SETTINGS:ユーザー/トレーダーは次の基準に基づいてニュースイベントを好みに合わせてフィルターできます。
- CALENDAR IMPORTANCE
- EVENT FREQUENCY
- EVENT SECTOR
- EVENT TYPE
- EVENT CURRENCY
- CUSTOM NEWS EVENTS:ユーザー/トレーダーは、入力として入力されたイベントIDに応じて、好みに合わせてニュースイベントをフィルターできます。入力ごとに最大14個のイベントIDを使用できます。
取引設定の入力
- SELECT TRADE ENTRY OPTION:このオプションの目的は、さまざまな取引エントリー方法を可能にすることです。以下の方法があります。
- MARKET POSITION:取引は市場執行(売買取引)を通じてのみおこなわれます。主な要件は、この取引エントリ選択で取引対象として選択されるニュースイベントには、事前に取引の方向(ロングまたはショート)を知るための「イベントの影響」がなければならないということです。
- STOP ORDERS:選択したニュースイベントが発生する前に、買い逆指値注文と売り逆指値注文でのみ取引が実行されます。主な要件は、ユーザー/トレーダーがEAに買い逆指値注文と売り逆指値注文を出すための価格バッファを持たせるために価格偏差を設定する必要があることです。買いストップまたは売りストップのいずれかがトリガーされると、反対の注文は削除されます。たとえば、NFP(ニュースイベント)の前に買い逆指値注文と売り逆指値注文が出され、買い逆指値注文がトリガーされると(つまり、特定の価格に達すると買いポジションが実行される)、EAは残っている売り逆指値注文を積極的に削除します。事前に取引をおこなうため、「イベントの影響」を必要としません。
- SINGLE STOP ORDER:取引は買い逆指値注文または売り逆指値注文のいずれかでのみ実行されます。主な2つの要件は次のとおりです。
- この取引エントリ選択で取引対象として選択されるニュースイベントは、事前に注文の方向(買いストップまたは売りストップ)を知るための「イベントの影響」を持っている必要があります。
- ユーザー/トレーダーは、買い逆指値注文または売り逆指値注文のいずれかを出すための価格バッファを確保するために、EAの価格偏差を設定する必要があります。
ニュースクラス
ヘッダーファイル「News.mqh」では、CNewsクラスの外側に列挙体「NewsSelection」を宣言します。この列挙体の目的は、ユーザー/トレーダーがEAの入力内でさまざまなニュースプロファイルを選択できるようにすることです。また、ユーザー/トレーダーの好みの選択を保存する変数「myNewsSelection」も用意します。さらに、構造体「CustomEvent」を宣言します。この構造体には、構造体内のEventIds文字列配列でイベントIDをフィルターするかどうかを決定するブール値が格納されます。さらに、この構造体には、CEvent1などの変数が宣言されており、この変数は、ユーザー/トレーダーがカスタムイベントIDをフィルターできる5つの選択肢のうちの1つとして機能します。
列挙体
- NewsSelection:2つのプロファイルを定義します。
- News_Select_Custom_Events:カスタムニュースイベント用
- News_Select_Settings:ニュース設定用
- myNewsSelectionは、現在のニュースプロファイルの選択を格納する列挙型の変数です。
構造体
- CustomEvent:カスタムイベントIDと、これらのイベントをクエリに含めるかどうかを示すフラグ(useEvents)を格納するための構造体
- 変数は5つあります。CustomEvent型のCEvent1、CEvent2、CEvent3、CEvent4、CEvent5は、それぞれ個別のイベントグループを表します。
//--- Enumeration for News Profiles enum NewsSelection { News_Select_Custom_Events,//CUSTOM NEWS EVENTS News_Select_Settings//NEWS SETTINGS } myNewsSelection; //--- Structure to store event ids and whether to use these ids struct CustomEvent { bool useEvents; string EventIds[]; } CEvent1,CEvent2,CEvent3,CEvent4,CEvent5;
CalendarComponents列挙体:
- CalendarComponents:DST(夏時間)スケジュール、イベント情報、通貨データに関連するデータを構造化するために使用されるテーブルやビューなど、経済指標カレンダーのさまざまなコンポーネントを列挙します。
CalendarComponents列挙体内に2つの新しい値を追加しました。
- RecentEventInfo_View
- UpcomingEventInfo_View
//-- To keep track of what is in our database enum CalendarComponents { // ... RecentEventInfo_View,//View for Recent Dates For Events UpcomingEventInfo_View,//View for Upcoming Dates For Events // ... };
GetCalendar(CalendarData&Data[])関数
- ストレージ内のカレンダーデータベースから関連するすべてのカレンダーデータを取得し、それをデータ配列に格納します。
- データベース(NEWS_DATABASE_FILE)を開き、現在のニュース選択(myNewsSelection)に基づいてSQLクエリを実行します。
- News_Select_Custom_EventsまたはNews_Select_Settingsのどちらが選択されているかに応じて、イベント情報を取得するための異なるSQLクエリが生成されます。
- カスタムイベント:MQL5CalendarテーブルとTimeScheduleテーブルを結合し、カスタムイベントIDに基づくフィルターを使用してカスタムニュースイベントを取得します。
- ニュース設定:重要度、頻度、セクター、タイプ、通貨などのユーザー指定の設定でフィルターされたイベントデータを取得します。
- この関数はSQLクエリの結果を処理し、取得したデータをデータ配列に格納します。
- データベースクエリが失敗した場合は、エラーと失敗したSQLクエリが出力されます。
//--- Will Retrieve all relevant Calendar data for DB in Memory from DB in Storage void GetCalendar(CalendarData &Data[]) { // ... string SqlRequest; //--- switch statement for different News Profiles switch(myNewsSelection) { case News_Select_Custom_Events://CUSTOM NEWS EVENTS //--- Get filtered calendar DB data SqlRequest = StringFormat("Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency," "MQ.EventCode,MQ.EventSector,MQ.EventForecast,MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency," "TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from %s MQ " "Inner Join %s TS on TS.ID=MQ.ID Where %s OR %s OR %s OR %s OR %s;", CalendarStruct(MQL5Calendar_Table).name,CalendarStruct(TimeSchedule_Table).name, Request_Events(CEvent1),Request_Events(CEvent2),Request_Events(CEvent3), Request_Events(CEvent4),Request_Events(CEvent5)); break; case News_Select_Settings://NEWS SETTINGS //--- Get filtered calendar DB data SqlRequest = StringFormat("Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency," "MQ.EventCode,MQ.EventSector,MQ.EventForecast,MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency," "TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from %s MQ " "Inner Join %s TS on TS.ID=MQ.ID " "Where %s and %s and %s and %s and %s;", CalendarStruct(MQL5Calendar_Table).name,CalendarStruct(TimeSchedule_Table).name, Request_Importance(myImportance),Request_Frequency(myFrequency), Request_Sector(mySector),Request_Type(myType),Request_Currency(myCurrency)); break; default://Unknown break; } // ...
Request_Events(CustomEvent&CEvent)関数
- CustomEvent構造体に格納されているカスタムイベントIDに基づいてデータベースを照会するためのSQLWHERE句を生成します。
- useEventsがtrueかどうかを確認します。trueであれば、CEvent.EventIds[]配列からの各イベントIDをSQLクエリに追加します。
//--- Retrieve Sql request string for custom event ids string Request_Events(CustomEvent &CEvent) { //--- Default request string string EventReq="MQ.EventId='0'"; //--- Check if this Custom event should be included in the SQL request if(CEvent.useEvents) { //--- Get request for first event id EventReq=StringFormat("(MQ.EventId='%s'", (CEvent.EventIds.Size()>0)? CEvent.EventIds[0]:"0"); //--- Iterate through remaining event ids and add to the SQL request for(uint i=1;i<CEvent.EventIds.Size();i++) { EventReq+=StringFormat(" OR MQ.EventId='%s'",CEvent.EventIds[i]); } EventReq+=")"; } //--- Return SQL request for custom event ids return EventReq; }
Publicクラスメンバー
関数「EconomicDetailsMemory」、 「EconomicNextEvent」、「isEvent」が更新されました。
- EconomicDetailsMemory:メモリ内のカレンダーデータベースから値を取得します。
- EconomicNextEvent:次のイベントのデータで構造体変数を更新します。
- isEvent:ニュースイベントが発生するかどうかを確認し、渡されたパラメータをそれに応じて変更します。
//Public declarations accessable via a class's Object public: // ... void EconomicDetailsMemory(Calendar &NewsTime[],datetime date,bool ImpactRequired);//Gets values from the MQL5 DB Calendar in Memory void EconomicNextEvent();//Will update UpcomingNews structure variable with the next event data // ... //--- Checks if a news event is occurring and modifies the parameters passed by reference bool isEvent(uint SecondsPreEvent,string &Name,string &Importance,string &Code);
関数「EconomicDetailsMemory」の目的は、「イベントの影響」をオプションで考慮しながら、特定の日付の経済指標カレンダーイベントデータをインメモリデータベース(DBMemory)から取得し、そのデータを配列NewsTime[]に格納することです。
- この関数は、指定された日付の経済イベントの詳細をデータベースから取得し、NewsTime[]配列に格納します。
- ImpactRequiredがtrueの場合、イベントの以前の値と予測値が取得され、履歴データに基づいて影響が割り当てられます。
- ImpactRequiredがfalseの場合、現在の日と次のイベントのデータを取得するだけです。
- 結果は準備されたSQLクエリを使用して取得され、NewsTime[]配列に保存されます。この配列は、取得されたイベントの数に基づいて動的にサイズが変更されます。
//+------------------------------------------------------------------+ //|Gets values from the MQL5 DB Calendar in Memory | //+------------------------------------------------------------------+ void CNews::EconomicDetailsMemory(Calendar &NewsTime[],datetime date,bool ImpactRequired) { //--- SQL query to retrieve news data for a certain date string request_text; //--- Check if Event impact is required for retrieving news events if(ImpactRequired) { request_text=StringFormat("WITH DAILY_EVENTS AS(SELECT M.EVENTID as 'E_ID',M.COUNTRY,M.EVENTNAME as 'Name',M.EVENTTYPE as" " 'Type',M.EVENTIMPORTANCE as 'Importance',M.%s as 'Time',M.EVENTCURRENCY as 'Currency',M.EVENTCODE" " as 'Code',M.EVENTSECTOR as 'Sector',M.EVENTFORECAST as 'Forecast',M.EVENTPREVALUE as 'PREVALUE'," "M.EVENTFREQUENCY as 'Freq' FROM %s M WHERE DATE(REPLACE(Time,'.','-'))=DATE(REPLACE('%s','.','-'))" " AND (Forecast<>'None' AND Prevalue<>'None')),DAILY_IMPACT AS(SELECT DE.E_ID,DE.COUNTRY,DE.Name," "DE.Type,DE.Importance,DE.Time,DE.Currency,DE.Code,DE.Sector,DE.Forecast,DE.Prevalue,DE.Freq," "MC.EVENTIMPACT as 'IMPACT', RANK() OVER(PARTITION BY DE.E_ID,DE.Time ORDER BY MC.%s DESC)DateOrder" " FROM %s MC INNER JOIN DAILY_EVENTS DE on DE.E_ID=MC.EVENTID WHERE DATE(REPLACE(MC.%s,'.','-'))<" "DATE(REPLACE(DE.Time,'.','-')) AND DATE(REPLACE(MC.%s,'.','-'))>=DATE(REPLACE(DE.Time,'.','-')," "'-24 months') AND (MC.EVENTFORECAST<>'None' AND MC.EVENTPREVALUE<>'None' AND (CASE WHEN Forecast>" "Prevalue THEN 'more' WHEN Forecast<Prevalue THEN 'less' ELSE 'equal' END)=(CASE WHEN MC.EVENTFORECAST" ">MC.EVENTPREVALUE THEN 'more' WHEN MC.EVENTFORECAST<MC.EVENTPREVALUE THEN 'less' ELSE 'equal' END)) " "ORDER BY MC.%s),DAILY_EVENTS_RECORDS AS(SELECT * FROM DAILY_IMPACT WHERE DateOrder=1 ORDER BY Time" " ASC),NEXT_EVENT AS(SELECT M.EVENTID as 'E_ID',M.COUNTRY,M.EVENTNAME as 'Name',M.EVENTTYPE as 'Type'," "M.EVENTIMPORTANCE as 'Importance',M.%s as 'Time',M.EVENTCURRENCY as 'Currency',M.EVENTCODE as 'Code'," "M.EVENTSECTOR as 'Sector',M.EVENTFORECAST as 'Forecast',M.EVENTPREVALUE as 'PREVALUE',M.EVENTFREQUENCY" " as 'Freq' FROM %s M WHERE DATE(REPLACE(Time,'.','-'))>DATE(REPLACE('%s','.','-')) AND (Forecast<>" "'None' AND Prevalue<>'None' AND DATE(REPLACE(Time,'.','-'))<=DATE(REPLACE('%s','.','-'),'+60 days')))," "NEXT_IMPACT AS(SELECT NE.E_ID,NE.COUNTRY,NE.Name,NE.Type,NE.Importance,NE.Time,NE.Currency,NE.Code" ",NE.Sector,NE.Forecast,NE.Prevalue,NE.Freq,MC.EVENTIMPACT as 'IMPACT',RANK() OVER(PARTITION BY " "NE.E_ID,NE.Time ORDER BY MC.%s DESC)DateOrder FROM %s MC INNER JOIN NEXT_EVENT NE on NE.E_ID=MC.EVENTID " "WHERE DATE(REPLACE(MC.%s,'.','-'))<DATE(REPLACE(NE.Time,'.','-')) AND DATE(REPLACE(MC.%s,'.','-'))>=" "DATE(REPLACE(NE.Time,'.','-'),'-24 months') AND (MC.EVENTFORECAST<>'None' AND MC.EVENTPREVALUE<>'None'" " AND (CASE WHEN Forecast>Prevalue THEN 'more' WHEN Forecast<Prevalue THEN 'less' ELSE 'equal' END)=" "(CASE WHEN MC.EVENTFORECAST>MC.EVENTPREVALUE THEN 'more' WHEN MC.EVENTFORECAST<MC.EVENTPREVALUE THEN " "'less' ELSE 'equal' END)) ORDER BY MC.%s),NEXT_EVENT_RECORD AS(SELECT * FROM NEXT_IMPACT WHERE " "DateOrder=1 ORDER BY Time ASC LIMIT 1),ALL_EVENTS AS(SELECT * FROM NEXT_EVENT_RECORD UNION ALL " "SELECT * FROM DAILY_EVENTS_RECORDS)SELECT E_ID,Country,Name,Type,Importance,Time,Currency,Code," "Sector,Forecast,Prevalue,Impact,Freq FROM ALL_EVENTS GROUP BY Time ORDER BY Time Asc;", EnumToString(MySchedule),DBMemory.name,TimeToString(date),EnumToString(MySchedule),DBMemory.name, EnumToString(MySchedule),EnumToString(MySchedule),EnumToString(MySchedule),EnumToString(MySchedule) ,DBMemory.name,TimeToString(date),TimeToString(date),EnumToString(MySchedule),DBMemory.name, EnumToString(MySchedule),EnumToString(MySchedule),EnumToString(MySchedule)); } else { /* Within this request we select all the news events that will occur or have occurred in the current day and the next news event after the current day */ request_text=StringFormat("WITH DAILY_EVENTS AS(SELECT M.EVENTID as 'E_ID',M.COUNTRY,M.EVENTNAME as 'Name',M.EVENTTYPE as" " 'Type',M.EVENTIMPORTANCE as 'Importance',M.%s as 'Time',M.EVENTCURRENCY as 'Currency',M.EVENTCODE" " as 'Code',M.EVENTSECTOR as 'Sector',M.EVENTFORECAST as 'Forecast',M.EVENTPREVALUE as 'PREVALUE'" ",M.EVENTFREQUENCY as 'Freq',M.EVENTIMPACT as 'Impact' FROM %s M WHERE DATE(REPLACE(Time,'.','-'))" "=DATE(REPLACE('%s','.','-'))),DAILY_EVENTS_RECORDS AS(SELECT * FROM DAILY_EVENTS ORDER BY Time ASC)" ",NEXT_EVENT AS(SELECT M.EVENTID as 'E_ID',M.COUNTRY,M.EVENTNAME as 'Name',M.EVENTTYPE as 'Type'," "M.EVENTIMPORTANCE as 'Importance',M.%s as 'Time',M.EVENTCURRENCY as 'Currency',M.EVENTCODE as " "'Code',M.EVENTSECTOR as 'Sector',M.EVENTFORECAST as 'Forecast',M.EVENTPREVALUE as 'PREVALUE'," "M.EVENTFREQUENCY as 'Freq',M.EVENTIMPACT as 'Impact' FROM %s M WHERE DATE(REPLACE(Time,'.','-'))" ">DATE(REPLACE('%s','.','-')) AND (DATE(REPLACE(Time,'.','-'))<=DATE(REPLACE('%s','.','-')," "'+60 days'))),NEXT_EVENT_RECORD AS(SELECT * FROM NEXT_EVENT ORDER BY Time ASC LIMIT 1)," "ALL_EVENTS AS(SELECT * FROM NEXT_EVENT_RECORD UNION ALL SELECT * FROM " "DAILY_EVENTS_RECORDS)SELECT * FROM ALL_EVENTS GROUP BY Time ORDER BY Time Asc;", EnumToString(MySchedule),DBMemory.name,TimeToString(date),EnumToString(MySchedule),DBMemory.name, TimeToString(date),TimeToString(date)); } // ...
関数シグネチャ
- 引数
- Calendar &NewsTime[]:取得したデータが格納されるカレンダー構造体の配列(それぞれにイベントの詳細が保持されます)
- datetime date:経済イベントを取得する特定の日付
- bool ImpactRequired:「イベントの影響」をクエリに考慮する必要があるかどうかを示すフラグ
影響要件に基づくSQLクエリ構築
SQLクエリは、ImpactRequiredがtrueに設定されているかfalseに設定されているかによって異なります。これがtrueの場合、ニュースイベントはイベントの取引方向に「イベントの影響」を必要とし、falseの場合、取引を開始するためにイベントの方向は必要ないため、イベントの影響は必要ありません。
A.ImpactRequiredがtrueの場合
このクエリはより複雑で、複数の部分から構成されます。
- 指定された日付の経済イベントを取得し、現在の日付以降の次のイベントも考慮します。
- 予測と事前値の両方が利用可能な過去24か月以内の過去のイベントを検索し、現在のイベントと比較します。
- 予測値が事前値より大きい場合(またはその逆)、または等しい場合、これは同じ傾向を持つ履歴イベントと一致します(予測>事前値、または予測<事前値、または予測=事前値)。
- 過去のイベントの「イベントの影響」を現在のイベントに割り当てます。
クエリの構築方法の概要は次のとおりです。
"WITH DAILY_EVENTS AS(...) , DAILY_IMPACT AS(...) , NEXT_EVENT AS(...), NEXT_IMPACT AS(...),
ALL_EVENTS AS(...) SELECT * FROM ALL_EVENTS GROUP BY Time ORDER BY Time Asc;"
- DAILY_EVENTS:カレンダーから指定された日付のすべての経済イベントを選択します。有効な予測と事前値を持つイベントが選択されます。
- DAILY_IMPACT:予測と予測値の傾向(大きい/小さいまたは等しい)が現在のイベントと類似している過去のイベント(過去24か月以内)を検索します。日付別にイベントをランク付けし、最新の一致するイベントを見つけて、その影響を現在のイベントに割り当てます。
- NEXT_EVENT:現在の日付の後の次の予定を選択します。
- NEXT_IMPACT:DAILY_IMPACTと同様に、予測/事前値の傾向に一致する最新の過去のイベントを取得し、その影響を次のイベントに割り当てます。
- ALL_EVENTS:DAILY_EVENTS_RECORDS(現在の日付)とNEXT_EVENT_RECORD(次のイベント)を結合し、時間順に並べます。
B.ImpactRequiredがfalseの場合
影響が必要ない場合、クエリはより単純になります。
- 現在の日付のすべてのイベントと、現在の日付以降の次のイベント(60日以内)を取得します。
以下がSQLクエリの概要です。
"WITH DAILY_EVENTS AS(...) , NEXT_EVENT AS(...), ALL_EVENTS AS(...) SELECT * FROM ALL_EVENTS GROUP BY Time ORDER BY Time Asc;"
- DAILY_EVENTS:当日の経済イベントを取得します。
- NEXT_EVENT:現在の日付以降の次の予定(今後60日以内)を取得します。
- ALL_EVENTS:毎日のイベントと次のイベントの両方を組み合わせて、時間順に並べます。
SQLクエリを実行します。
int request = DatabasePrepare(DBMemoryConnection, request_text);
- データベースの準備:実行用のSQLクエリを準備します。結果は、クエリへのハンドルであるrequestに保存されます。このハンドルはデータを取得するために使用されます。
- エラー処理:リクエストが失敗した場合(request==INVALID_HANDLE)、デバッグの目的でエラーメッセージとSQLクエリが出力されます。
結果を読み取ります。
Calendar ReadDB_Data; ArrayRemove(NewsTime, 0, WHOLE_ARRAY); for (int i = 0; DatabaseReadBind(request, ReadDB_Data); i++) { ArrayResize(NewsTime, i + 1, i + 2); NewsTime[i] = ReadDB_Data; }
- ArrayRemove:新しいデータの準備としてNewsTime[]配列をクリアします。
- DatabaseReadBind:準備されたクエリから結果を取得し、各結果行をカレンダー構造であるReadDB_Data変数にバインドします。
- ArrayResize:新しいデータに合わせてNewsTime[]配列のサイズを変更します。DatabaseReadBindによって返される行ごとに、配列は1要素ずつ増加します。
- NewsTime[i]=ReadDB_Data:取得したデータをNewsTime[]配列にコピーします。
クエリを確定します。
DatabaseFinalize(request);
- DatabaseFinalize:DatabasePrepareによって割り当てられたリソースをクリーンアップし、クエリハンドルを解放します。
EconomicNextEvent関数は、次に予定されている経済ニュースイベントを識別し、その詳細でUpcomingNews変数を更新する役割を担います。
- CalendarArray内のすべてのイベントを調べ、サーバー時間(TimeTradeServer())に基づいて次に予定されているイベントを見つけます。
- 次のイベントの詳細でUpcomingNews構造体を更新します。
- このロジックにより、将来のイベント(サーバー時間を基準として)のみが考慮され、最も近い将来のイベントが選択されます。
//+------------------------------------------------------------------+ //|Will update UpcomingNews structure variable with the next news | //|event data | //+------------------------------------------------------------------+ void CNews::EconomicNextEvent() { //--- Declare unassigned Calendar structure variable Next Calendar Next; //--- assign empty values to Calendar structure variable UpcomingNews UpcomingNews = Next; //--- assign default date datetime NextEvent=0; //--- Iterate through CalendarArray to retrieve news events for(uint i=0;i<CalendarArray.Size();i++) { //--- Check for next earliest news event from CalendarArray if((NextEvent==0)||(TimeTradeServer()<datetime(CalendarArray[i].EventDate) &&NextEvent>datetime(CalendarArray[i].EventDate))||(NextEvent<TimeTradeServer())) { //--- assign values from the CalendarArray NextEvent = datetime(CalendarArray[i].EventDate); Next = CalendarArray[i]; } } //--- assign the next news event data into UpcomingNews variable UpcomingNews = Next; }
未割り当てのカレンダー構造を宣言します。
Calendar Next;
- 変数「Next」は、カレンダー構造(イベントの日付、名前、国などのイベントの詳細を保持するカスタム構造)として宣言されます。
- 最初は割り当てられておらず、後で次の経済イベントのデータが保持されます。
UpcomingNewsに空の値を割り当てます。
UpcomingNews = Next;
- UpcomingNewsは、次に予定されているイベントの詳細を格納するグローバルまたはクラスレベルの変数(別のカレンダー構造体)です。
- 開始時に、Next変数のデフォルト(空)値にリセットされます。
デフォルト日付を割り当てます。
datetime NextEvent = 0;
- NextEvent変数は0に初期化されます。これは、まだイベントが割り当てられていないことを意味します。
- NextEventは、ループ中に次の経済イベントのタイムスタンプ(datetime形式)を保存します。
CalendarArrayを反復処理します。
for (uint i = 0; i < CalendarArray.Size(); i++)
- CalendarArrayは経済イベントの詳細を保持する配列です。
- forループは、この配列の各要素(それぞれがイベントを表す)を反復処理し、そのイベントが次に発生するイベントとして適格かどうかを確認します。
次のイベントの条件を確認します。
if ((NextEvent == 0) || (TimeTradeServer() < datetime(CalendarArray[i].EventDate) && NextEvent > datetime(CalendarArray[i].EventDate)) || (NextEvent < TimeTradeServer()))
このif文はいくつかの条件をチェックして、配列(CalendarArray[i])内の現在のイベントが次のニュースイベントであるかどうかを判断します。
- 1番目の条件:(NextEvent == 0)
- NextEventがまだ0(イベントがまだ割り当てられていない)の場合、現在のイベントが次のイベントとして選択されます。
- 2番目の条件:(TimeTradeServer() < datetime(CalendarArray[i].EventDate) && NextEvent > datetime(CalendarArray[i].EventDate))
- 配列(CalendarArray[i])内の現在のイベントが現在のサーバー時間(TimeTradeServer())以降に発生し、NextEventに現在格納されているイベントよりも早いかどうかを確認します。trueの場合、現在のイベントが次のイベントになります。
- 3番目の条件:(NextEvent < TimeTradeServer())
- 現在NextEventに格納されているイベントがすでに発生している場合(過去である場合)、関数は有効な将来のイベントの検索を続行します。
CalendarArrayから値を割り当てます。
NextEvent = datetime(CalendarArray[i].EventDate);
Next = CalendarArray[i];
- いずれかの条件が満たされた場合、CalendarArray[i]の現在のイベントが次のイベントとして決定されます。
- NextEventは現在のイベントのイベント日付に更新されます。
- Nextが更新され、現在のイベントのすべての詳細(日付、名前、タイプなど)が保持されます。
次のイベントをUpcomingNewsに割り当てます。
UpcomingNews = Next;
- ループがCalendarArrayの反復処理を終了すると、変数UpcomingNewsが次の予定イベントの詳細(Nextに格納)で更新されます。
- この関数は、現在のサーバー時間を基準にして最初に見つかった将来のイベントがUpcomingNewsに保存されることを保証します。
関数「isEvent」では、ニュースイベントが特定の時間範囲内でこれから発生するか、または現在発生しているかを確認します。 この関数の目的は、時間オフセットに基づいて、CalendarArrayのニュースイベントが発生中か、または発生しようとしているかどうかを確認することです。そのようなイベントが見つかった場合は、イベントの名前、重要度、コードなどの詳細情報が提供されます。
- CalendarArray(経済イベントのデータを保持)を反復処理し、SecondsPreEventで定義された時間範囲内でイベントが発生しているかどうか、または発生しようとしているかどうかを確認します。
- そのようなイベントが見つかった場合は、イベントの詳細で名前、重要度、コードを更新し、trueを返します。
- 定義された時間範囲内にイベントが見つからない場合は、falseを返し、名前、重要度、コードは変更されません(またはデフォルト/NULLに設定されます)。
//+------------------------------------------------------------------+ //|Checks if News is event is about to occur or is occurring | //+------------------------------------------------------------------+ bool CNews::isEvent(uint SecondsPreEvent,string &Name,string &Importance,string &Code) { //--- assign default value Name=NULL; //--- Iterate through CalendarArray for(uint i=0;i<CalendarArray.Size();i++) { //--- Check if news event is within a timespan if(CTime.TimeIsInRange(CTime.TimeMinusOffset(datetime(CalendarArray[i].EventDate),SecondsPreEvent), CTime.TimePlusOffset(datetime(CalendarArray[i].EventDate),59))) { //--- assign appropriate CalendarArray values Name=CalendarArray[i].EventName; Importance=CalendarArray[i].EventImportance; Code=CalendarArray[i].EventCode; //--- news event is currently within the timespan return true; } } //--- no news event is within the current timespan. return false; }
パラメータ
- uintSecondsPreEvent:イベントが「近づいている」と見なされるイベント前の秒数
- string &Name:定義された時間範囲内にある場合にイベントの名前を格納する文字列への参照
- string &Importance:見つかった場合にイベントの重要度レベルを格納する文字列への参照
- string &Code:イベントコードを格納する文字列への参照
名前にデフォルト値を割り当てます。
Name = NULL;
- 変数「Name」はNULLに初期化されます。指定された時間範囲内にイベントが見つからない場合、NameはNULLのままになります。
- これにより、時間範囲内にイベントが見つかった場合にのみNameが更新されます。
CalendarArrayを反復処理します。
for (uint i = 0; i < CalendarArray.Size(); i++)
- ループはCalendarArray内のすべての要素を処理します。各要素はニュースイベントを表します。
- ループは各イベントをチェックし、それが現在の時刻を中心とした指定された時間範囲内にあるかどうかを確認します。
イベントが時間範囲内にあるかどうかを確認します。
if (CTime.TimeIsInRange(CTime.TimeMinusOffset(datetime(CalendarArray[i].EventDate), SecondsPreEvent), CTime.TimePlusOffset(datetime(CalendarArray[i].EventDate), 59)))
- CTime.TimeMinusOffset(datetime(CalendarArray[i].EventDate),SecondsPreEvent)
- この関数呼び出しは、イベントの日付(CalendarArray[i].EventDate)からSecondsPreEvent値を引いた値が過去かどうかを確認します。
- 基本的に、時間ウィンドウの下限(イベントの何秒前)を定義します。
- CTime.TimePlusOffset(datetime(CalendarArray[i].EventDate), 59)
- この関数は、イベントの日付に59秒を加えた日付が将来であるかどうかを確認し、時間ウィンドウの上限(イベントがアクティブであると見なされる期間)を定義します。
- 関数CTime.TimeIsInRangeは、現在の時刻がこの時間範囲内にあるかどうかを確認します。現在の時刻がこの範囲内にある場合、イベントがこれから発生するか、すでに発生していることを意味します。
CalendarArrayから値を割り当てます。
Name = CalendarArray[i].EventName; Importance = CalendarArray[i].EventImportance; Code = CalendarArray[i].EventCode;
- イベントが指定された時間範囲内にあることが判明した場合、関連する詳細(イベント名、重要度、コード)がCalendarArrayから抽出され、参照パラメータに割り当てられます。
- Name:ニュースイベントの名前
- Importance:ニュースイベントの重要度レベル
- Code:イベントコード
イベントが見つかった場合はtrueを返します。
return true;
- 時間範囲内にニュースイベントが見つかった場合、関数はtrueを返し、関連するイベントが現在発生中であるか、または発生しようとしていることを示します。
イベントが見つからない場合はfalseを返します。
return false;
- ループがCalendarArray内のすべてのイベントの反復処理を終了し、指定された時間範囲内にイベントが見つからない場合、関数はfalseを返し、関連するイベントが発生していないことを示します。
コンストラクタ「CNews::CNews(void)」
- 経済指標カレンダーのさまざまなコンポーネントに対してSQLDROP、CREATE、INSERT文を設定して、クラスを初期化します。
- Tables:AutoDST、Record、TimeSchedule、MQL5Calendarなどのテーブルを定義します。
- Views:さまざまなDSTスケジュール(Calendar_AU、Calendar_UKなど)、イベント情報、通貨、最近/今後のイベントの日付のビューを定義します。
- Triggers:OnlyOne_AutoDSTやOnlyOne_Recordなどのトリガーを定義して、特定のテーブルにレコードが1つだけ存在するようにします。
各コンポーネント(テーブル、ビュー、トリガー)は、レコードの作成、挿入、削除など、それぞれのSQLコマンドで初期化されます。
SQLビューを作成します。
- 各DSTスケジュールおよびカレンダーコンポーネントごとに、さまざまな基準(イベントの重要度、通貨、イベントの日付など)に従ってデータを構造化するための特定のSQLビューが定義されています。以下は例です。
- 今後のイベントを表示:今後のイベントの日付、曜日、イベントの詳細を表示します。
- 最近のイベントを表示:今後のイベントに似ていますが、最近のイベントの日付を取得します。
SQLトリガー
- トリガーは、挿入操作の前に既存のレコードを削除することで、AutoDSTテーブルとRecordテーブルに常に1つのレコードだけが存在するようにするために使用されます。
//+------------------------------------------------------------------+ //|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 { // ... string views[] = {"AU","NONE","UK","US"}; //-- Sql statement for creating the table views for each DST schedule string view_sql = "CREATE VIEW IF NOT EXISTS Calendar_%s " "AS " "SELECT C.Eventid as 'ID',C.Eventname as 'Name',C.Country as 'Country', " "(CASE WHEN Date(REPLACE(T.DST_%s,'.','-'))<R.Date THEN CONCAT(T.DST_%s,' | Yesterday') " "WHEN Date(REPLACE(T.DST_%s,'.','-'))=R.Date THEN CONCAT(T.DST_%s,' | Today') " "WHEN Date(REPLACE(T.DST_%s,'.','-'))>R.Date THEN CONCAT(T.DST_%s,' | Tomorrow') END) as " "'Date',C.EventCurrency as 'Currency',Replace(C.EventImportance,'CALENDAR_IMPORTANCE_','')" " as 'Importance' from MQL5Calendar C,Record R Inner join TimeSchedule T on C.ID=T.ID Where" " DATE(REPLACE(T.DST_%s,'.','-'))>=DATE(R.Date,'-1 day') AND DATE(REPLACE(T.DST_%s,'.','-'))" "<=DATE(R.Date,'+1 day') Order by T.DST_%s Asc;"; // ... //--- initializing properties for the EventInfo view CalendarContents[5].Content = EventInfo_View; CalendarContents[5].name = "Event Info"; CalendarContents[5].sql = "CREATE VIEW IF NOT EXISTS 'Event Info' " "AS SELECT DISTINCT MC.EVENTID as 'ID',MC.COUNTRY as 'Country',MC.EVENTNAME as 'Name'," "REPLACE(MC.EVENTTYPE,'CALENDAR_TYPE_','') as 'Type',REPLACE(MC.EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector'," "REPLACE(MC.EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') as 'Importance',MC.EVENTCURRENCY as 'Currency'," "REPLACE(MC.EVENTFREQUENCY,'CALENDAR_FREQUENCY_','') as 'Frequency',MC.EVENTCODE as 'Code' " "FROM MQL5Calendar MC ORDER BY \"Country\" Asc," "CASE \"Importance\" WHEN 'HIGH' THEN 1 WHEN 'MODERATE' THEN 2 WHEN 'LOW' THEN 3 ELSE 4 END,\"Sector\" Desc;"; CalendarContents[5].tbl_name = "Event Info"; CalendarContents[5].type = "view"; // ... //--- initializing properties for the UpcomingEventInfo view CalendarContents[7].Content = UpcomingEventInfo_View; CalendarContents[7].name = "Upcoming Event Dates"; CalendarContents[7].sql = "CREATE VIEW IF NOT EXISTS 'Upcoming Event Dates' AS WITH UNIQUE_EVENTS AS(SELECT DISTINCT M.EVENTID as 'E_ID'," "M.COUNTRY as 'Country',M.EVENTNAME as 'Name',M.EVENTCURRENCY as 'Currency' FROM 'MQL5Calendar' M)," "INFO_DATE AS(SELECT E_ID,Country,Name,Currency,(SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M," "Record R INNER JOIN TIMESCHEDULE T ON T.ID=M.ID WHERE DATE(REPLACE(Time,'.','-'))>R.Date AND " "E_ID=M.EVENTID ORDER BY Time ASC LIMIT 1) as 'Next Event Date' FROM UNIQUE_EVENTS) SELECT E_ID " "as 'ID',Country,Name,Currency,(CASE WHEN \"Next Event Date\" IS NULL THEN 'Unknown' ELSE " "\"Next Event Date\" END) as 'Upcoming Date',(CASE WHEN \"Next Event Date\"<>'Unknown' THEN " "(case cast (strftime('%w', DATE(REPLACE(\"Next Event Date\",'.','-'))) as integer) WHEN 0 THEN" " 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday'" " WHEN 5 THEN 'Friday' ELSE 'Saturday' END) ELSE 'Unknown' END) as 'Day' FROM INFO_DATE Order BY " "\"Upcoming Date\" ASC;"; CalendarContents[7].tbl_name = "Upcoming Event Dates"; CalendarContents[7].type = "view"; //--- initializing properties for the RecentEventInfo view CalendarContents[8].Content = RecentEventInfo_View; CalendarContents[8].name = "Recent Event Dates"; CalendarContents[8].sql = "CREATE VIEW IF NOT EXISTS 'Recent Event Dates' AS WITH UNIQUE_EVENTS AS(SELECT DISTINCT M.EVENTID" " as 'E_ID',M.COUNTRY as 'Country',M.EVENTNAME as 'Name',M.EVENTCURRENCY as 'Currency'" "FROM 'MQL5Calendar' M),INFO_DATE AS(SELECT E_ID,Country,Name,Currency," "(SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M,Record R INNER JOIN TIMESCHEDULE T ON" " T.ID=M.ID WHERE DATE(REPLACE(Time,'.','-'))<=R.Date AND E_ID=M.EVENTID ORDER BY Time DESC" " LIMIT 1) as 'Last Event Date' FROM UNIQUE_EVENTS) SELECT E_ID as 'ID',Country,Name,Currency" ",\"Last Event Date\" as 'Recent Date',(case cast (strftime('%w', DATE(REPLACE(\"Last Event Date\"" ",'.','-'))) as integer) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN" " 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END) as 'Day' FROM INFO_DATE" " Order BY \"Recent Date\" DESC;"; CalendarContents[8].tbl_name = "Recent Event Dates"; CalendarContents[8].type = "view"; // ...
以下のクエリは、類似しているが異なるCalendar_AU、Calendar_NONE、Calendar_UK、Calendar_USの4つのビューを作成します。 これらの各ビューは、3つのテーブル(MQL5Calendar、Record、TimeSchedule)からデータを取得します。 これらのクエリは、さまざまなタイムゾーン(オーストラリア、なし/デフォルト、英国、米国)のイベントの詳細(ID、名前、国、日付、通貨、重要度)を表示するビューを作成します。各イベントの日付は、現在の日付を基準として昨日、今日、明日のいずれに発生したかに基づいてラベル付けされ、結果は現在の日付から1日以内に発生するイベントを表示するようにフィルターされます。ビューは、対応するタイムゾーンのイベント日付でソートされます。
Calendar_AUビューを使用して説明します。
以下が完全なクエリです。
CREATE VIEW IF NOT EXISTS Calendar_AU AS SELECT C.Eventid as 'ID',C.Eventname as 'Name',C.Country as 'Country', (CASE WHEN Date(REPLACE(T.DST_AU,'.','-'))<R.Date THEN CONCAT(T.DST_AU,' | Yesterday') WHEN Date(REPLACE(T.DST_AU,'.','-'))=R.Date THEN CONCAT(T.DST_AU,' | Today') WHEN Date(REPLACE(T.DST_AU,'.','-'))>R.Date THEN CONCAT(T.DST_AU,' | Tomorrow') END) as 'Date', C.EventCurrency as 'Currency',Replace(C.EventImportance,'CALENDAR_IMPORTANCE_','') as 'Importance' from MQL5Calendar C, Record R Inner join TimeSchedule T on C.ID=T.ID Where DATE(REPLACE(T.DST_AU,'.','-'))>=DATE(R.Date,'-1 day') AND DATE(REPLACE(T.DST_AU,'.','-'))<=DATE(R.Date,'+1 day') Order by T.DST_AU Asc;
CREATE VIEW IF NOT EXISTS Calendar_AU:「Calendar_AU」ビューがまだ存在しない場合は作成します。ビューは基本的にクエリに基づいて作成される仮想テーブルであり、データを再度保存せずに取得できます。
SELECT句
SELECT C.Eventid as 'ID', C.Eventname as 'Name', C.Country as 'Country', (CASE WHEN Date(REPLACE(T.DST_AU,'.','-')) < R.Date THEN CONCAT(T.DST_AU, ' | Yesterday') WHEN Date(REPLACE(T.DST_AU,'.','-')) = R.Date THEN CONCAT(T.DST_AU, ' | Today') WHEN Date(REPLACE(T.DST_AU,'.','-')) > R.Date THEN CONCAT(T.DST_AU, ' | Tomorrow') END) as 'Date', C.EventCurrency as 'Currency', Replace(C.EventImportance,'CALENDAR_IMPORTANCE_','') as 'Importance'
クエリのこの部分は、MQL5Calendar、Record、およびTimeScheduleテーブルから特定のフィールドを選択し、それに応じてデータをフォーマットします。
- C.EventidはイベントIDです。
- C.Eventnameはイベント名です。
- C.Countryはイベントに関連付けられた国です。
- CASE文を使用して、日付DST_AU(オーストラリアのタイムゾーン、TimeScheduleに保存)とR.Date(Recordテーブルの現在の日付)を比較し、イベントに「昨日」、「今日」、または「明日」のラベルを付けます。
- C.EventCurrencyは、イベントに関連する通貨です。
- Replace(C.EventImportance,'CALENDAR_IMPORTANCE_','')は、EventImportanceフィールドからプレフィックス'CALENDAR_IMPORTANCE_'を削除し、関連する重要度レベル(例:「HIGH」または「LOW」)のみを抽出します。
FROM句
FROM MQL5Calendar C, Record R Inner join TimeSchedule T on C.ID=T.ID
- クエリは3つのテーブルからデータを取得します。MQL5Calendar(C)、Record(R)、およびTimeSchedule(T)。
- MQL5CalendarテーブルとRecordテーブルはFROM句の一部であり、TimeScheduleテーブルは条件C.ID=T.IDでINNERJOINを使用して結合されます。つまり、MQL5CalendarテーブルのIDはTimeScheduleテーブルのIDと一致する必要があります。
WHERE句
WHERE DATE(REPLACE(T.DST_AU,'.','-')) >= DATE(R.Date,'-1 day') AND DATE(REPLACE(T.DST_AU,'.','-')) <= DATE(R.Date,'+1 day')
- これにより、DST_AU(オーストラリア時間帯のイベント日付)が現在の日付(R.Date)より1日前または1日後のイベントのみが含まれるように結果がフィルターされます。
ORDER BY句
ORDER BY T.DST_AU Asc;
- オーストラリア時間の日付であるDST_AUに基づいてイベントを昇順に並べ替えます。
重要な概念
- 日付と時刻の書式設定:REPLACE()関数は、日付を表す文字列として保存されているDST_列内のピリオド(.)をハイフン(-)に置き換えるために使用されます(例:2024.09.23は2024-09-23に変換されます)。
- 条件付きロジック:CASE文は、イベントの日付(DST_AU、DST_NONE、DST_UK、DST_US)が現在の日付(R.Date)より前か、等しいか、後かを確認し、それに応じてラベル(「Yesterday」、「Today」、「Tomorrow」)を追加します。
- フィルター:WHERE句により、現在の日付の1日前または1日後の範囲内のイベントのみがビューに含まれるようになります。
- 重要度の抽出:REPLACE()関数は、EventImportance列からプレフィックスを削除し、関連する重要度レベル(「HIGH」、「MEDIUM」、「LOW」など)のみを表示します。
以下は、「Calendar_AU」ビューの出力データです。
ID Name Country Date Currency Importance 392080012 Autumnal Equinox Day Japan 2024.09.22 02:00 | Yesterday JPY NONE 554010007 Exports New Zealand 2024.09.23 00:45 | Today NZD LOW 554010008 Imports New Zealand 2024.09.23 00:45 | Today NZD LOW // ... 710010010 Heritage Day South Africa 2024.09.24 02:00 | Tomorrow ZAR NONE 36030005 RBA Rate Statement Australia 2024.09.24 07:30 | Tomorrow AUD MODERATE // ...
以下のクエリは、MQL5Calendarテーブルからイベント情報を選択して整理する「Event Info」ビューを作成します。 このビューは、MQL5Calendarテーブルから個別のイベント情報を抽出し、読み取りと分析を容易にするためにデータをフォーマットします。不要なプレフィックス(CALENDAR_TYPE_、CALENDAR_SECTOR_など)を削除してフィールド名をクリーンアップします。
CREATE VIEW IF NOT EXISTS 'Event Info' AS SELECT DISTINCT MC.EVENTID as 'ID',MC.COUNTRY as 'Country',MC.EVENTNAME as 'Name', REPLACE(MC.EVENTTYPE,'CALENDAR_TYPE_','') as 'Type',REPLACE(MC.EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector', REPLACE(MC.EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') as 'Importance',MC.EVENTCURRENCY as 'Currency', REPLACE(MC.EVENTFREQUENCY,'CALENDAR_FREQUENCY_','') as 'Frequency',MC.EVENTCODE as 'Code' FROM MQL5Calendar MC ORDER BY "Country" Asc,CASE "Importance" WHEN 'HIGH' THEN 1 WHEN 'MODERATE' THEN 2 WHEN 'LOW' THEN 3 ELSE 4 END,"Sector" Desc;
CREATE VIEW IF NOT EXISTS 'Event Info'
この部分では、「Event Info」ビューがまだ存在しない場合は作成します。SQLのビューはSELECTクエリの結果に基づく仮想テーブルであり、複雑なクエリをカプセル化してテーブルのように参照することができます。
SELECT DISTINCT句
SELECT DISTINCT MC.EVENTID as 'ID', MC.COUNTRY as 'Country', MC.EVENTNAME as 'Name', REPLACE(MC.EVENTTYPE,'CALENDAR_TYPE_','') as 'Type', REPLACE(MC.EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector', REPLACE(MC.EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') as 'Importance', MC.EVENTCURRENCY as 'Currency', REPLACE(MC.EVENTFREQUENCY,'CALENDAR_FREQUENCY_','') as 'Frequency', MC.EVENTCODE as 'Code'
このセクションでは、MQL5Calendarテーブル(MCはこのテーブルの別名)から一意の行(重複を削除)を取得し、特定の列を選択してビューを形成します。
選択したフィールド
- MC.EVENTID as 'ID':各イベントの一意のIDを取得し、名前を「ID」に変更します。
- MC.COUNTRY as 'Country':イベントに関連付けられた国を取得します。
- MC.EVENTNAME as 'Name':イベント名を取得します。
REPLACE()を使用したデータ変換
いくつかのフィールドでは、REPLACE()関数を使用して、CALENDAR_TYPE_、CALENDAR_SECTOR_、CALENDAR_IMPORTANCE_、CALENDAR_FREQUENCY_などのプレフィックスを削除し、フィールドの意味のある部分のみを残します。
- REPLACE(MC.EVENTTYPE,'CALENDAR_TYPE_','') as 'Type':よりクリーンな値を取得するために、EVENTTYPEフィールドからCALENDAR_TYPE_プレフィックスを削除します(たとえば、CALENDAR_TYPE_CONSUMERの代わりにCONSUMERを取得します)。
- REPLACE(MC.EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector':セクター名を取得するには、EVENTSECTORフィールドからCALENDAR_SECTOR_プレフィックスを削除します。
- REPLACE(MC.EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') as 'Importance':EVENTIMPORTANCEフィールドからCALENDAR_IMPORTANCE_プレフィックスを削除して、重要度レベル(HIGH、MODERATE、LOWなど)を表示します。
- MC.EVENTCURRENCY as 'Currency':イベントに関係する通貨を取得します。
- REPLACE(MC.EVENTFREQUENCY,'CALENDAR_FREQUENCY_','') as 'Frequency':EVENTFREQUENCYフィールドからCALENDAR_FREQUENCY_プレフィックスを削除し、イベントの頻度(MONTHLYまたはQUARTERLYなど)を指定します。
- MC.EVENTCODE as 'Code':変換せずにイベントコードを取得します。
FROM句
FROM MQL5Calendar MC
クエリは、MCという別名のMQL5Calendarテーブルからデータを取得します。
ORDER BY句
ORDER BY "Country" Asc, CASE "Importance" WHEN 'HIGH' THEN 1 WHEN 'MODERATE' THEN 2 WHEN 'LOW' THEN 3 ELSE 4 END, "Sector" Desc
このセクションでは、ビューの出力を整理するために、複数のフィールドでデータを並べ替えます。
Countryによる順序付け(昇順)
- 最初の並べ替え基準は、Countryの昇順(Asc)です。つまり、イベントはCountryフィールドごとにグループ化され、アルファベット順に並べ替えられます。
Importanceによる順序付け(カスタム順序付け)
- CASE文は、カスタム優先度に基づいて Importanceレベルを並べ替えます。
- 重要度「HIGH」には値1(最高の優先度)が割り当てられます。
- 重要度「MODERATE」には値2が割り当てられます。
- 重要度「LOW」には値3が割り当てられます。
- その他の値(例:「NONE」)には、最も低い優先度である4の値が割り当てられます。
つまり、重要度がHIGHのイベントが最初に表示され、次にMODERATE、LOW、そしてNONEの重要度のイベントが表示されます。
Sector別順序付け(降順)
- 最後に、イベントは、Sectorフィールドによって降順(Desc)で並べられます。これにより、MONEY、CONSUMERなどのセクターが逆アルファベット順に並べられる可能性があります。
以下は、「Event Info」ビューの出力データサンプルです。
ID Country Name Type Sector Importance Currency Frequency Code 36030008 Australia RBA Interest Rate Decision INDICATOR MONEY HIGH AUD NONE AU 36030006 Australia RBA Governor Lowe Speech EVENT MONEY HIGH AUD NONE AU 36010003 Australia Employment Change INDICATOR JOBS HIGH AUD MONTH AU // ... 36010036 Australia Current Account INDICATOR TRADE MODERATE AUD QUARTER AU 36010011 Australia Trade Balance INDICATOR TRADE MODERATE AUD MONTH AU 36010029 Australia PPI q/q INDICATOR PRICES MODERATE AUD QUARTER AU // ... 36010009 Australia Exports m/m INDICATOR TRADE LOW AUD MONTH AU 36010010 Australia Imports m/m INDICATOR TRADE LOW AUD MONTH AU 36010037 Australia Net Exports Contribution INDICATOR TRADE LOW AUD QUARTER AU // ... 76020002 Brazil BCB Interest Rate Decision INDICATOR MONEY HIGH BRL NONE BR // ...
以下のクエリは、「Recent Event Dates」ビューを作成します。このビューには、MQL5Calendarテーブルの最新のイベントの概要と、イベントが発生した曜日が表示されます。 このビューには、MQL5Calendarテーブルの最近のイベントのリストと、イベントが発生した曜日が表示されます。カレンダー内の各イベントの最新のイベントに焦点を当てます。
CREATE VIEW IF NOT EXISTS 'Recent Event Dates' AS WITH UNIQUE_EVENTS AS(SELECT DISTINCT M.EVENTID as 'E_ID',M.COUNTRY as 'Country', M.EVENTNAME as 'Name',M.EVENTCURRENCY as 'Currency'FROM 'MQL5Calendar' M),INFO_DATE AS(SELECT E_ID,Country,Name,Currency, (SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M,Record R INNER JOIN TIMESCHEDULE T ON T.ID=M.ID WHERE DATE(REPLACE(Time,'.','-')) <=R.Date AND E_ID=M.EVENTID ORDER BY Time DESC LIMIT 1) as 'Last Event Date' FROM UNIQUE_EVENTS) SELECT E_ID as 'ID',Country,Name, Currency,"Last Event Date" as 'Recent Date',(case cast (strftime('%w', DATE(REPLACE("Last Event Date",'.','-'))) as integer) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END) as 'Day' FROM INFO_DATE Order BY "Recent Date" DESC;
CREATE VIEW IF NOT EXISTS 'Recent Event Dates'
この部分では、「Recent Event Dates」ビューがまだ存在しない場合は作成します。
WITH句:共通テーブル式(CTE)
この部分では、クエリロジックを中間ステップに分割して簡素化する2つの共通テーブル式(CTE)を定義します。
CTE 1:UNIQUE_EVENTS
WITH UNIQUE_EVENTS AS ( SELECT DISTINCT M.EVENTID as 'E_ID', M.COUNTRY as 'Country', M.EVENTNAME as 'Name', M.EVENTCURRENCY as 'Currency' FROM 'MQL5Calendar' M )
- このCTE (UNIQUE_EVENTS)は、MQL5Calendarテーブルから個別のイベントを抽出します。
- イベントのID (EVENTID)、国、名前、通貨を選択し、各イベントが1回だけリストされるようにします(DISTINCTは重複するエントリを削除します)。
UNIQUE_EVENTSで選択された列
- M.EVENTID as 'E_ID':一意のイベントID
- M.COUNTRY as 'Country':イベントに関連付けられた国
- M.EVENTNAME as 'Name':イベントの名前
- M.EVENTCURRENCY as 'Currency':イベントに関連付けられた通貨
CTE2:INFO_DATE
INFO_DATE AS ( SELECT E_ID, Country, Name, Currency, ( SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M, Record R INNER JOIN TimeSchedule T ON T.ID = M.ID WHERE DATE(REPLACE(Time, '.', '-')) <= R.Date AND E_ID = M.EVENTID ORDER BY Time DESC LIMIT 1 ) as 'Last Event Date' FROM UNIQUE_EVENTS )
このCTE (INFO_DATE)は、前のCTEからの個別のイベントに日付フィールド(最後のイベント日付)を追加します。これは次のようにおこなわれます。
- 一意のイベント(E_ID、Country、Name、Currency)ごとに、TimeScheduleテーブルとMQL5Calendarテーブルから最新のイベント日付を選択します。
INFO_DATEのフィールドサブクエリの説明
- サブクエリは、タイムスタンプまたはイベント時間を表すフィールドDST_NONEをTimeScheduleテーブル(MQL5CalendarテーブルおよびRecordテーブルと結合)から取得します。
- 条件「DATE(REPLACE(Time,'.','-'))<=R.Date」は、イベントの日付(Time、ピリオド(.)をハイフン(-)に置き換えて有効な日付形式にした後)がRecordテーブルの現在の日付(R.Date)以下であることを確認します。
- イベントは時間の降順(ORDER BY Time DESC)でソートされ、LIMIT1により最新のイベント時間のみが取得されます。
- E_ID:UNIQUE_EVENTSCTEからのイベントID
- Country:UNIQUE_EVENTSCTEからの国
- Name:UNIQUE_EVENTSCTEからのイベント名
- Currency:UNIQUE_EVENTSCTEからの通貨
- Last Event Date:MQL5CalendarおよびTimeScheduleテーブルから取得された、各イベントの最新のイベント日付
メインクエリ:最終選別と変革
SELECT E_ID as 'ID', Country, Name, Currency, "Last Event Date" as 'Recent Date', ( CASE CAST (strftime('%w', DATE(REPLACE("Last Event Date", '.', '-'))) AS INTEGER) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END ) as 'Day' FROM INFO_DATE ORDER BY "Recent Date" DESC;
クエリの主要部分では、INFO_DATECTEから最終結果が選択され、次のフィールドが含まれます。
- E_ID as 'ID':イベントIDを「ID」に名前変更
- Country:イベントに関連付けられた国
- Name:イベント名
- Currency:イベントに関連する通貨
- "Last Event Date" as 'Recent Date':最新のイベント日付。「Recent Date」に名前変更。
曜日の計算
- このクエリは、「strftime('%w', DATE(REPLACE("Last Event Date", '.', '-')))」を使用して、LastEventDateを有効な日付形式に変換し、曜日を取得します。
- %wは曜日を表す整数を返します。0=日曜日、1=月曜日、...、6=土曜日です。
- CASE文は、整数を適切な曜日名にマッピングします(例:0->日曜日、1->月曜日など)。
結果の並べ替え
ORDER BY "Recent Date" DESC
結果は「Recent Date」の降順で並べ替えられ、最新のイベントがリストの一番上に表示されます。
以下は、「Recent Event Dates」ビューの出力データサンプルです。
ID Country Name Currency Recent Date Day 554520001 New Zealand CFTC NZD Non-Commercial Net Positions NZD 2024.09.27 21:30 Friday 999520001 European Union CFTC EUR Non-Commercial Net Positions EUR 2024.09.27 21:30 Friday 392520001 Japan CFTC JPY Non-Commercial Net Positions JPY 2024.09.27 21:30 Friday // ...
以下のクエリは、MQL5Calendarテーブルから今後のイベントをリストするための「Upcoming Event Dates」ビューを作成します。イベントの次の日付と曜日が含まれます。クエリには2つの主要な部分があります。最初に一意のイベントを識別し、次にそれらのイベントごとに次に予定されているイベントの日付を決定します。
CREATE VIEW IF NOT EXISTS 'Upcoming Event Dates' AS WITH UNIQUE_EVENTS AS(SELECT DISTINCT M.EVENTID as 'E_ID',M.COUNTRY as 'Country',M.EVENTNAME as 'Name',M.EVENTCURRENCY as 'Currency' FROM 'MQL5Calendar' M),INFO_DATE AS(SELECT E_ID,Country,Name,Currency,(SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M,Record R INNER JOIN TIMESCHEDULE T ON T.ID=M.ID WHERE DATE(REPLACE(Time,'.','-'))>R.Date AND E_ID=M.EVENTID ORDER BY Time ASC LIMIT 1) as 'Next Event Date' FROM UNIQUE_EVENTS) SELECT E_ID as 'ID',Country,Name,Currency,(CASE WHEN "Next Event Date" IS NULL THEN 'Unknown' ELSE "Next Event Date" END) as 'Upcoming Date',(CASE WHEN "Next Event Date"<>'Unknown' THEN (case cast (strftime('%w', DATE(REPLACE("Next Event Date",'.','-'))) as integer) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END) ELSE 'Unknown' END) as 'Day' FROM INFO_DATE Order BY "Upcoming Date" ASC;
CREATE VIEW IF NOT EXISTS 'Upcoming Event Dates'
これにより、「Upcoming Event Dates」ビューが作成されますが、これはまだ存在しない場合のみです。
WITH句:共通テーブル式(CTE)
クエリでは共通テーブル式(CTE)が使用されます。これは、複雑なクエリをより単純で再利用可能な部分に分割するのに役立ちます。ここには、UNIQUE_EVENTSとINFO_DATEの2つのCTEがあります。
CTE 1:UNIQUE_EVENTS
WITH UNIQUE_EVENTS AS ( SELECT DISTINCT M.EVENTID as 'E_ID', M.COUNTRY as 'Country', M.EVENTNAME as 'Name', M.EVENTCURRENCY as 'Currency' FROM 'MQL5Calendar' M )
- この部分は、MQL5Calendarテーブルから一意のイベントを選択します。
- イベントのID (EVENTID)、国、名前、通貨を取得し、DISTINCTを使用して各イベントが1回だけリストされるようにします。
このCTEの結果は、関連する詳細を含む個別のイベントのセットです。
以下は、UNIQUE_EVENTSの列です。
- E_ID:イベントの一意のID
- Country:イベントが関連付けられている国
- Name:イベントの名前
- Currency:イベントに関連付けられた通貨
CTE2:INFO_DATE
INFO_DATE AS ( SELECT E_ID, Country, Name, Currency, ( SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M, Record R INNER JOIN TimeSchedule T ON T.ID=M.ID WHERE DATE(REPLACE(Time, '.', '-')) > R.Date AND E_ID = M.EVENTID ORDER BY Time ASC LIMIT 1 ) as 'Next Event Date' FROM UNIQUE_EVENTS )
このCTE (INFO_DATE)は、一意のイベントごとに次のイベント日付を取得します。
- 各イベント(E_ID、Country、Name、Currency)について、TimeScheduleテーブル(DST_NONEフィールド)で今後のイベント日付を検索します。
サブクエリの説明
- サブクエリは、イベントの時刻または日付を表すDST_NONEフィールドをTimeScheduleテーブルから取得します。
- 条件「DATE(REPLACE(Time, '.', '-')) > R.Date」は、将来のイベント(現在の日付R.Dateよりも後の日付)のみが選択されるようにします。
- イベントは時間の昇順(ORDER BY Time ASC)で並べ替えられるため、最も早いイベント日付が選択されます(LIMIT1により、1つの日付のみが返されます)。
以下は、INFO_DATEの列です。
- E_ID:UNIQUE_EVENTSからのイベントID
- Country:イベントの開催国
- Name:イベント名
- Currency:イベントの通貨
- Next Event Date:サブクエリによって決定される次のイベントの日付
メインクエリ:最終選別と変革
SELECT E_ID as 'ID', Country, Name, Currency, (CASE WHEN "Next Event Date" IS NULL THEN 'Unknown' ELSE "Next Event Date" END) as 'Upcoming Date', (CASE WHEN "Next Event Date" <> 'Unknown' THEN (CASE CAST (strftime('%w', DATE(REPLACE("Next Event Date", '.', '-'))) AS INTEGER) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END) ELSE 'Unknown' END) as 'Day' FROM INFO_DATE ORDER BY "Upcoming Date" ASC;
メインクエリは、INFO_DATECTEから最終出力を取得し、結果を変換して、イベントの日付が欠落している可能性がある場合(NULL値)を処理するための追加ロジックを追加します。
選択された列
- E_ID as 'ID':イベントIDを「ID」に名前変更
- Country:イベントに関連付けられた国
- Name:イベント名
- Currency:イベントに関連する通貨
- 今後の予定:これは「Next Event Date」に基づいています。「Next Event Date」がNULLの場合、「Unknown」と表示され、それ以外の場合は日付が表示されます。
CASE WHEN "Next Event Date" IS NULL THEN 'Unknown' ELSE "Next Event Date" END
このCASE文は、有効な今後のイベント日付があるかどうかを確認します。日付がNULLの場合、「Unknown」を出力します。それ以外の場合は、「Next Event Date」が表示されます。
曜日の計算
- 「Next Event Date」が「Unknown」でない場合、クエリは次のCASE文を使用して、今後の日付を曜日に変換します。
CASE CAST (strftime('%w', DATE(REPLACE("Next Event Date", '.', '-'))) AS INTEGER) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END
「strftime('%w',...)」関数は、「Next Event Date」から曜日を抽出します。
- %wは曜日を表す整数を返します(0=日曜日、1=月曜日など)。
- CASE文は、この整数を曜日名にマッピングします(例:0->日曜日)。
- 「Next Event Date」が「Unknown」の場合、日付列にも「Unknown」と表示されます。
結果の順序
ORDER BY "Upcoming Date" ASC;
結果は「Upcoming Date」の昇順で並べ替えられ、最も早い今後のイベントが最初に表示されます。
以下は、「Upcoming Date」ビューの出力データサンプルです。
ID Country Name Currency Upcoming Date Day 410020004 South Korea Industrial Production y/y KRW 2024.09.30 01:00 Monday 410020005 South Korea Retail Sales m/m KRW 2024.09.30 01:00 Monday 410020006 South Korea Index of Services m/m KRW 2024.09.30 01:00 Monday // ... 36500001 Australia S&P Global Manufacturing PMI AUD 2024.10.01 01:00 Tuesday 392030007 Japan Unemployment Rate JPY 2024.10.01 01:30 Tuesday 392050002 Japan Jobs to Applicants Ratio JPY 2024.10.01 01:30 Tuesday // ...
EAコード
これは、ニュース取引戦略を実装するメインプログラムファイルです。以下のコードでは、カスタムニュースイベントの取引の入力設定が可能です。
input Choice iCustom_Event_1=No;//USE EVENT IDs BELOW? input string iCustom_Event_1_IDs="";//EVENT IDs[Separate with a comma][MAX 14] input Choice iCustom_Event_2=No;//USE EVENT IDs BELOW? input string iCustom_Event_2_IDs="";//EVENT IDs[Separate with a comma][MAX 14] input Choice iCustom_Event_3=No;//USE EVENT IDs BELOW? input string iCustom_Event_3_IDs="";//EVENT IDs[Separate with a comma][MAX 14] input Choice iCustom_Event_4=No;//USE EVENT IDs BELOW? input string iCustom_Event_4_IDs="";//EVENT IDs[Separate with a comma][MAX 14] input Choice iCustom_Event_5=No;//USE EVENT IDs BELOW? input string iCustom_Event_5_IDs="";//EVENT IDs[Separate with a comma][MAX 14]
各入力の説明
Choice iCustom_Event_1=No;
- 型:Choice
- 変数名:iCustom_Event_1
- デフォルト値:No
- 説明:この入力により、ユーザーはイベント1のカスタムイベントIDの使用を有効または無効にできます。ここでの選択肢タイプは、YesまたはNoの2つの値を持つ列挙型です。Yesに設定すると、プログラムは対応する文字列入力(iCustom_Event_1_IDs)で提供されるイベントIDを使用します。
string iCustom_Event_1_IDs="";
- 型:文字列
- 変数名:iCustom_Event_1_IDs
- デフォルト値:空の文字列""
- 説明:この入力により、ユーザーはカスタムイベント1のイベントIDのリストを入力できます。これらのIDはコンマで区切られることが想定されており(例:"36010006,840030005,840030016")、最大14個のIDが指定されます。
カスタムニュースイベントを初期化します。
CEvent1.useEvents = Answer(iCustom_Event_1); StringSplit(iCustom_Event_1_IDs, ',', CEvent1.EventIds); CEvent2.useEvents = Answer(iCustom_Event_2); StringSplit(iCustom_Event_2_IDs, ',', CEvent2.EventIds); CEvent3.useEvents = Answer(iCustom_Event_3); StringSplit(iCustom_Event_3_IDs, ',', CEvent3.EventIds); CEvent4.useEvents = Answer(iCustom_Event_4); StringSplit(iCustom_Event_4_IDs, ',', CEvent4.EventIds); CEvent5.useEvents = Answer(iCustom_Event_5); StringSplit(iCustom_Event_5_IDs, ',', CEvent5.EventIds);
このコードブロックは、いくつかのカスタムニュースイベントオブジェクト(CEvent1、CEvent2など)を初期化し、それらの関連IDを処理します。
- CEvent1.useEvents = Answer(iCustom_Event_1);
- CEvent1は、特定のカスタムニュースイベントセットに関する情報を保持する構造体です。useEventsフラグはAnswer()関数を使用して設定され、入力変数iCustom_Event_1に基づいてブール値(trueまたはfalse)を返します。
- iCustom_Event_1がtrueの場合、EAは取引ロジックにこのカスタムイベントを使用します。それ以外の場合は無視されます。
- StringSplit(iCustom_Event_1_IDs, ',', CEvent1.EventIds);
- StringSplit()は、イベントIDの文字列(iCustom_Event_1_IDs)をカンマ(,)で分割する関数で、結果のイベントIDのリストはCEvent1.EventIdsに格納されます。
- iCustom_Event_1_IDsは1つ以上のイベントIDを含む文字列であり、split関数はこの文字列をイベントIDの配列に変換し、取引ロジックでさらに使用できるようにします。
CEvent2からCEvent5までの同様のコード。
OnInit()関数
これは、EAが起動されるかチャートに追加されたときに呼び出される初期化関数です。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Assign if in LightMode or not isLightMode=(iDisplayMode==Display_LightMode)?true:false; //--- call function for common initialization procedure InitCommon(); //--- store Init result int InitResult; if(!MQLInfoInteger(MQL_TESTER))//Checks whether the program is in the strategy tester { //--- initialization procedure outside strategy tester InitResult=InitNonTester(); } else { //--- initialization procedure inside strategy tester InitResult=InitTester(); } //--- Create DB in memory NewsObject.CreateEconomicDatabaseMemory(); //--- Initialize Candle properties pointer object CP = new CCandleProperties(); //--- Retrieve news events for the current Daily period into array CalendarArray NewsObject.EconomicDetailsMemory(CalendarArray,CTM.Time(TimeTradeServer(),0,0,0), (iOrderType!=StopOrdersType)?true:false); //--- Initialize Common graphics class pointer object CGraphics = new CCommonGraphics(Answer(iDisplay_Date),Answer(iDisplay_Spread), Answer(iDisplay_NewsInfo),Answer(iDisplay_EventObj)); CGraphics.GraphicsRefresh(iSecondsPreEvent);//-- Create chart objects //--- Set Time CDay.SetmyTime(CalendarArray); /* create timer, if in the strategy tester set the timer to 30s else 100ms */ EventSetMillisecondTimer((!MQLInfoInteger(MQL_TESTER))?100:30000); //-- Initialize Trade Management class pointer object Trade = new CTradeManagement(iDeviation); //--- return Init result return InitResult; }
主な手順
表示モード
- EAは、ライトモードまたはダークモード(isLightMode)のどちらで実行されているかを確認します。
共通の初期化
- 一般的な初期化タスクを実行するためにInitCommon()関数を呼び出します。
ストラテジーテスターチェック
- MQLInfoInteger(MQL_TESTER)を使用してEAがストラテジーテスターモードで実行されているかどうかを確認し、結果に応じてInitNonTester()またはInitTester()のいずれかを呼び出します。
ニュースイベントデータベース
- NewsObject.CreateEconomicDatabaseMemory()関数は、経済イベントのメモリ内データベースを初期化します。これはEAがニュース関連のデータを保存する場所です。
ローソク足のプロパティを初期化します。
- CCandlePropertiesのクラスポインタCPが作成されます。このクラスは、始値、終値、高値、安値などのローソク足のプロパティを管理する役割を担います。
ニュースイベントを取得します。
- NewsObject.EconomicDetailsMemory()関数は、選択した基準に基づいて関連するニュースイベントを取得します。現在の取引日のニュースイベントをフィルターします。
グラフィカルオブジェクトを初期化します。
- ニュースイベントデータの視覚化など、チャート上にグラフィカル要素を作成する役割を担うCGraphicsクラスが初期化されます。GraphicsRefresh()メソッドは、構成された時間(iSecondsPreEvent)に従ってグラフィカルオブジェクトが更新されることを保証します。
時間とタイマーを設定します。
- CDay.SetmyTime()メソッドは、ニュースイベント配列を処理し、取引の時間管理を行います。
- EAがテスターで実行されているかライブモード(EventSetMillisecondTimer())で実行されているかに応じて、異なる間隔でタイマーが設定されます。これは、ストラテジーテスターでEAをテストするときにパフォーマンスを提供するためです。
取引管理を初期化します。
- Tradeクラスポインタは、必要に応じて逆指値注文取引を開くためにiDeviationで初期化されます。
初期化結果を返します。
- この関数は初期化プロセスの結果を返します。
OnTimer関数
OnTimer()関数は定期的にトリガーされ、タイマーイベントが呼び出されるたびに実行されます。
//+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { if(((!MQLInfoInteger(MQL_TESTER))?int(TimeTradeServer())%30==0:true)) { //--- Store start-up time. static datetime Startup_date = TimeTradeServer(); if(CTM.DateisToday(Startup_date)&&CP.NewCandle(0,PERIOD_D1) &&MQLInfoInteger(MQL_TESTER)) { //--- Retrieve news events for the current Daily period into array CalendarArray NewsObject.EconomicDetailsMemory(CalendarArray,CTM.Time(TimeTradeServer(),0,0,0), (iOrderType!=StopOrdersType)?true:false); //--- Initialize Common graphics class pointer object CGraphics = new CCommonGraphics(Answer(iDisplay_Date),Answer(iDisplay_Spread), Answer(iDisplay_NewsInfo),Answer(iDisplay_EventObj)); CGraphics.GraphicsRefresh(iSecondsPreEvent);//-- Create chart objects //--- Set Time CDay.SetmyTime(CalendarArray); } //--- Run procedures ExecutionOnTimer(Startup_date); if(CTS.isSessionStart()&&!CTS.isSessionEnd()) { //--- function to open trades TradeTime(); } //--- close trades within 45 min before end of session if(CTS.isSessionStart()&&CTS.isSessionEnd()&&!CTS.isSessionEnd(0,0)) { Trade.CloseTrades("NewsTrading"); } } }
主な特長
- 時間ベースの条件:if文は、コードがストラテジーテスターで実行されているかリアルタイムで実行されているかを確認します。リアルタイムで実行している場合(ストラテジーテスターの外)、サーバー時間が30で割り切れるかどうか(30秒間隔)を確認します。
- ローソク足とニュースの最新情報
- 今日の日付がStartup_dateと一致する場合。今日が新しい日であり、新しい日足ローソク足が形成されている場合(CP.NewCandle(0,PERIOD_D1))、EAはNewsObject.EconomicDetailsMemoryを使用して当日の経済ニュースを取得します。
- また、新しい情報でチャートも更新されます。
- セッション制御:取引セッションが開始された場合(CTS.isSessionStart())、セッション中の取引が許可されます。セッションが終了に近づいている場合も、EAは取引を終了します。
ExecutionOnTimer関数
//+------------------------------------------------------------------+ //|Execute program procedures in time intervals | //+------------------------------------------------------------------+ void ExecutionOnTimer(datetime Startup_date) { //--- Check if not start-up date if(!CTM.DateisToday(Startup_date)) { //--- Run every New Daily Candle if(CP.NewCandle(1,PERIOD_D1)) { //--- Check if not in strategy tester if(!MQLInfoInteger(MQL_TESTER)) { //--- Update/Create DB in Memory NewsObject.CreateEconomicDatabaseMemory(); } //--- retrieve news events for the current day NewsObject.EconomicDetailsMemory(CalendarArray,CTM.Time(TimeTradeServer(),0,0,0), (iOrderType!=StopOrdersType)?true:false); //--- Set time from news events CDay.SetmyTime(CalendarArray); CGraphics.GraphicsRefresh(iSecondsPreEvent);//-- Create/Re-create chart objects } //--- Check if not in strategy tester if(!MQLInfoInteger(MQL_TESTER)) { //--- Run every New Hourly Candle if(CP.NewCandle(2,PERIOD_H1)) { //--- Check if DB in Storage needs an update if(NewsObject.UpdateRecords()) { //--- initialization procedure outside strategy tester InitNonTester(); } } } } else { //--- Run every New Daily Candle if(CP.NewCandle(3,PERIOD_D1)) { //--- Update Event objects on chart CGraphics.NewsEvent(); } } //--- Update realtime Graphic every 1 min if(CP.NewCandle(4,PERIOD_M1)) { //--- get the news events for the next min ahead of time. datetime Time_ahead = TimeTradeServer()+CTM.MinutesS(); CDay.GetmyTime(CTV.Hourly(CTM.ReturnHour(Time_ahead)), CTV.Minutely(CTV.Minutely(CTM.ReturnMinute(Time_ahead))), myTimeData,myEvents); CGraphics.Block_2_Realtime(iSecondsPreEvent); } }
主な特長
- 日足更新:新しい日足をチェックします。新しい日足ローソク足が形成された場合、ニュースデータベースが更新され(NewsObject.CreateEconomicDatabaseMemory())、その日のニュースイベントが取得されます。
- 時間足更新:新しい時間足が形成されると、EAは経済ニュースデータベースの更新が必要かどうかを確認し、必要な場合は、ストラテジーテスターモードの外部でEAを再初期化します(InitNonTester())。
- リアルタイム更新:EAは毎分チャート上のリアルタイムグラフィックを更新し、次の1分間のニュースイベントを取得して、それに応じて取引の準備をします。
TradeTime関数
この関数は、ニュースイベントに関する取引の実行を管理します。
//+------------------------------------------------------------------+ //|function to check trading time | //+------------------------------------------------------------------+ void TradeTime() { //--- Iterate through the event times for(uint i=0;i<myTimeData.Size();i++) { //--- Check if it is time to trade each news event if(CTM.TimePreEvent(CTM.TimeMinusOffset(datetime(myEvents[i].EventDate),iSecondsPreEvent) ,datetime(myEvents[i].EventDate)) &&(CTM.isDayOfTheWeek(TradingDay)||iNewSelection==News_Select_Custom_Events)) { //--- switch for order type selection switch(iOrderType) { case StopOrdersType:// triggers for STOP ORDERS StopOrders(myEvents[i]); break; default:// triggers for both MARKET POSITION & SINGLE STOP ORDER SingleOrder(myEvents[i]); break; } } } }
主な特長
- イベント時間の確認:myTimeData配列内の各イベントについて、EAは、現在の時刻が実際のイベントの前に事前定義された「イベント前」時間ウィンドウ(iSecondsPreEvent)内にあるかどうかを確認します。
- 曜日フィルター:取引は、現在の日が設定された取引日(TradingDay)と一致する場合、またはカスタムイベントが選択されている(iNewSelection==News_Select_Custom_Events)場合にのみ開始されます。
- 注文タイプの選択:iOrderType入力(市場ポジションまたは逆指値注文)に基づいて、関数はニュースイベントの周囲で市場注文または逆指値注文を開きます。
SingleOrder関数
この関数は、ニュースイベントの影響に基づいて、単一成り行き注文または逆指値注文を開きます。
//+------------------------------------------------------------------+ //|function to open single order types | //+------------------------------------------------------------------+ void SingleOrder(Calendar &NewsEvent) { //--- Check each Impact value type switch(NewsObject.IMPACT(NewsEvent.EventImpact)) { //--- When Impact news is negative case CALENDAR_IMPACT_NEGATIVE: //--- Check if profit currency is news event currency if(NewsEvent.EventCurrency==CSymbol.CurrencyProfit()) { switch(iOrderType) { case MarketPositionType:// triggers for MARKET POSITION //--- Open buy trade with Event id as Magic number Trade.Buy(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-"+NewsEvent.EventCode); break; case StopOrderType:// triggers for SINGLE STOP ORDER //--- Open buy-stop with Event id as Magic number Trade.OpenBuyStop(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-SStop-"+NewsEvent.EventCode); break; default: break; } } else { switch(iOrderType) { case MarketPositionType:// triggers for MARKET POSITION //--- Open sell trade with Event id as Magic number Trade.Sell(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-"+NewsEvent.EventCode); break; case StopOrderType:// triggers for SINGLE STOP ORDER //--- Open buy-stop with Event id as Magic number Trade.OpenSellStop(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-SStop-"+NewsEvent.EventCode); break; default: break; } } break; //--- When Impact news is positive case CALENDAR_IMPACT_POSITIVE: //--- Check if profit currency is news event currency if(NewsEvent.EventCurrency==CSymbol.CurrencyProfit()) { switch(iOrderType) { case MarketPositionType:// triggers for MARKET POSITION //--- Open sell trade with Event id as Magic number Trade.Sell(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-"+NewsEvent.EventCode); break; case StopOrderType:// triggers for SINGLE STOP ORDER //--- Open sell-stop with Event id as Magic number Trade.OpenSellStop(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-SStop-"+NewsEvent.EventCode); break; default: break; } } else { switch(iOrderType) { case MarketPositionType:// triggers for MARKET POSITION //--- Open buy trade with Event id as Magic number Trade.Buy(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-"+NewsEvent.EventCode); break; case StopOrderType:// triggers for SINGLE STOP ORDER //--- Open sell-stop with Event id as Magic number Trade.OpenBuyStop(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-SStop-"+NewsEvent.EventCode); break; default: break; } } break; //--- Unknown default: break; } }
主な特長
- 影響評価:EAはニュースイベントの影響(NewsObject.IMPACT(NewsEvent.EventImpact))を評価します。これは、マイナスまたはプラスのいずれかになります。
- マイナスの影響:ニュースの影響がマイナスで、イベント通貨が口座の利益通貨と一致する場合、iOrderTypeが市場ポジションの場合は買い取引が開始され、逆指値注文の場合は買い逆指値注文が開始されます。イベント通貨が利益通貨と一致しない場合は、売り取引または売り逆指値注文が開かれます。
- プラスの影響:ニュースの影響がプラスで、イベント通貨が利益通貨と一致する場合、売り取引または売り逆指値注文が開始されます。イベント通貨が一致しない場合は、買い取引または買い逆指値注文が開かれます。
StopOrders関数
この関数は、ニュースイベントの前後で買い逆指値注文と売り逆指値注文の両方を同時に出します。イベントの影響に関係なく、それぞれの方向の価格変動も捕捉するために両タイプの逆指値注文が出されます。
//+------------------------------------------------------------------+ //|function to open orders | //+------------------------------------------------------------------+ void StopOrders(Calendar &NewsEvent) { //--- Opens both buy-stop & sell-stop regardless of event impact Trade.OpenStops(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-Stops-"+NewsEvent.EventCode); }
主な特長
- 買い逆指値注文と売り逆指値注文:両タイプの逆指値注文を開きます。各逆指値注文は特定のイベントにリンクされており、イベントのEventIdが取引のマジックナンバーとして使用されます。
OnTrade関数
この関数は、新しい取引イベントが発生するたびにトリガーされます。この関数は取引を管理するために使用されます。
void OnTrade() { //--- Check if time is within the trading session if(CTS.isSessionStart() && !CTS.isSessionEnd(0,0)) { //--- Run procedures ExecutionOnTrade(); } }
- CTS.isSessionStart()と!CTS.isSessionEnd(0,0)
- CTS(セッションクラスオブジェクト)は、現在の時刻がアクティブな取引セッション内であるかどうかを確認するために使用されます。
- isSessionStart()はセッションが開始されたかどうかを確認します。
- 「!CTS.isSessionEnd(0,0)」はセッションがまだ終了していないかどうかを確認します。引数「0,0」は、セッションが終了する前のオフセットまたはバッファ期間を表します。
- これにより、現在の時刻がアクティブな取引セッション内にある場合にのみ取引が調整されるようになります。
- ExecutionOnTrade();
- セッションがアクティブな場合、新しい取引に関連する必要な手順の実行を処理するためにExecutionOnTrade()関数が呼び出されます。
ExecutionOnTrade関数
この関数には、新しい取引が実行されるたびに実行されるロジックが含まれています。
void ExecutionOnTrade() { //--- if stop orders, enable fundamental mode if(iOrderType == StopOrdersType) { Trade.FundamentalMode("NewsTrading"); } //--- when stop order(s), enable slippage reduction if(iOrderType != MarketPositionType) { Trade.SlippageReduction(iStoploss, iTakeprofit, "NewsTrading"); } }
- if (iOrderType == StopOrdersType)
- 現在の取引が逆指値注文(StopOrdersType)であるかどうかを確認します。条件がtrueの場合、コードはTrade.FundamentalMode()関数を呼び出します。
- Trade.FundamentalMode("NewsTrading");
- この関数はファンダメンタルモードを有効にし、反対方向の保留注文を削除する役割を果たします。
- "NewsTrading":EAのトレードラベル/コメント
- if(iOrderType != MarketPositionType)
- この条件は、入力変数が列挙型MarketPositionTypeではないかどうかを確認します。trueの場合、コードはスリップの削減を有効にします。
- Trade.SlippageReduction(iStoploss, iTakeprofit, "NewsTrading")
- この関数は、非市場注文のスリッページを削減します。
- スリッページは、特に不安定な市場やニュースイベントの際に、実行価格が予想価格と異なる場合に発生します。
- SlippageReduction()を呼び出すことによって、EAはこのスリッページを最小限に抑えようとします。
- パラメータ
- iStoploss:ストップロス値。これは、潜在的な損失を制限するために取引が自動的に終了する価格です。
- iTakeprofit:テイクプロフィット値。利益を確保するために取引が自動的に終了する価格です。
- "NewsTrading": EAのトレードラベル/コメント
結論
この記事では、EAにおいて、ユーザーが取引対象となるカスタムニュースイベントを定義できるようにしました。これらのイベントはユーザー入力によって初期化され、その後EAによって解析され、適切に処理されます。ストレージ内のカレンダーデータベースも改良され、今後のイベントや直近のイベントに関する追加情報がビュー形式で提供されるようになりました。各ビューのクエリについても、詳細に解説しています。また、特定のタイミング条件に基づいてコードの実行を制御するために、OnTimer関数およびOnTrade関数が追加されました。お読みいただき、ありがとうございました。次回の記事でも、さらに価値ある内容をお届けできたら嬉しいです :)
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/16170





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索