
DoEasy - サービス関数(第3回):アウトサイドバーパターン
内容
概念
本記事では、DoEasyライブラリにおける価格パターンの処理に関するセクションを続けていきます。
前回の記事では、2本のローソク足からなる「はらみ線」の検索および表示機能を作成しました。今回は、その鏡像的な存在である「アウトサイドバーパターン」の検索処理を構築します。
ただし、両者には違いがあります。はらみ線が双方向パターンで、上方向・下方向いずれにもエントリーできるのに対し、アウトサイドバーは強気と弱気の2つの方向に分類されます。
- BUOVB(強気のアウトサイドバーパターン):シグナルバーが前のバーを完全に包み込み、その終値が前のバーの高値を上回るパターンです。シグナルバーの高値+フィルター(5〜10ポイント)を上抜けたタイミングで買いエントリーをおこないます。
- BEOVB(弱気のアウトサイドバーパターン): シグナルバーが前のバーを完全に包み込み、その終値が前のバーの安値を下回るパターンです。シグナルバーの安値+フィルター(5〜10ポイント)を下抜けたタイミングで売りエントリーをおこないます。
パターンクラスの作成に入る前に、既存のライブラリクラスにいくつか修正を加える必要があります。まず、パターンアクセス用クラス内のすべてのメソッドは、最適な構成とは言えません。これらはもともと、各パターンタイプごとに個別のメソッドを多数用意し、「力技」で実装された概念実証でした。今回は、指定されたパターンに対して処理をおこなう単一のメソッドを作成し、それぞれの操作に対しては専用のメソッドを使い、対象パターンは変数で指定する方式に変更します。これにより、クラスのコードは大幅に簡素化され、可読性や保守性も向上します。
次に、ライブラリの開発が一時中断していた期間に、MQL5言語にいくつかの変更・追加が行われました(なお、発表されたすべての変更が実際に実装されているわけではありません)。これらの仕様変更をライブラリにも取り入れます。また、ライブラリのテスト期間中に発見されたいくつかの不具合やエラーも修正しました。これから、これらすべての改良点について順を追って説明していきます。
ライブラリクラスの改善
特定のパターンを判定する際には、パターン形成に関与する隣接するローソク足同士のサイズ比が重要になる場合があります。そのため、ローソク足のサイズ比率を判定するための新しいプロパティを、パターンのプロパティに追加します。具体的には、各パターンおよびパターン管理クラスに、ローソク足間の比率に基づいて検索をおこなうための数値プロパティを追加します。
また、\MQL5\Include\DoEasy\Defines.mqhライブラリファイルにあるパターンの実数プロパティを定義する列挙型に新たに2つのプロパティを追加し、実数プロパティの総数を10から12に増加させます。
//+------------------------------------------------------------------+ //| Pattern real properties | //+------------------------------------------------------------------+ enum ENUM_PATTERN_PROP_DOUBLE { //--- bar data PATTERN_PROP_BAR_PRICE_OPEN = PATTERN_PROP_INTEGER_TOTAL,// Pattern defining bar Open price PATTERN_PROP_BAR_PRICE_HIGH, // Pattern defining bar High price PATTERN_PROP_BAR_PRICE_LOW, // Pattern defining bar Low price PATTERN_PROP_BAR_PRICE_CLOSE, // Pattern defining bar Close price PATTERN_PROP_RATIO_BODY_TO_CANDLE_SIZE, // Percentage ratio of the candle body to the full size of the candle PATTERN_PROP_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE, // Percentage ratio of the upper shadow size to the candle size PATTERN_PROP_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE, // Percentage ratio of the lower shadow size to the candle size PATTERN_PROP_RATIO_CANDLE_SIZES, // Ratio of pattern candle sizes PATTERN_PROP_RATIO_BODY_TO_CANDLE_SIZE_CRITERION, // Defined criterion of the ratio of the candle body to the full candle size in % PATTERN_PROP_RATIO_LARGER_SHADOW_TO_CANDLE_SIZE_CRITERION, // Defined criterion of the ratio of the maximum shadow to the candle size in % PATTERN_PROP_RATIO_SMALLER_SHADOW_TO_CANDLE_SIZE_CRITERION, // Defined criterion of the ratio of the minimum shadow to the candle size in % PATTERN_PROP_RATIO_CANDLE_SIZES_CRITERION, // Defined criterion for the ratio of pattern candle sizes }; #define PATTERN_PROP_DOUBLE_TOTAL (12) // Total number of real pattern properties #define PATTERN_PROP_DOUBLE_SKIP (0) // Number of pattern properties not used in sorting
MetaTrader 5クライアント端末のベータ版4540以降、ENUM_SYMBOL_SWAP_MODE列挙型にSYMBOL_SWAP_MODE_CURRENCY_PROFITが追加されました。
SymbolInfoInteger()関数がこのような値を返す場合、口座のスワップは利益通貨で計算されます。
この値をライブラリファイルに追加しましょう。 \MQL5\Include\DoEasy\Data.mqhに新しいライブラリメッセージのインデックスを追加します。
MSG_SYM_SWAP_MODE_CURRENCY_MARGIN, // Swaps charged in money in symbol margin currency MSG_SYM_SWAP_MODE_CURRENCY_DEPOSIT, // Swaps charged in money in client deposit currency MSG_SYM_SWAP_MODE_CURRENCY_PROFIT, // Swaps charged in money in profit calculation currency MSG_SYM_SWAP_MODE_INTEREST_CURRENT, // Swaps charged as specified annual interest from symbol price at calculation of swap MSG_SYM_SWAP_MODE_INTEREST_OPEN, // Swaps charged as specified annual interest from position open price
...
MSG_LIB_TEXT_PATTERN_RATIO_BODY_TO_CANDLE_SIZE, // Percentage ratio of the candle body to the full size of the candle MSG_LIB_TEXT_PATTERN_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE, // Percentage ratio of the upper shadow size to the candle size MSG_LIB_TEXT_PATTERN_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE, // Percentage ratio of the lower shadow size to the candle size MSG_LIB_TEXT_PATTERN_RATIO_CANDLE_SIZES, // Ratio of pattern candle sizes MSG_LIB_TEXT_PATTERN_RATIO_BODY_TO_CANDLE_SIZE_CRIT, // Defined criterion of the ratio of the candle body to the full candle size in % MSG_LIB_TEXT_PATTERN_RATIO_LARGER_SHADOW_TO_CANDLE_SIZE_CRIT, // Defined criterion of the ratio of the maximum shadow to the candle size in % MSG_LIB_TEXT_PATTERN_RATIO_SMALLER_SHADOW_TO_CANDLE_SIZE_CRIT, // Defined criterion of the ratio of the minimum shadow to the candle size in % MSG_LIB_TEXT_PATTERN_RATIO_CANDLE_SIZES_CRITERION, // Defined criterion for the ratio of pattern candle sizes MSG_LIB_TEXT_PATTERN_NAME, // Name
以下は、新しく追加されたインデックスに対応するテストメッセージです。
{"Свопы начисляются в деньгах в маржинальной валюте символа","Swaps charged in money in symbol margin currency"}, {"Свопы начисляются в деньгах в валюте депозита клиента","Swaps charged in money in client deposit currency"}, {"Свопы начисляются в деньгах в валюте расчета прибыли","Swaps are charged in money, in profit calculation currency"}, { "Свопы начисляются в годовых процентах от цены инструмента на момент расчета свопа", "Swaps are charged as the specified annual interest from the instrument price at calculation of swap" }, {"Свопы начисляются в годовых процентах от цены открытия позиции по символу","Swaps charged as specified annual interest from open price of position"},
...
{"Отношение размера верхней тени к размеру свечи в %","Ratio of the size of the upper shadow to the size of the candle in %"}, {"Отношение размера нижней тени к размеру свечи в %","Ratio of the size of the lower shadow to the size of the candle in %"}, {"Отношение размеров свечей паттерна","Ratio of pattern candle sizes"}, {"Установленный критерий отношения тела свечи к полному размеру свечи в %","Criterion for the Ratio of candle body to full candle size in %"}, {"Установленный критерий отношения размера наибольшей тени к размеру свечи в %","Criterion for the Ratio of the size of the larger shadow to the size of the candle in %"}, {"Установленный критерий отношения размера наименьшей тени к размеру свечи в %","Criterion for the Ratio of the size of the smaller shadow to the size of the candle in %"}, {"Установленный критерий отношения размеров свечей паттерна","Criterion for the Ratio of pattern candle sizes"}, {"Наименование","Name"},
コード4306と4307の2つの新しいランタイムエラーの説明をランタイムエラーメッセージ配列に追加します。
//+------------------------------------------------------------------+ //| Array of runtime error messages (4301 - 4307) | //| (MarketInfo) | //| (1) in user's country language | //| (2) in the international language | //+------------------------------------------------------------------+ string messages_runtime_market[][TOTAL_LANG]= { {"Неизвестный символ","Unknown symbol"}, // 4301 {"Символ не выбран в MarketWatch","Symbol not selected in MarketWatch"}, // 4302 {"Ошибочный идентификатор свойства символа","Wrong identifier of symbol property"}, // 4303 {"Время последнего тика неизвестно (тиков не было)","Time of the last tick not known (no ticks)"}, // 4304 {"Ошибка добавления или удаления символа в MarketWatch","Error adding or deleting a symbol in MarketWatch"}, // 4305 {"Превышен лимит выбранных символов в MarketWatch","Exceeded the limit of selected symbols in MarketWatch"}, // 4306 { "Неправильный индекс сессии при вызове функции SymbolInfoSessionQuote/SymbolInfoSessionTrade", // 4307 "Wrong session ID when calling the SymbolInfoSessionQuote/SymbolInfoSessionTrade function" }, };
\MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqhで、スワップ計算方法に応じて小数点以下の桁数を返すメソッドに新しい列挙値の処理を追加します。
//+------------------------------------------------------------------+ //| Return the number of decimal places | //| depending on the swap calculation method | //+------------------------------------------------------------------+ int CSymbol::SymbolDigitsBySwap(void) { return ( this.SwapMode()==SYMBOL_SWAP_MODE_POINTS || this.SwapMode()==SYMBOL_SWAP_MODE_REOPEN_CURRENT || this.SwapMode()==SYMBOL_SWAP_MODE_REOPEN_BID ? this.Digits() : this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_SYMBOL || this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_MARGIN || this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_PROFIT || this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT ? this.DigitsCurrency(): this.SwapMode()==SYMBOL_SWAP_MODE_INTEREST_CURRENT || this.SwapMode()==SYMBOL_SWAP_MODE_INTEREST_OPEN ? 1 : 0 ); }
スワップ計算モデルの説明を返すメソッドで、新しいスワップ計算モードの説明を含む文字列の戻り値を入力します。
//+------------------------------------------------------------------+ //| Return the description of a swap calculation model | //+------------------------------------------------------------------+ string CSymbol::GetSwapModeDescription(void) const { return ( this.SwapMode()==SYMBOL_SWAP_MODE_DISABLED ? CMessage::Text(MSG_SYM_SWAP_MODE_DISABLED) : this.SwapMode()==SYMBOL_SWAP_MODE_POINTS ? CMessage::Text(MSG_SYM_SWAP_MODE_POINTS) : this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_SYMBOL ? CMessage::Text(MSG_SYM_SWAP_MODE_CURRENCY_SYMBOL) : this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_MARGIN ? CMessage::Text(MSG_SYM_SWAP_MODE_CURRENCY_MARGIN) : this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT ? CMessage::Text(MSG_SYM_SWAP_MODE_CURRENCY_DEPOSIT) : this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_PROFIT ? CMessage::Text(MSG_SYM_SWAP_MODE_CURRENCY_PROFIT) : this.SwapMode()==SYMBOL_SWAP_MODE_INTEREST_CURRENT ? CMessage::Text(MSG_SYM_SWAP_MODE_INTEREST_CURRENT) : this.SwapMode()==SYMBOL_SWAP_MODE_INTEREST_OPEN ? CMessage::Text(MSG_SYM_SWAP_MODE_INTEREST_OPEN) : this.SwapMode()==SYMBOL_SWAP_MODE_REOPEN_CURRENT ? CMessage::Text(MSG_SYM_SWAP_MODE_REOPEN_CURRENT) : this.SwapMode()==SYMBOL_SWAP_MODE_REOPEN_BID ? CMessage::Text(MSG_SYM_SWAP_MODE_REOPEN_BID) : CMessage::Text(MSG_SYM_MODE_UNKNOWN) ); }
MQL4定義ファイルの\MQL5\Include\DoEasy\ToMQL4.mqhにおいて、ポジション移行時のスワップ計算方法を列挙する列挙型に、新しいプロパティを追加します。
//+------------------------------------------------------------------+ //| Swap charging methods during a rollover | //+------------------------------------------------------------------+ enum ENUM_SYMBOL_SWAP_MODE { SYMBOL_SWAP_MODE_POINTS, // (MQL5 - 1, MQL4 - 0) Swaps are charged in points SYMBOL_SWAP_MODE_CURRENCY_SYMBOL, // (MQL5 - 2, MQL4 - 1) Swaps are charged in money in symbol base currency SYMBOL_SWAP_MODE_INTEREST_OPEN, // (MQL5 - 6, MQL4 - 2) Swaps are charged as the specified annual interest from the open price of position SYMBOL_SWAP_MODE_CURRENCY_MARGIN, // (MQL5 - 3, MQL4 - 3) Swaps are charged in money in margin currency of the symbol SYMBOL_SWAP_MODE_DISABLED, // (MQL5 - 0, MQL4 - N) No swaps SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT, // Swaps are charged in money, in client deposit currency SYMBOL_SWAP_MODE_INTEREST_CURRENT, // Swaps are charged as the specified annual interest from the instrument price at calculation of swap SYMBOL_SWAP_MODE_REOPEN_CURRENT, // Swaps are charged by reopening positions by the close price SYMBOL_SWAP_MODE_REOPEN_BID, // Swaps are charged by reopening positions by the current Bid price SYMBOL_SWAP_MODE_CURRENCY_PROFIT // Swaps charged in money in profit calculation currency };
\MQL5\Include\DoEasy\Objects\BaseObj.mqhライブラリの基本オブジェクトクラスファイルにおいて、メモリリークの可能性がある文字列処理を修正します。
//+------------------------------------------------------------------+ //| Add the event object to the list | //+------------------------------------------------------------------+ bool CBaseObjExt::EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam) { CEventBaseObj *event=new CEventBaseObj(event_id,lparam,dparam,sparam); if(event==NULL) return false; this.m_list_events.Sort(); if(this.m_list_events.Search(event)>WRONG_VALUE) { delete event; return false; } return this.m_list_events.Add(event); } //+------------------------------------------------------------------+ //| Add the object base event to the list | //+------------------------------------------------------------------+ bool CBaseObjExt::EventBaseAdd(const int event_id,const ENUM_BASE_EVENT_REASON reason,const double value) { CBaseEvent* event=new CBaseEvent(event_id,reason,value); if(event==NULL) return false; this.m_list_events_base.Sort(); if(this.m_list_events_base.Search(event)>WRONG_VALUE) { delete event; return false; } return this.m_list_events_base.Add(event); }
ここで、メソッドはオブジェクトをリストに追加した結果を返します(trueは成功、falseはエラー)。new演算子で生成したオブジェクトをリストに追加する際にエラーが発生すると、そのオブジェクトのポインタがリストに保存されないまま、どこかのメモリ上に残ってしまう可能性があります。これによりメモリリークが発生します。これを修正しましょう。
//+------------------------------------------------------------------+ //| Add the event object to the list | //+------------------------------------------------------------------+ bool CBaseObjExt::EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam) { CEventBaseObj *event=new CEventBaseObj(event_id,lparam,dparam,sparam); if(event==NULL) return false; this.m_list_events.Sort(); if(this.m_list_events.Search(event)>WRONG_VALUE || !this.m_list_events.Add(event)) { delete event; return false; } return true; } //+------------------------------------------------------------------+ //| Add the object base event to the list | //+------------------------------------------------------------------+ bool CBaseObjExt::EventBaseAdd(const int event_id,const ENUM_BASE_EVENT_REASON reason,const double value) { CBaseEvent* event=new CBaseEvent(event_id,reason,value); if(event==NULL) return false; this.m_list_events_base.Sort(); if(this.m_list_events_base.Search(event)>WRONG_VALUE || !this.m_list_events_base.Add(event)) { delete event; return false; } return true; }
これで、イベントオブジェクトをリストに追加する際に失敗した場合でも、同一のオブジェクトがすでにリストに存在していた場合と同様に、新たに作成されたオブジェクトは削除され、メソッドはfalseを返すようになります。
\MT5\MQL5\Include\DoEasy\Objects\Orders\Order.mqhにおいて、ポイント数を計算する際には、結果に対して丸め処理を追加する必要があります。そうしないと、1未満であっても1に近い値の場合、小数から整数に変換するとポイント数がゼロになってしまいます。
//+------------------------------------------------------------------+ //| Get order profit in points | //+------------------------------------------------------------------+ int COrder::ProfitInPoints(void) const { MqlTick tick={0}; string symbol=this.Symbol(); if(!::SymbolInfoTick(symbol,tick)) return 0; ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)this.TypeOrder(); double point=::SymbolInfoDouble(symbol,SYMBOL_POINT); if(type==ORDER_TYPE_CLOSE_BY || point==0) return 0; if(this.Status()==ORDER_STATUS_HISTORY_ORDER) return int(type==ORDER_TYPE_BUY ? (::round(this.PriceClose()-this.PriceOpen())/point) : type==ORDER_TYPE_SELL ? ::round((this.PriceOpen()-this.PriceClose())/point) : 0); else if(this.Status()==ORDER_STATUS_MARKET_POSITION) { if(type==ORDER_TYPE_BUY) return (int)::round((tick.bid-this.PriceOpen())/point); else if(type==ORDER_TYPE_SELL) return (int)::round((this.PriceOpen()-tick.ask)/point); } else if(this.Status()==ORDER_STATUS_MARKET_PENDING) { if(type==ORDER_TYPE_BUY_LIMIT || type==ORDER_TYPE_BUY_STOP || type==ORDER_TYPE_BUY_STOP_LIMIT) return (int)fabs(::round((tick.bid-this.PriceOpen())/point)); else if(type==ORDER_TYPE_SELL_LIMIT || type==ORDER_TYPE_SELL_STOP || type==ORDER_TYPE_SELL_STOP_LIMIT) return (int)fabs(::round((this.PriceOpen()-tick.ask)/point)); } return 0; }
\MQL5\Include\DoEasy\Objects\Events\Event.mqhでは、列挙型の値としてlong値を返す際にエラーが発生していました。
//--- When changing position direction, return (1) previous position order type, (2) previous position order ticket, //--- (3) current position order type, (4) current position order ticket, //--- (5) position type and (6) ticket before changing direction, (7) position type and (8) ticket after changing direction ENUM_ORDER_TYPE TypeOrderPosPrevious(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE); } long TicketOrderPosPrevious(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE); } ENUM_ORDER_TYPE TypeOrderPosCurrent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT); } long TicketOrderPosCurrent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT);} ENUM_POSITION_TYPE TypePositionPrevious(void) const { return PositionTypeByOrderType(this.TypeOrderPosPrevious()); } ulong TicketPositionPrevious(void) const { return this.TicketOrderPosPrevious(); } ENUM_POSITION_TYPE TypePositionCurrent(void) const { return PositionTypeByOrderType(this.TypeOrderPosCurrent()); } ulong TicketPositionCurrent(void) const { return this.TicketOrderPosCurrent(); }
long型の注文チケットの値がINT_MAX値(列挙データ型-int)を超えない限り、注文チケットは正しく返されていましたが、チケットの値がINT_MAXを超えた瞬間にオーバーフローが発生し、負の数が返されるようになりました。現在はすべて修正されています。
//--- When changing position direction, return (1) previous position order type, (2) previous position order ticket, //--- (3) current position order type, (4) current position order ticket, //--- (5) position type and (6) ticket before changing direction, (7) position type and (8) ticket after changing direction ENUM_ORDER_TYPE TypeOrderPosPrevious(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE); } long TicketOrderPosPrevious(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE); } ENUM_ORDER_TYPE TypeOrderPosCurrent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT); } long TicketOrderPosCurrent(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT); } ENUM_POSITION_TYPE TypePositionPrevious(void) const { return PositionTypeByOrderType(this.TypeOrderPosPrevious()); } ulong TicketPositionPrevious(void) const { return this.TicketOrderPosPrevious(); } ENUM_POSITION_TYPE TypePositionCurrent(void) const { return PositionTypeByOrderType(this.TypeOrderPosCurrent()); } ulong TicketPositionCurrent(void) const { return this.TicketOrderPosCurrent(); }
注文システムイベントステータス名を返す列挙体に変更ステータスがなく、場合によっては取引イベントの説明にステータスが[Unknown]と表示されていました。文字列を追加して修正しました。
//+------------------------------------------------------------------+ //| Return the event status name | //+------------------------------------------------------------------+ string CEvent::StatusDescription(void) const { ENUM_EVENT_STATUS status=(ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT); return ( status==EVENT_STATUS_MARKET_PENDING ? CMessage::Text(MSG_EVN_STATUS_MARKET_PENDING) : status==EVENT_STATUS_MARKET_POSITION ? CMessage::Text(MSG_EVN_STATUS_MARKET_POSITION) : status==EVENT_STATUS_HISTORY_PENDING ? CMessage::Text(MSG_EVN_STATUS_HISTORY_PENDING) : status==EVENT_STATUS_HISTORY_POSITION ? CMessage::Text(MSG_EVN_STATUS_HISTORY_POSITION) : status==EVENT_STATUS_BALANCE ? CMessage::Text(MSG_LIB_PROP_BALANCE) : status==EVENT_STATUS_MODIFY ? CMessage::Text(MSG_EVN_REASON_MODIFY) : CMessage::Text(MSG_EVN_STATUS_UNKNOWN) ); }
取引オブジェクトクラスにおける見落としを1つ修正しましょう。ポジションを開くメソッドに対して、ボリュームの約定ポリシー(ENUM_ORDER_TYPE_FILLING列挙体からの値)が正しく渡されていませんでした。
この修正のために、MQL5\Include\DoEasy\Objects\Trade\TradeObj.mqhにある取引メソッドを改善しましょう。 ポジションを開くメソッド内の取引リクエスト構造体に値を設定するブロックに、ボリューム約定ポリシーの設定を追加して実装します。
//+------------------------------------------------------------------+ //| Open a position | //+------------------------------------------------------------------+ bool CTradeObj::OpenPosition(const ENUM_POSITION_TYPE type, const double volume, const double sl=0, const double tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { if(this.m_program==PROGRAM_INDICATOR || this.m_program==PROGRAM_SERVICE) return true; ::ResetLastError(); //--- If failed to get the current prices, write the error code and description, send the message to the journal and return 'false' if(!::SymbolInfoTick(this.m_symbol,this.m_tick)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode)); return false; } //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure this.m_request.action = TRADE_ACTION_DEAL; this.m_request.symbol = this.m_symbol; this.m_request.magic = (magic==ULONG_MAX ? this.m_magic : magic); this.m_request.type = (ENUM_ORDER_TYPE)type; this.m_request.price = (type==POSITION_TYPE_BUY ? this.m_tick.ask : this.m_tick.bid); this.m_request.volume = volume; this.m_request.sl = sl; this.m_request.tp = tp; this.m_request.deviation = (deviation==ULONG_MAX ? this.m_deviation : deviation); this.m_request.type_filling= (type_filling>WRONG_VALUE ? type_filling : this.m_type_filling); this.m_request.comment = (comment==NULL ? this.m_comment : comment); //--- Return the result of sending a request to the server #ifdef __MQL5__ return(!this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result)); #else ::ResetLastError(); int ticket=::OrderSend(m_request.symbol,m_request.type,m_request.volume,m_request.price,(int)m_request.deviation,m_request.sl,m_request.tp,m_request.comment,(int)m_request.magic,m_request.expiration,clrNONE); this.m_result.retcode=::GetLastError(); ::SymbolInfoTick(this.m_symbol,this.m_tick); this.m_result.ask=this.m_tick.ask; this.m_result.bid=this.m_tick.bid; this.m_result.comment=CMessage::Text(this.m_result.retcode); if(ticket!=WRONG_VALUE) { this.m_result.deal=ticket; this.m_result.price=(::OrderSelect(ticket,SELECT_BY_TICKET) ? ::OrderOpenPrice() : this.m_request.price); this.m_result.volume=(::OrderSelect(ticket,SELECT_BY_TICKET) ? ::OrderLots() : this.m_request.volume); return true; } else { return false; } #endif }
クラスでは、注文に許可された値を使用してライブラリを初期化する際に、約定ポリシーの値が最初にm_type_filling変数に設定されます(CEngine::TradingSetCorrectTypeFillingメソッド)。openメソッドに負の値の約定ポリシーが渡された場合は、ライブラリの初期化時に設定されたm_type_fillingの値が使用されます。別の約定方法を指定する必要がある場合は、type_fillingメソッドパラメータに希望する値を渡すことで、その値が優先的に使用されます。
以前は追加された文字列が実装されておらず、デフォルト以外のポリシーを指定しても、MqlTradeRequest構造体のtype_fillingフィールドが未設定だったため、常にReturn(ORDER_FILLING_RETURN)が適用されていました。現在はこの問題が修正されています。
このように約定ポリシーが関係する他のメソッドについても、同様の不備があれば修正していきましょう。
//+------------------------------------------------------------------+ //| Close a position | //+------------------------------------------------------------------+ bool CTradeObj::ClosePosition(const ulong ticket, const string comment=NULL, const ulong deviation=ULONG_MAX) { if(this.m_program==PROGRAM_INDICATOR || this.m_program==PROGRAM_SERVICE) return true; ::ResetLastError(); //--- If the position selection failed. Write the error code and error description, send the message to the log and return 'false' if(!::PositionSelectByTicket(ticket)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- If failed to get the current prices, write the error code and description, send the message to the journal and return 'false' if(!::SymbolInfoTick(this.m_symbol,this.m_tick)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode)); return false; } //--- Get a position type and an order type inverse of the position type ENUM_POSITION_TYPE position_type=(ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); ENUM_ORDER_TYPE type=OrderTypeOppositeByPositionType(position_type); //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure this.m_request.action = TRADE_ACTION_DEAL; this.m_request.symbol = this.m_symbol; this.m_request.type = type; this.m_request.magic = ::PositionGetInteger(POSITION_MAGIC); this.m_request.price = (position_type==POSITION_TYPE_SELL ? this.m_tick.ask : this.m_tick.bid); this.m_request.volume = ::PositionGetDouble(POSITION_VOLUME); this.m_request.deviation = (deviation==ULONG_MAX ? this.m_deviation : deviation); this.m_request.comment = (comment==NULL ? this.m_comment : comment); this.m_request.type_filling= this.m_type_filling; //--- In case of a hedging account, write the ticket of a closed position to the structure if(this.IsHedge()) this.m_request.position=::PositionGetInteger(POSITION_TICKET); //--- Return the result of sending a request to the server #ifdef __MQL5__ return(!this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result)); #else ::SymbolInfoTick(this.m_symbol,this.m_tick); this.m_result.ask=this.m_tick.ask; this.m_result.bid=this.m_tick.bid; ::ResetLastError(); if(::OrderClose((int)this.m_request.position,this.m_request.volume,this.m_request.price,(int)this.m_request.deviation,clrNONE)) { this.m_result.retcode=::GetLastError(); this.m_result.deal=ticket; this.m_result.price=(::OrderSelect((int)ticket,SELECT_BY_TICKET) ? ::OrderClosePrice() : this.m_request.price); this.m_result.volume=(::OrderSelect((int)ticket,SELECT_BY_TICKET) ? ::OrderLots() : this.m_request.volume); this.m_result.comment=CMessage::Text(this.m_result.retcode); return true; } else { this.m_result.retcode=::GetLastError(); this.m_result.ask=this.m_tick.ask; this.m_result.bid=this.m_tick.bid; this.m_result.comment=CMessage::Text(this.m_result.retcode); return false; } #endif } //+------------------------------------------------------------------+ //| Close a position partially | //+------------------------------------------------------------------+ bool CTradeObj::ClosePositionPartially(const ulong ticket, const double volume, const string comment=NULL, const ulong deviation=ULONG_MAX) { if(this.m_program==PROGRAM_INDICATOR || this.m_program==PROGRAM_SERVICE) return true; ::ResetLastError(); //--- If the position selection failed. Write the error code and error description, send the message to the log and return 'false' if(!::PositionSelectByTicket(ticket)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,"#",(string)ticket,": ",CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_SELECT_POS),CMessage::Text(this.m_result.retcode)); return false; } //--- If failed to get the current prices, write the error code and description, send the message to the journal and return 'false' if(!::SymbolInfoTick(this.m_symbol,this.m_tick)) { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),CMessage::Text(this.m_result.retcode)); return false; } //--- Get a position type and an order type inverse of the position type ENUM_POSITION_TYPE position_type=(ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE); ENUM_ORDER_TYPE type=OrderTypeOppositeByPositionType(position_type); //--- Get a position volume double position_volume=::PositionGetDouble(POSITION_VOLUME); //--- Clear the structures ::ZeroMemory(this.m_request); ::ZeroMemory(this.m_result); //--- Fill in the request structure this.m_request.action = TRADE_ACTION_DEAL; this.m_request.position = ticket; this.m_request.symbol = this.m_symbol; this.m_request.magic = ::PositionGetInteger(POSITION_MAGIC); this.m_request.type = type; this.m_request.price = (position_type==POSITION_TYPE_SELL ? this.m_tick.ask : this.m_tick.bid); this.m_request.volume = (volume<position_volume ? volume : position_volume); this.m_request.deviation = (deviation==ULONG_MAX ? this.m_deviation : deviation); this.m_request.comment = (comment==NULL ? this.m_comment : comment); this.m_request.type_filling= this.m_type_filling; //--- In case of a hedging account, write the ticket of a closed position to the structure if(this.IsHedge()) this.m_request.position=::PositionGetInteger(POSITION_TICKET); //--- Return the result of sending a request to the server #ifdef __MQL5__ return(!this.m_async_mode ? ::OrderSend(this.m_request,this.m_result) : ::OrderSendAsync(this.m_request,this.m_result)); #else ::SymbolInfoTick(this.m_symbol,this.m_tick); this.m_result.ask=this.m_tick.ask; this.m_result.bid=this.m_tick.bid; ::ResetLastError(); if(::OrderClose((int)this.m_request.position,this.m_request.volume,this.m_request.price,(int)this.m_request.deviation,clrNONE)) { this.m_result.retcode=::GetLastError(); this.m_result.deal=ticket; this.m_result.price=(::OrderSelect((int)ticket,SELECT_BY_TICKET) ? ::OrderClosePrice() : this.m_request.price); this.m_result.volume=(::OrderSelect((int)ticket,SELECT_BY_TICKET) ? ::OrderLots() : this.m_request.volume); this.m_result.comment=CMessage::Text(this.m_result.retcode); return true; } else { this.m_result.retcode=::GetLastError(); this.m_result.comment=CMessage::Text(this.m_result.retcode); return false; } #endif }
ここで、\MQL5\Include\DoEasy\Trading.mqhのライブラリの取引クラスにおける同様の欠落を修正しましょう。
OpenPosition()メソッドでは、メソッドに渡された値に基づいて取引リクエスト構造体に値が入力されます。
//--- Write the volume, deviation, comment and filling type to the request structure this.m_request.volume=volume; this.m_request.deviation=(deviation==ULONG_MAX ? trade_obj.GetDeviation() : deviation); this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment); this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : trade_obj.GetTypeFilling());
銘柄取引オブジェクトのポジションを開くメソッドを呼び出す際、取引リクエスト構造体に設定された値ではなく、ポジションを開くメソッドに渡されたパラメータの値が使用されます。
//--- In the loop by the number of attempts for(int i=0;i<this.m_total_try;i++) { //--- Send a request res=trade_obj.OpenPosition(type,this.m_request.volume,this.m_request.sl,this.m_request.tp,magic,comment,deviation,type_filling); //... ... ...
これを修正しましょう。
//--- Send a request res=trade_obj.OpenPosition(type,this.m_request.volume,this.m_request.sl,this.m_request.tp,magic,this.m_request.comment,this.m_request.deviation,this.m_request.type_filling);
ライブラリを基にしたプログラムが、接続されているチャートだけでなく、開いているすべてのチャート上で発生するイベントを追跡できるようにするため、開かれた(または既に開かれている)各チャートに、チャートイベントを監視してプログラムに送信するインジケーターが配置されます。
たとえば、クライアント端末が最初にあるサーバーに接続され、複数の銘柄のチャートを開いていたとします。その後、端末を別のサーバーに接続し、そこには既に開いていた銘柄が存在しない場合、プログラムはそれらのチャートにインジケーターを配置できず、操作ログには「インジケーター作成エラー」といった曖昧なエラーが記録されます。本来の問題は「チャートの銘柄がサーバーに存在しない」ことなのに、それがログからは分かりません。このような曖昧なログ出力を防ぐため、インジケーターを作成する前に、対象の銘柄が現在接続中のサーバー上に存在するかどうかを確認するチェック処理を追加する必要があります。
そのために、\MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqhにある、イベント用コントロールインジケーターを作成するメソッド内に、サーバー上の銘柄の存在確認をおこなうチェックを追加します。
//+------------------------------------------------------------------+ //| CChartObjectsControl: Create the event control indicator | //+------------------------------------------------------------------+ bool CChartObjectsControl::CreateEventControlInd(const long chart_id_main) { //--- If the symbol is not on the server, return 'false' bool is_custom=false; if(!::SymbolExist(this.Symbol(), is_custom)) { CMessage::ToLog(DFUN+" "+this.Symbol()+": ",MSG_LIB_SYS_NOT_SYMBOL_ON_SERVER); return false; } //--- Create the indicator this.m_chart_id_main=chart_id_main; string name="::"+PATH_TO_EVENT_CTRL_IND; ::ResetLastError(); this.m_handle_ind=::iCustom(this.Symbol(),this.Timeframe(),name,this.ChartID(),this.m_chart_id_main); if(this.m_handle_ind==INVALID_HANDLE) { CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_CREATE_EVN_CTRL_INDICATOR); CMessage::ToLog(DFUN,::GetLastError(),true); return false; } this.m_name_ind="EventSend_From#"+(string)this.ChartID()+"_To#"+(string)this.m_chart_id_main; ::Print ( DFUN,this.Symbol()," ",TimeframeDescription(this.Timeframe()),": ", CMessage::Text(MSG_GRAPH_OBJ_CREATE_EVN_CTRL_INDICATOR)," \"",this.m_name_ind,"\"" ); return true; }
これで、サーバー上に銘柄が存在しない場合は、その旨のメッセージが操作ログに表示され、メソッドはfalseを返すようになりました。
次に、基本グラフィカルオブジェクトのファイルである\MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqhにある、グラフィカル要素の型を返すメソッドに、未定義の型「ビットマップグラフィカルオブジェクト」を追加します。
//+------------------------------------------------------------------+ //| Return the description of the graphical element type | //+------------------------------------------------------------------+ string CGBaseObj::TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type) { return ( type==GRAPH_ELEMENT_TYPE_STANDARD ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD) : type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED) : type==GRAPH_ELEMENT_TYPE_ELEMENT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT) : type==GRAPH_ELEMENT_TYPE_BITMAP ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_BITMAP) : type==GRAPH_ELEMENT_TYPE_SHADOW_OBJ ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ) : type==GRAPH_ELEMENT_TYPE_FORM ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM) : type==GRAPH_ELEMENT_TYPE_WINDOW ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW) : //--- WinForms type==GRAPH_ELEMENT_TYPE_WF_UNDERLAY ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY) : type==GRAPH_ELEMENT_TYPE_WF_BASE ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE) : //--- Containers type==GRAPH_ELEMENT_TYPE_WF_CONTAINER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER) : type==GRAPH_ELEMENT_TYPE_WF_GROUPBOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX) : type==GRAPH_ELEMENT_TYPE_WF_PANEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL) : type==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL) : type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER) : //--- Standard controls type==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE) : type==GRAPH_ELEMENT_TYPE_WF_LABEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL) : type==GRAPH_ELEMENT_TYPE_WF_CHECKBOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX) : type==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON) : type==GRAPH_ELEMENT_TYPE_WF_BUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON) : type==GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM) : type==GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX) : type==GRAPH_ELEMENT_TYPE_WF_TOOLTIP ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TOOLTIP) : type==GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR) : //--- Auxiliary control objects type==GRAPH_ELEMENT_TYPE_WF_TAB_HEADER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_HEADER) : type==GRAPH_ELEMENT_TYPE_WF_TAB_FIELD ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_FIELD) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX) : type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX) : type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL) : type==GRAPH_ELEMENT_TYPE_WF_SPLITTER ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLITTER) : type==GRAPH_ELEMENT_TYPE_WF_HINT_BASE ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_BASE) : type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT) : type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT) : type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP) : type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN) : type==GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR) : type==GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ) : type==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR) : type==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_VERTICAL) : type==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL) : type==GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB) : "Unknown" ); }
また、パターンクラスの構成と構造を少し見直します。基本となる抽象パターンクラスは\MQL5\Include\DoEasy\Objects\Series\Patterns\Pattern.mqhにあり、そこに基底クラスから継承された各パターンクラスも記述されています。
このすべてのパターンクラスを、個別のファイルに分割しましょう。これまで、パターンアイコンはチャート上に通常のポイントとして表示されており、これは価格と時間の座標を1本のバーに持たせた標準のトレンドラインオブジェクトを使って描画されていましたが、コードの簡素化のためこの機能は廃止することにしました。今後すべてのアイコンは、ビットマップグラフィカルオブジェクト上に描画されるようになります。パターンの描画は、パターンを構成するバーのサイズに基づいておこなわれます。チャートの水平スケールを変更する際に描画オブジェクトのサイズも自動的に調整するため、スケール値を保持する変数を導入する必要があります。その他のチャートサイズを保持するための変数は、すでにベースのパターンオブジェクトに含まれています。チャートのサイズが変更されると、検出されたすべてのパターンオブジェクトに対して新しいサイズが適用され、それに応じて再描画されます。
その準備として、\MT5\MQL5\Include\DoEasy\Objects\Series\Patterns\フォルダ内に、2つの新しいファイルPatternPinBar.mqhとPatternInsideBar.mqhを作成し、現在基底抽象パターンクラス内に記述されているピンバーおよびはらみ線パターンのクラスをカットアンドペーストでそれぞれのファイルに移動します。今後これらに個別の変更を加えていく予定ですが、まずは抽象パターンクラスの編集を続けます。
具体的には、抽象クラスのprotectedセクションから、パターンアイコンをドットで描画するためのフラグ変数m_draw_dotsを削除し、代わりにチャートの横幅をピクセル単位で保持する変数を新たに宣言します。
protected: CForm *m_form; // Pointer to form object CGCnvBitmap *m_bitmap; // Pointer to the bitmap object int m_digits; // Symbol's digits value ulong m_symbol_code; // Symbol as a number (sum of name symbol codes) string m_name_graph_obj; // Name of the graphical object displaying the pattern double m_price; // Price level the graphical object is placed at color m_color_bullish; // Color of a graphical object set to the bullish pattern icon color m_color_bearish; // Color of a graphical object set to the bearish pattern icon color m_color_bidirect; // Color of a graphical object set to the bidirectional pattern icon color m_color; // Graphical object color color m_color_panel_bullish; // Bullish pattern panel color color m_color_panel_bearish; // Bearish pattern panel color color m_color_panel_bidirect; // Bidirectional pattern panel color int m_bars_formation; // Number of bars in the formation (nested pattern) bool m_draw_dots; // Draw on the chart with dots int m_chart_scale; // Chart scale int m_chart_height_px; // Height of the chart in pixels int m_chart_width_px; // Height of the chart in pixels double m_chart_price_max; // Chart maximum double m_chart_price_min; // Chart minimum public:
以下は、ビットマップオブジェクトの幅と高さを計算するメソッドです。
//--- Calculate the bitmap object (1) width and (2) height int GetBitmapWidth(void); int GetBitmapHeight(void);
正しい名前に変更し、仮想として宣言します。
//--- Calculate the bitmap object (1) width and (2) height virtual int CalculatetBitmapWidth(void); virtual int CalculatetBitmapHeight(void);
ただし、Getは「計算する」ではなく「受け取る」という意味です。仮想メソッドを使用すると、継承されたクラスは、パターンの種類と描画方法に応じて、パターンの幅と高さを独自に計算できるようになります。
クラスのpublicセクションからSetDrawAsDots()メソッドを削除します。
public: //--- Remove a graphical object bool DeleteGraphObj(bool redraw=false); //--- Set graphical object display colors and pattern display color void SetColors(const color color_bullish,const color color_bearish,const color color_bidirect,const bool redraw=false); //--- Set the flag for drawing pattern labels as dots void SetDrawAsDots(const bool flag) { this.m_draw_dots=flag; } //--- Set the background color for the (1) bullish, (2) bearish and (3) bidirectional pattern panel void SetColorPanelBullish(const color clr) { this.m_color_panel_bullish=clr; } void SetColorPanelBearish(const color clr) { this.m_color_panel_bearish=clr; } void SetColorPanelBiDirect(const color clr) { this.m_color_panel_bidirect=clr; } //--- Set the background color for the (1) bullish, (2) bearish and (3) bidirectional pattern panel by setting the values of the RGB color components void SetColorPanelBullish(const uchar R,const uchar G,const uchar B); void SetColorPanelBearish(const uchar R,const uchar G,const uchar B); void SetColorPanelBiDirect(const uchar R,const uchar G,const uchar B); //--- Draw the pattern icon on the chart virtual void Draw(const bool redraw); //--- (1) Display, (2) hide the pattern icon on the chart void Show(const bool redraw=false); void Hide(const bool redraw=false); //--- (1) Display and (2) hide the info panel on the chart void ShowInfoPanel(const int x,const int y,const bool redraw=true); void HideInfoPanel(void);
Redraw()仮想メソッドを宣言します。
public: //--- Remove a graphical object bool DeleteGraphObj(bool redraw=false); //--- Set graphical object display colors and pattern display color void SetColors(const color color_bullish,const color color_bearish,const color color_bidirect,const bool redraw=false); //--- Set the background color for the (1) bullish, (2) bearish and (3) bidirectional pattern panel void SetColorPanelBullish(const color clr) { this.m_color_panel_bullish=clr; } void SetColorPanelBearish(const color clr) { this.m_color_panel_bearish=clr; } void SetColorPanelBiDirect(const color clr) { this.m_color_panel_bidirect=clr; } //--- Set the background color for the (1) bullish, (2) bearish and (3) bidirectional pattern panel by setting the values of the RGB color components void SetColorPanelBullish(const uchar R,const uchar G,const uchar B); void SetColorPanelBearish(const uchar R,const uchar G,const uchar B); void SetColorPanelBiDirect(const uchar R,const uchar G,const uchar B); //--- (1) Draw and (2) resize the pattern icon on the chart virtual bool Draw(const bool redraw); virtual bool Redraw(const bool redraw) { return true; } //--- (1) Display, (2) hide the pattern icon on the chart void Show(const bool redraw=false); void Hide(const bool redraw=false); //--- (1) Display and (2) hide the info panel on the chart void ShowInfoPanel(const int x,const int y,const bool redraw=true); void HideInfoPanel(void);
Redraw()メソッドは、ビットマップオブジェクトを新しい寸法で再描画します。各パターンタイプは独自のビットマップタイプを持つことができるため、メソッドは仮想として宣言され、ここでは単にtrueを返します。継承されたクラスでは、メソッドはオーバーライドされ、指定されたパターンに対して描画されたビットマップを正確に再描画します。
そこで、publicセクションで、チャートの幅をピクセル単位で設定するメソッドと返すメソッドも設定します。
//--- Set the (1) chart scale, (2) height, (3) width in pixels, (4) high, (5) low void SetChartScale(const int scale) { this.m_chart_scale=scale; } void SetChartHeightInPixels(const int height) { this.m_chart_height_px=height; } void SetChartWidthInPixels(const int width) { this.m_chart_width_px=width; } void SetChartPriceMax(const double price) { this.m_chart_price_max=price; } void SetChartPriceMin(const double price) { this.m_chart_price_min=price; } //--- Return the (1) chart scale, (2) height, (3) width in pixels, (4) high, (5) low int ChartScale(void) const { return this.m_chart_scale; } int ChartHeightInPixels(void) const { return this.m_chart_height_px; } int ChartWidthInPixels(void) const { return this.m_chart_width_px; } double ChartPriceMax(void) const { return this.m_chart_price_max; } double ChartPriceMin(void) const { return this.m_chart_price_min; }
チャートの幅を変更すると、パターン管理クラスは新しいチャートのサイズをすべてのパターンオブジェクトに1つずつ書き込みます。そのため、作成された各パターンオブジェクトではチャートのプロパティは取得されませんが、サイズが変更されたときに新しいサイズが1回だけ取得され、作成されたすべてのパターンオブジェクトに書き込まれます。
クラスコンストラクタの最後で、不要になった変数を初期化するための文字列を削除します。
//--- Set base colors of the pattern information panels this.m_color_panel_bullish=clrLightGray; this.m_color_panel_bearish=clrLightGray; this.m_color_panel_bidirect=clrLightGray; this.m_form=NULL; this.m_bitmap=NULL; this.m_draw_dots=true; this.m_bars_formation=1; }
パターンの実際のプロパティの説明を返すメソッドに、2つの新しいパターンプロパティの説明を表示するための2つのコードブロックを追加します。
//+------------------------------------------------------------------+ //| Return the description of the pattern real property | //+------------------------------------------------------------------+ string CPattern::GetPropertyDescription(ENUM_PATTERN_PROP_DOUBLE property) { int dg=(this.m_digits>0 ? this.m_digits : 1); return ( property==PATTERN_PROP_BAR_PRICE_OPEN ? CMessage::Text(MSG_LIB_TEXT_PATTERN_BAR_PRICE_OPEN)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==PATTERN_PROP_BAR_PRICE_HIGH ? CMessage::Text(MSG_LIB_TEXT_PATTERN_BAR_PRICE_HIGH)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==PATTERN_PROP_BAR_PRICE_LOW ? CMessage::Text(MSG_LIB_TEXT_PATTERN_BAR_PRICE_LOW)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==PATTERN_PROP_BAR_PRICE_CLOSE ? CMessage::Text(MSG_LIB_TEXT_PATTERN_BAR_PRICE_CLOSE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),dg) ) : property==PATTERN_PROP_RATIO_BODY_TO_CANDLE_SIZE ? CMessage::Text(MSG_LIB_TEXT_PATTERN_RATIO_BODY_TO_CANDLE_SIZE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : property==PATTERN_PROP_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE ? CMessage::Text(MSG_LIB_TEXT_PATTERN_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : property==PATTERN_PROP_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE ? CMessage::Text(MSG_LIB_TEXT_PATTERN_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : property==PATTERN_PROP_RATIO_CANDLE_SIZES ? CMessage::Text(MSG_LIB_TEXT_PATTERN_RATIO_CANDLE_SIZES)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : property==PATTERN_PROP_RATIO_BODY_TO_CANDLE_SIZE_CRITERION ? CMessage::Text(MSG_LIB_TEXT_PATTERN_RATIO_BODY_TO_CANDLE_SIZE_CRIT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : property==PATTERN_PROP_RATIO_LARGER_SHADOW_TO_CANDLE_SIZE_CRITERION ? CMessage::Text(MSG_LIB_TEXT_PATTERN_RATIO_LARGER_SHADOW_TO_CANDLE_SIZE_CRIT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : property==PATTERN_PROP_RATIO_SMALLER_SHADOW_TO_CANDLE_SIZE_CRITERION ? CMessage::Text(MSG_LIB_TEXT_PATTERN_RATIO_SMALLER_SHADOW_TO_CANDLE_SIZE_CRIT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : property==PATTERN_PROP_RATIO_CANDLE_SIZES_CRITERION ? CMessage::Text(MSG_LIB_TEXT_PATTERN_RATIO_CANDLE_SIZES_CRITERION)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(property),2) ) : "" ); }
チャート上に情報パネルを表示するメソッドでは、チャートのプロパティを取得する必要がなくなりました。これは、チャートの作成時またはチャートのサイズが変更されたときに、チャートのプロパティがパターンオブジェクトに既に設定されているためです。
メソッドからチャートのプロパティを受け取るための文字列を削除します。
//+------------------------------------------------------------------+ //| Display the info panel on the chart | //+------------------------------------------------------------------+ void CPattern::ShowInfoPanel(const int x,const int y,const bool redraw=true) { //--- If there is no panel object yet, create it if(this.m_form==NULL) if(!this.CreateInfoPanel()) return; //--- Get the chart width and height int chart_w=(int)::ChartGetInteger(this.m_chart_id,CHART_WIDTH_IN_PIXELS); int chart_h=(int)::ChartGetInteger(this.m_chart_id,CHART_HEIGHT_IN_PIXELS); //--- Calculate the X and Y coordinates of the panel so that it does not go beyond the chart
以前取得していたチャートプロパティの代わりに、オブジェクト作成時に、あらかじめ設定された以下のプロパティが現在は使用されるようになっています。
//+------------------------------------------------------------------+ //| Display the info panel on the chart | //+------------------------------------------------------------------+ void CPattern::ShowInfoPanel(const int x,const int y,const bool redraw=true) { //--- If there is no panel object yet, create it if(this.m_form==NULL) if(!this.CreateInfoPanel()) return; //--- Calculate the X and Y coordinates of the panel so that it does not go beyond the chart int cx=(x+this.m_form.Width() >this.m_chart_width_px-1 ? this.m_chart_width_px-1-this.m_form.Width() : x); int cy=(y+this.m_form.Height()>this.m_chart_height_px-1 ? this.m_chart_height_px-1-this.m_form.Height() : y); //--- Set the calculated coordinates and display the panel if(this.m_form.SetCoordX(cx) && this.m_form.SetCoordY(cy)) this.m_form.Show(); if(redraw) ::ChartRedraw(this.m_chart_id); }
パターンアイコンを描画、表示、非表示するメソッドから、ドットでパターンアイコンを描画することに関連する文字列を削除します。
//+------------------------------------------------------------------+ //| Draw the pattern icon on the chart | //+------------------------------------------------------------------+ void CPattern::Draw(const bool redraw) { //--- If the graphical object has not yet been created, create it if(::ObjectFind(this.m_chart_id,this.m_name_graph_obj)<0) this.CreateTrendLine(this.m_chart_id,this.m_name_graph_obj,0,this.Time(),this.m_price,this.Time(),this.m_price,this.m_color,5); //--- Otherwise - display else this.Show(redraw); } //+------------------------------------------------------------------+ //| Display the pattern icon on the chart | //+------------------------------------------------------------------+ void CPattern::Show(const bool redraw=false) { if(this.m_draw_dots) { ::ObjectSetInteger(this.m_chart_id,this.m_name_graph_obj,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS); return; } if(this.m_bitmap!=NULL) this.m_bitmap.Show(); if(redraw) ::ChartRedraw(this.m_chart_id); } //+------------------------------------------------------------------+ //| Hide the pattern icon on the chart | //+------------------------------------------------------------------+ void CPattern::Hide(const bool redraw=false) { if(this.m_draw_dots) { ::ObjectSetInteger(this.m_chart_id,this.m_name_graph_obj,OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS); return; } if(this.m_bitmap!=NULL) this.m_bitmap.Hide(); if(redraw) ::ChartRedraw(this.m_chart_id); }
これらのメソッドはよりシンプルに見えます。
//+------------------------------------------------------------------+ //| Draw the pattern icon on the chart | //+------------------------------------------------------------------+ bool CPattern::Draw(const bool redraw) { this.Show(redraw); return true; } //+------------------------------------------------------------------+ //| Display the pattern icon on the chart | //+------------------------------------------------------------------+ void CPattern::Show(const bool redraw=false) { if(this.m_bitmap==NULL) return; this.m_bitmap.Show(); if(redraw) ::ChartRedraw(this.m_chart_id); } //+------------------------------------------------------------------+ //| Hide the pattern icon on the chart | //+------------------------------------------------------------------+ void CPattern::Hide(const bool redraw=false) { if(this.m_bitmap==NULL) return; this.m_bitmap.Hide(); if(redraw) ::ChartRedraw(this.m_chart_id); }
次に、抽象パターンファイルからコードが転送されたパターンクラスを改良します。
\MQL5\Include\DoEasy\Objects\Series\Patterns\PatternPinBar.mqhピンバーパターンクラスファイルを開き、変更を加えます。
以前は、このパターンのアイコンは標準のグラフィカルオブジェクトを使用してドットとしてのみ描画されていました。ここで、ビットマップグラフィカルオブジェクト上にポイントを描画するためのメソッドを追加する必要があります。
クラス本体に新しいメソッドの宣言を追加しましょう。
//+------------------------------------------------------------------+ //| PatternPinBar.mqh | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Pattern.mqh" //+------------------------------------------------------------------+ //| Pin Bar pattern class | //+------------------------------------------------------------------+ class CPatternPinBar : public CPattern { protected: //--- Create the (1) image object, the appearance of the (2) info panel and (3) the bitmap object virtual bool CreateBitmap(void); virtual void CreateInfoPanelView(void); void CreateBitmapView(void); //--- Calculate the bitmap object (1) width and (2) height virtual int CalculatetBitmapWidth(void) { return(20); } virtual int CalculatetBitmapHeight(void) { return(40); } public: //--- Return the flag of the pattern supporting the specified property virtual bool SupportProperty(ENUM_PATTERN_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_PATTERN_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_PATTERN_PROP_STRING property) { return true; } //--- Return description of the pattern (1) status and (2) type virtual string StatusDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PATTERN_STATUS_PA); } virtual string TypeDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PATTERN_TYPE_PIN_BAR); } //--- Draw the pattern icon on the chart virtual bool Draw(const bool redraw); //--- Constructor CPatternPinBar(const uint id,const string symbol,const ENUM_TIMEFRAMES timeframe,MqlRates &rates,const ENUM_PATTERN_DIRECTION direct); };
CalculatetBitmapWidth()およびCalculatetBitmapHeight()の仮想メソッドは、常に20x40ピクセルという固定サイズの画像サイズを返すように実装されています。これは、このパターンが1本のバー上のみに描画されるものであり、画像の高さや幅を計算する必要がないためです。画像サイズは常に同じで十分です。ビットマップのアンカーポイントはオブジェクトの中央に設定され、描画されるドットはパターンの方向に応じて、ビットマップの上半分または下半分に表示されます。強気パターンの場合、ドットはビットマップの下半分に描画され、弱気パターンの場合、ドットは上半分に描画されます。このように描画位置を固定することで、垂直スケールやチャートの時間足に関係なく、常にローソク足の髭から一定距離にパターンが表示されるため、非常に視認性が良く、実用的な表現になります。
CreateInfoPanelView()情報パネルの外観を作成するメソッドの実装で、チャートのプロパティを取得するための文字列を削除します。
//--- Depending on the chart size and coordinates, we calculate the coordinates of the panel so that it does not go beyond the chart int chart_w=(int)::ChartGetInteger(this.m_chart_id,CHART_WIDTH_IN_PIXELS); int chart_h=(int)::ChartGetInteger(this.m_chart_id,CHART_HEIGHT_IN_PIXELS); int cx=(this.m_form.RightEdge() >chart_w-1 ? chart_w-1-this.m_form.Width() : this.m_form.CoordX()); int cy=(this.m_form.BottomEdge()>chart_h-1 ? chart_h-1-this.m_form.Height() : this.m_form.CoordY());
これらのプロパティはオブジェクト作成時にあらかじめ設定されるか、チャートのサイズが変更されたときに更新されるようになっています。そのため、チャートの幅や高さを保持する変数に格納された値を使用します。
//--- Depending on the chart size and coordinates, we calculate the coordinates of the panel so that it does not go beyond the chart int cx=(this.m_form.RightEdge() >this.m_chart_width_px-1 ? this.m_chart_width_px-1-this.m_form.Width() : this.m_form.CoordX()); int cy=(this.m_form.BottomEdge()>this.m_chart_height_px-1 ? this.m_chart_height_px-1-this.m_form.Height() : this.m_form.CoordY());
パターンアイコンを描画するためのメソッドを実装しましょう。
以下は、チャート上にパターンアイコンを描画するメソッドです。
//+------------------------------------------------------------------+ //| Draw the pattern icon on the chart | //+------------------------------------------------------------------+ bool CPatternPinBar::Draw(const bool redraw) { //--- If the bitmap object has not yet been created, create it if(this.m_bitmap==NULL) { if(!this.CreateBitmap()) return false; } //--- display this.Show(redraw); return true; }
物理的なビットマップオブジェクトがまだ存在しない場合は、作成してチャートに表示します。
以下は、ビットマップオブジェクトを作成するメソッドです。
//+------------------------------------------------------------------+ //| Create the bitmap object | //+------------------------------------------------------------------+ bool CPatternPinBar::CreateBitmap(void) { //--- If the bitmap object has already been created earlier, return 'true' if(this.m_bitmap!=NULL) return true; //--- Calculate the object coordinates and dimensions datetime time=this.MotherBarTime(); double price=(this.Direction()==PATTERN_DIRECTION_BULLISH ? this.MotherBarLow() : this.MotherBarHigh()); int w=this.CalculatetBitmapWidth(); int h=this.CalculatetBitmapHeight(); //--- Create the Bitmap object this.m_bitmap=this.CreateBitmap(this.ID(),this.GetChartID(),0,this.Name(),time,price,w,h,this.m_color_bidirect); if(this.m_bitmap==NULL) return false; //--- Set the object origin to its center and remove the tooltip ::ObjectSetInteger(this.GetChartID(),this.m_bitmap.NameObj(),OBJPROP_ANCHOR,ANCHOR_CENTER); ::ObjectSetString(this.GetChartID(),this.m_bitmap.NameObj(),OBJPROP_TOOLTIP,"\n"); //--- Draw the bitmap object appearance this.CreateBitmapView(); return true; }
メソッドのロジックは、コードにコメントされています。オブジェクトの座標を設定するときは、パターンの方向を考慮してください。パターンが強気の場合、座標はパターンバーの安値になり、弱気の場合は高値になります。
以下は、ビットマップオブジェクトの外観を作成するメソッドです。
//+------------------------------------------------------------------+ //| Create the bitmap object appearance | //+------------------------------------------------------------------+ void CPatternPinBar::CreateBitmapView(void) { this.m_bitmap.Erase(CLR_CANV_NULL,0); int y=(this.Direction()==PATTERN_DIRECTION_BULLISH ? this.m_bitmap.Height()/2+6 : this.m_bitmap.Height()/2-6); int x=this.m_bitmap.Width()/2; int r=2; this.m_bitmap.DrawCircleFill(x,y,r,this.Direction()==PATTERN_DIRECTION_BULLISH ? this.m_color_bullish : this.m_color_bearish); this.m_bitmap.Update(false); }
まず、キャンバスを完全に透明な色でクリアします。次に、描画される点のローカル座標を決定します。強気パターンの場合、Y座標はパターンの高さの半分に6ピクセルを加えた値(パターンの中心から6ピクセル下)になります。弱気のパターンの場合は、ビットマップの中心の座標から6ピクセルを減算します。X座標はビットマップの幅の半分になります。描画される円の半径は2に設定されています。このサイズにより、どのチャートの時間足でも視認性が確保され、点が常に明確に表示されます。
ここで、\MQL5\Include\DoEasy\Objects\Series\Patterns\PatternInsideBar.mqhはらみ線パターンファイルに同様の改善を実装してみましょう。
パターンアイコンを再描画する仮想メソッドを宣言します。
//+------------------------------------------------------------------+ //| PatternInsideBar.mqh | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Pattern.mqh" //+------------------------------------------------------------------+ //| "Inside Bar" pattern class | //+------------------------------------------------------------------+ class CPatternInsideBar : public CPattern { protected: //--- Create the (1) image object, the appearance of the (2) info panel and (3) the bitmap object virtual bool CreateBitmap(void); virtual void CreateInfoPanelView(void); void CreateBitmapView(void); public: //--- Return the flag of the pattern supporting the specified property virtual bool SupportProperty(ENUM_PATTERN_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_PATTERN_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_PATTERN_PROP_STRING property) { return true; } //--- Return description of the pattern (1) status and (2) type virtual string StatusDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PATTERN_STATUS_PA); } virtual string TypeDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PATTERN_TYPE_INSIDE_BAR); } //--- (1) Draw and (2) resize the pattern icon on the chart virtual bool Draw(const bool redraw); virtual bool Redraw(const bool redraw); //--- Constructor CPatternInsideBar(const uint id,const string symbol,const ENUM_TIMEFRAMES timeframe,MqlRates &rates,const ENUM_PATTERN_DIRECTION direct); };
クラス本体の外側に、新しいサイズでパターンアイコンを再描画するメソッドを実装します。
//+------------------------------------------------------------------+ //| Redraw the pattern icon on the chart with a new size | //+------------------------------------------------------------------+ bool CPatternInsideBar::Redraw(const bool redraw) { //--- If a drawing object has not yet been created, create and display it in the Draw() method if(this.m_bitmap==NULL) return CPatternInsideBar::Draw(redraw); //--- Calculate the new object dimensions int w=this.CalculatetBitmapWidth(); int h=this.CalculatetBitmapHeight(); //--- If canvas resizing failed, return 'false' if(!this.m_bitmap.SetWidth(w) || !this.m_bitmap.SetHeight(h)) return false; //--- Draw the new bitmap object appearance with new dimensions this.CreateBitmapView(); //--- display and return 'true' this.Show(redraw); return true; }
メソッドのロジックはコード内にコメントされています。ここではすべてが非常にシンプルです。キャンバスの新しい寸法を計算し、その寸法を変更し、新しい寸法に従ってキャンバス上に新しいビットマップを描画します。
情報パネルの外観を作成するメソッドで、バー内のパターンサイズの計算にエラーが発生していました。
//--- Create strings to describe the pattern, its parameters and search criteria string name=::StringFormat("Inside Bar (%lu bars)",int(this.Time()-this.MotherBarTime())/::PeriodSeconds(this.Timeframe())+1); string param=this.DirectDescription();
この計算方法も理論的には正当ですが、パターンを構成する左右のバーの間に週末がある場合、週末を含むバー数としてカウントされてしまうという問題があります。その結果、本来は2本のバーから成るパターンにもかかわらず、バー数が4として誤認識されることになります。
計算を修正してみましょう。
//--- Create strings to describe the pattern, its parameters and search criteria string name=::StringFormat("Inside Bar (%lu bars)",::Bars(this.Symbol(),this.Timeframe(),this.MotherBarTime(),this.Time())); string param=this.DirectDescription();
これで、パターンの2本のバーの時間差から常に正確なバー本数を取得できるようになります。
チャートの幅と高さの値は、あらかじめ変数に設定されているため、チャートプロパティを取得する処理は削除します。
//--- Depending on the chart size and coordinates, we calculate the coordinates of the panel so that it does not go beyond the chart int chart_w=(int)::ChartGetInteger(this.m_chart_id,CHART_WIDTH_IN_PIXELS); int chart_h=(int)::ChartGetInteger(this.m_chart_id,CHART_HEIGHT_IN_PIXELS); int cx=(this.m_form.RightEdge() >chart_w-1 ? chart_w-1-this.m_form.Width() : this.m_form.CoordX()); int cy=(this.m_form.BottomEdge()>chart_h-1 ? chart_h-1-this.m_form.Height() : this.m_form.CoordY());
変数からプリセットされた値を使用するようにパネル座標の計算を調整します。
//--- Depending on the chart size and coordinates, we calculate the coordinates of the panel so that it does not go beyond the chart int cx=(this.m_form.RightEdge() >this.m_chart_width_px-1 ? this.m_chart_width_px-1-this.m_form.Width() : this.m_form.CoordX()); int cy=(this.m_form.BottomEdge()>this.m_chart_height_px-1 ? this.m_chart_height_px-1-this.m_form.Height() : this.m_form.CoordY());
チャート上にパターンアイコンを描画するメソッドのドットを描画するためのコードブロックを削除します。
//+------------------------------------------------------------------+ //| Draw the pattern icon on the chart | //+------------------------------------------------------------------+ void CPatternInsideBar::Draw(const bool redraw) { //--- If the flag for drawing with dots is set, call the parent class method and leave if(this.m_draw_dots) { CPattern::Draw(redraw); return; } //--- If the bitmap object has not yet been created, create it if(this.m_bitmap==NULL) { if(!this.CreateBitmap()) return; } //--- display this.Show(redraw); }
メソッドはよりシンプルになりました。
//+------------------------------------------------------------------+ //| Draw the pattern icon on the chart | //+------------------------------------------------------------------+ bool CPatternInsideBar::Draw(const bool redraw) { //--- If the bitmap object has not yet been created, create it if(this.m_bitmap==NULL) { if(!this.CreateBitmap()) return false; } //--- display this.Show(redraw); return true; }
その他すべての改善は、新しい「アウトサイドバー」パターンクラスを作成した後に実装されます。
「アウトサイドバー」パターンクラス
\MQL5\Include\DoEasy\Objects\Series\Patterns\ライブラリフォルダに、CPatternOutsideBarクラスの新しいファイルPatternOutsideBar.mqhを作成します。
クラスはパターンオブジェクトの基底クラスから継承する必要があり、そのファイルは作成されたパターンクラスファイルにインクルードされる必要があります。
//+------------------------------------------------------------------+ //| PatternOutsideBar.mqh | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Pattern.mqh" //+------------------------------------------------------------------+ //| "Outside bar" pattern class | //+------------------------------------------------------------------+ class CPatternOutsideBar : public CPattern { }
パターンオブジェクトクラスの標準メソッドを宣言します。
//+------------------------------------------------------------------+ //| "Outside bar" pattern class | //+------------------------------------------------------------------+ class CPatternOutsideBar : public CPattern { protected: //--- Create the (1) image object, the appearance of the (2) info panel and (3) the bitmap object virtual bool CreateBitmap(void); virtual void CreateInfoPanelView(void); void CreateBitmapView(void); //--- Calculate the bitmap object height virtual int CalculatetBitmapHeight(void); public: //--- Return the flag of the pattern supporting the specified property virtual bool SupportProperty(ENUM_PATTERN_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_PATTERN_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_PATTERN_PROP_STRING property) { return true; } //--- Return description of the pattern (1) status and (2) type virtual string StatusDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PATTERN_STATUS_PA); } virtual string TypeDescription(void) const { return CMessage::Text(MSG_LIB_TEXT_PATTERN_TYPE_OUTSIDE_BAR);} //--- (1) Draw and (2) resize the pattern icon on the chart virtual bool Draw(const bool redraw); virtual bool Redraw(const bool redraw); //--- Constructor CPatternOutsideBar(const uint id,const string symbol,const ENUM_TIMEFRAMES timeframe,MqlRates &rates,const ENUM_PATTERN_DIRECTION direct); };
これらのメソッドはすべてのパターンで同じです。ただし、その実装はパターンごとに若干異なります。それぞれのメソッドを見てみましょう。
クラスコンストラクタで、パターン名、形状に含まれるローソク足の数、およびパターンローソク足の数に等しい隣接する連続パターンの数を定義します。
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CPatternOutsideBar::CPatternOutsideBar(const uint id,const string symbol,const ENUM_TIMEFRAMES timeframe,MqlRates &rates,const ENUM_PATTERN_DIRECTION direct) : CPattern(PATTERN_STATUS_PA,PATTERN_TYPE_OUTSIDE_BAR,id,direct,symbol,timeframe,rates) { this.SetProperty(PATTERN_PROP_NAME,"Outside Bar"); this.SetProperty(PATTERN_PROP_CANDLES,2); this.m_bars_formation=(int)this.GetProperty(PATTERN_PROP_CANDLES); }
以下は、情報パネルの外観を作成するメソッドです。
//+------------------------------------------------------------------+ //| Create the info panel appearance | //+------------------------------------------------------------------+ void CPatternOutsideBar::CreateInfoPanelView(void) { //--- If the form object is not created, leave if(this.m_form==NULL) return; //--- Change the color tone for bullish and bearish patterns: the bullish ones will have a blue tint, while the bearish ones will have a red tint color color_bullish=this.m_form.ChangeRGBComponents(this.m_form.ChangeColorLightness(this.m_color_panel_bullish,5),0,0,100); color color_bearish=this.m_form.ChangeRGBComponents(this.m_form.ChangeColorLightness(this.m_color_panel_bearish,5),100,0,0); color color_bidirect=this.m_color_panel_bidirect; //--- Declare the array for the initial and final colors of the gradient fill color clr[2]={}; //--- Depending on the direction of the pattern, change the lightness of the corresponding colors - the initial one is a little darker, the final one is a little lighter switch(this.Direction()) { case PATTERN_DIRECTION_BULLISH : clr[0]=this.m_form.ChangeColorLightness(color_bullish,-2.5); clr[1]=this.m_form.ChangeColorLightness(color_bullish,2.5); break; case PATTERN_DIRECTION_BEARISH : clr[0]=this.m_form.ChangeColorLightness(color_bearish,-2.5); clr[1]=this.m_form.ChangeColorLightness(color_bearish,2.5); break; default: clr[0]=this.m_form.ChangeColorLightness(color_bidirect,-2.5); clr[1]=this.m_form.ChangeColorLightness(color_bidirect,2.5); break; } //--- Set the background and form frame colors this.m_form.SetBackgroundColor(this.Direction()==PATTERN_DIRECTION_BULLISH ? color_bullish : this.Direction()==PATTERN_DIRECTION_BEARISH ? color_bearish : color_bidirect,true); this.m_form.SetBorderColor(clrGray,true); //--- Create strings to describe the pattern, its parameters and search criteria string name=::StringFormat("Outside Bar (%.2f/%.2f)",this.GetProperty(PATTERN_PROP_RATIO_CANDLE_SIZES),this.GetProperty(PATTERN_PROP_RATIO_BODY_TO_CANDLE_SIZE)); string param=::StringFormat("%s (%.2f/%.2f)",this.DirectDescription(),this.GetProperty(PATTERN_PROP_RATIO_CANDLE_SIZES_CRITERION),this.GetProperty(PATTERN_PROP_RATIO_BODY_TO_CANDLE_SIZE_CRITERION)); //--- Set the coordinates of the panel and calculate its width and height depending on the size of the texts placed on the panel int x=3; int y=20; int w=4+(::fmax(20+this.m_form.TextWidth(name),::fmax(x+this.m_form.TextWidth(param),x+this.m_form.TextWidth(::TimeToString(this.Time()))))); int h=2+(20+this.m_form.TextHeight(this.DirectDescription())+this.m_form.TextHeight(::TimeToString(this.Time()))); //--- Set the width and height of the panel according to the calculated values this.m_form.SetWidth(w); this.m_form.SetHeight(h); //--- Depending on the chart size and coordinates, we calculate the coordinates of the panel so that it does not go beyond the chart int cx=(this.m_form.RightEdge() >this.m_chart_width_px-1 ? this.m_chart_width_px-1-this.m_form.Width() : this.m_form.CoordX()); int cy=(this.m_form.BottomEdge()>this.m_chart_height_px-1 ? this.m_chart_height_px-1-this.m_form.Height() : this.m_form.CoordY()); this.m_form.SetCoordX(cx); this.m_form.SetCoordY(cy); //--- Fill the background with a gradient color this.m_form.Erase(clr,200,true,false); //--- Draw the panel frame, an icon with (i), draw the header text with the proportions of a candle and separate the header with a horizontal line this.m_form.DrawFrameSimple(0,0,this.m_form.Width(),this.m_form.Height(),1,1,1,1,this.m_form.BorderColor(),200); this.m_form.DrawIconInfo(1,1,200); this.m_form.Text(20,3,name,clrBlack,200); this.m_form.DrawLine(1,18,this.m_form.Width()-1,18,clrDarkGray,250); //--- Under the horizontal line, enter the pattern description with its search criteria and the date of the pattern-defining bar y=20; this.m_form.Text(x,y,param,clrBlack,200); y+=this.m_form.TextHeight(::TimeToString(this.Time())); this.m_form.Text(x,y,::TimeToString(this.Time()),clrBlack,200); //--- Update the panel while redrawing the chart this.m_form.Update(true); }
メソッドのロジックは、コードにコメントされています。ここでは、弱気パターンには赤みがかったグラデーション、強気パターンには青みがかったグラデーションでパネルが描画されます。パネルの上部には「(i)」マークのアイコンが表示され、パターン名と特徴(パターンを構成する2本のバーの比率)が記されています。パネルの下部には、パターン検索で指定された値と検出されたパターンの時間を用いて、パターンの方向が説明されています。
以下は、チャート上にパターンアイコンを描画するメソッドです。
//+------------------------------------------------------------------+ //| Draw the pattern icon on the chart | //+------------------------------------------------------------------+ bool CPatternOutsideBar::Draw(const bool redraw) { //--- If the bitmap object has not yet been created, create it if(this.m_bitmap==NULL) { if(!this.CreateBitmap()) return false; } //--- display this.Show(redraw); return true; }
以下は、チャート上のパターンアイコンを新しいサイズで再描画するメソッドです。
//+------------------------------------------------------------------+ //| Redraw the pattern icon on the chart with a new size | //+------------------------------------------------------------------+ bool CPatternOutsideBar::Redraw(const bool redraw) { //--- If a drawing object has not yet been created, create and display it in the Draw() method if(this.m_bitmap==NULL) return CPatternOutsideBar::Draw(redraw); //--- Calculate the new object dimensions int w=this.CalculatetBitmapWidth(); int h=this.CalculatetBitmapHeight(); //--- If canvas resizing failed, return 'false' if(!this.m_bitmap.SetWidth(w) || !this.m_bitmap.SetHeight(h)) return false; //--- Draw the new bitmap object appearance with new dimensions this.CreateBitmapView(); //--- display and return 'true' this.Show(redraw); return true; }
キャンバスのサイズを変更し、新しいサイズで新しいパターンビットマップを再描画するだけです。
以下は、ビットマップオブジェクトを作成するメソッドです。
//+------------------------------------------------------------------+ //| Create the bitmap object | //+------------------------------------------------------------------+ bool CPatternOutsideBar::CreateBitmap(void) { //--- If the bitmap object has already been created earlier, return 'true' if(this.m_bitmap!=NULL) return true; //--- Calculate the object coordinates and dimensions datetime time=this.MotherBarTime(); double price=(this.BarPriceHigh()+this.BarPriceLow())/2; int w=this.CalculatetBitmapWidth(); int h=this.CalculatetBitmapHeight(); //--- Create the Bitmap object this.m_bitmap=this.CreateBitmap(this.ID(),this.GetChartID(),0,this.Name(),time,price,w,h,this.m_color_bidirect); if(this.m_bitmap==NULL) return false; //--- Set the object origin to its center and remove the tooltip ::ObjectSetInteger(this.GetChartID(),this.m_bitmap.NameObj(),OBJPROP_ANCHOR,ANCHOR_CENTER); ::ObjectSetString(this.GetChartID(),this.m_bitmap.NameObj(),OBJPROP_TOOLTIP,"\n"); //--- Draw the bitmap object appearance this.CreateBitmapView(); return true; }
グラフィカルオブジェクトがすでに作成されている場合は、メソッドをそのまま終了します。それ以外の場合は、グラフィカルオブジェクトを配置する価格と時間を取得し、その幅と高さを計算して新しいオブジェクトを作成します。次に、オブジェクトの中心にアンカーポイントを設定し、その上にオブジェクトの外観を描画します。グラフィカルオブジェクトの中心点となる価格は、パターンの中で最大のローソク足の中心を基に計算されます。
以下は、ビットマップオブジェクトの外観を作成するメソッドです。
//+------------------------------------------------------------------+ //| Create the bitmap object appearance | //+------------------------------------------------------------------+ void CPatternOutsideBar::CreateBitmapView(void) { this.m_bitmap.Erase(CLR_CANV_NULL,0); int x=this.m_bitmap.Width()/2-int(1<<this.m_chart_scale)/2; this.m_bitmap.DrawRectangleFill(x,0,this.m_bitmap.Width()-1,this.m_bitmap.Height()-1,(this.Direction()==PATTERN_DIRECTION_BULLISH ? this.m_color_bullish : this.m_color_bearish),80); this.m_bitmap.DrawRectangle(x,0,this.m_bitmap.Width()-1,this.m_bitmap.Height()-1,clrGray); this.m_bitmap.Update(false); }
ここでは、最初に完全に透明な色を使用してビットマップが消去されます。次に、チャートのスケールに応じて、ペイントされた領域の開始のローカルX座標が計算されます。計算された初期X座標を使用して、グラフィカルオブジェクトの高さ全体に塗りつぶされた矩形が描画され、同じ座標と同じサイズの矩形のフレームがその上に描画されます。
以下は、描画オブジェクトの高さを計算するメソッドです。
//+------------------------------------------------------------------+ //| Calculate the bitmap object height | //+------------------------------------------------------------------+ int CPatternOutsideBar::CalculatetBitmapHeight(void) { //--- Calculate the chart price range and pattern price range double chart_price_range=this.m_chart_price_max-this.m_chart_price_min; double patt_price_range=this.BarPriceHigh()-this.BarPriceLow(); //--- Using the calculated price ranges, calculate and return the height of the bitmap object return (int)ceil(patt_price_range*this.m_chart_height_px/chart_price_range)+8; }
ここでは、チャートの価格範囲(チャートの最高価格から最低価格まで)と、パターンの価格範囲(定義ローソク足の高値から定義ローソク足の安値まで)を取得します。次に、これらの範囲の比率をピクセル単位で計算し、結果のビットマップオブジェクトの高さを返します。高さには、上部に4ピクセル、下部に4ピクセルの合計8ピクセルが追加されます。
アウトサイドバーパターンクラスが準備できました。次に、時系列クラス内にある多くの同一のメソッドを整理する必要があります。これらのメソッドは、それぞれ自分のパターンに対して同じ処理をおこなっています。パターンに対する各アクションごとに1つのメソッドを作成し、その中で目的のパターンタイプを指定する形にしましょう。
パターンに含まれるローソク足の関係性の必要なパラメータは、MqlParam構造体を使ってパターンクラスに渡します。これにより、すべてのクラスで共通の形式変数に限定されることなく、異なるパターンの異なるクラスに対して完全に異なるパラメータを渡すことが可能になります。
現時点では、パターンのローソク足比率を示す変数名は変更しませんが、必要に応じて「param1」「param2」などの汎用的な名前に変更することも検討します。クラスのコンストラクタ内で、必要に応じて各変数に適切なパターンパラメータを割り当てます。当面は、すべてのパターンで共通して使われている既存の変数名をそのまま使用します。
\MQL5\Include\DoEasy\Objects\Series\Bar.mqh内のバーオブジェクトクラスにて、AddPattern()メソッドをAddPatternType()に名前変更します。
//--- Return itself CBar *GetObject(void) { return &this;} //--- Set (1) bar symbol, timeframe and time, (2) bar object parameters void SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time); void SetProperties(const MqlRates &rates); //--- Add the pattern type on bar void AddPatternType(const ENUM_PATTERN_TYPE pattern_type){ this.m_long_prop[BAR_PROP_PATTERNS_TYPE] |=pattern_type; } //--- Compare CBar objects by all possible properties (for sorting the lists by a specified bar object property)
ただし、このメソッドはパターンへのポインタではなく、パターンタイプをバーオブジェクトに追加します。したがって、メソッドの名前をより正しいものに変更する方が論理的です。
リストから必要なパターンを選択して受け取ることができるように、CSelectクラスの\MQL5\Include\DoEasy\Services\Select.mqhファイルにすべてのパターンファイルをインクルードします。
//+------------------------------------------------------------------+ //| Select.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> #include "..\Objects\Orders\Order.mqh" #include "..\Objects\Events\Event.mqh" #include "..\Objects\Accounts\Account.mqh" #include "..\Objects\Symbols\Symbol.mqh" #include "..\Objects\PendRequest\PendRequest.mqh" #include "..\Objects\Series\\Patterns\PatternPinBar.mqh" #include "..\Objects\Series\\Patterns\PatternInsideBar.mqh" #include "..\Objects\Series\\Patterns\PatternOutsideBar.mqh" #include "..\Objects\Series\SeriesDE.mqh" #include "..\Objects\Indicators\Buffer.mqh" #include "..\Objects\Indicators\IndicatorDE.mqh" #include "..\Objects\Indicators\DataInd.mqh" #include "..\Objects\Ticks\DataTick.mqh" #include "..\Objects\Book\MarketBookOrd.mqh" #include "..\Objects\MQLSignalBase\MQLSignal.mqh" #include "..\Objects\Chart\ChartObj.mqh" #include "..\Objects\Graph\GCnvElement.mqh" #include "..\Objects\Graph\Standard\GStdGraphObj.mqh"
これにより、パターンを処理するために、パターンクラスを時系列クラスで使用できるようになります。
MqlParam構造体の配列が等しいかどうかを比較する必要があります。\MQL5\Include\DoEasy\Services\DELib.mqhに構造体フィールドと構造体配列を比較する関数を記述しましょう。
//+------------------------------------------------------------------+ //| Compare MqlParam structures with each other | //+------------------------------------------------------------------+ bool IsEqualMqlParams(MqlParam &struct1,MqlParam &struct2) { if(struct1.type!=struct2.type) return false; switch(struct1.type) { //--- integer types case TYPE_BOOL : case TYPE_CHAR : case TYPE_UCHAR : case TYPE_SHORT : case TYPE_USHORT : case TYPE_COLOR : case TYPE_INT : case TYPE_UINT : case TYPE_DATETIME : case TYPE_LONG : case TYPE_ULONG : return(struct1.integer_value==struct2.integer_value); //--- real types case TYPE_FLOAT : case TYPE_DOUBLE : return(NormalizeDouble(struct1.double_value-struct2.double_value,DBL_DIG)==0); //--- string type case TYPE_STRING : return(struct1.string_value==struct2.string_value); default : return false; } } //+------------------------------------------------------------------+ //| Compare array of MqlParam structures with each other | //+------------------------------------------------------------------+ bool IsEqualMqlParamArrays(MqlParam &array1[],MqlParam &array2[]) { int total=ArraySize(array1); int size=ArraySize(array2); if(total!=size || total==0 || size==0) return false; for(int i=0;i<total;i++) { if(!IsEqualMqlParams(array1[i],array2[i])) return false; } return true; } //+------------------------------------------------------------------+
パターン管理クラスは、\MQL5\Include\DoEasy\Objects\Series\SeriesDE.mqh時系列クラスファイルにあります。抽象パターン管理クラスで、不要になった変数を削除し、新しい変数を追加します。
//+------------------------------------------------------------------+ //| Abstract pattern control class | //+------------------------------------------------------------------+ class CPatternControl : public CBaseObjExt { private: ENUM_TIMEFRAMES m_timeframe; // Pattern timeseries chart period string m_symbol; // Pattern timeseries symbol double m_point; // Symbol Point bool m_used; // Pattern use flag bool m_drawing; // Flag for drawing the pattern icon on the chart bool m_draw_dots; // Flag for drawing the pattern icon on the chart with dots //--- Handled pattern ENUM_PATTERN_TYPE m_type_pattern; // Pattern type protected: //--- Candle proportions double m_ratio_body_to_candle_size; // Percentage ratio of the candle body to the full size of the candle double m_ratio_larger_shadow_to_candle_size; // Percentage ratio of the size of the larger shadow to the size of the candle double m_ratio_smaller_shadow_to_candle_size; // Percentage ratio of the size of the smaller shadow to the size of the candle double m_ratio_candle_sizes; // Percentage of candle sizes uint m_min_body_size; // The minimum size of the candlestick body ulong m_object_id; // Unique object code based on pattern search criteria //--- List views CArrayObj *m_list_series; // Pointer to the timeseries list CArrayObj *m_list_all_patterns; // Pointer to the list of all patterns CPattern m_pattern_instance; // Pattern object for searching by property //--- Graph ulong m_symbol_code; // Chart symbol name as a number int m_chart_scale; // Chart scale int m_chart_height_px; // Height of the chart in pixels int m_chart_width_px; // Height of the chart in pixels double m_chart_price_max; // Chart maximum double m_chart_price_min; // Chart minimum
以前は、パターン検索メソッドで、パターンを検索するために変数に最小のローソク足サイズを渡していました。
virtual ENUM_PATTERN_DIRECTION FindPattern(const datetime series_bar_time,const uint min_body_size,MqlRates &mother_bar_data) const { return WRONG_VALUE; }
このサイズをMqlParam変数を介して転送するようにします。これにより、この変数はすべてのパターン管理クラスのすべてのパターン検索メソッドから削除されます。
//--- (1) Search for a pattern, return direction (or -1 if no pattern is found), //--- (2) create a pattern with a specified direction, //--- (3) create and return a unique pattern code, //--- (4) return the list of patterns managed by the object virtual ENUM_PATTERN_DIRECTION FindPattern(const datetime series_bar_time,MqlRates &mother_bar_data) const { return WRONG_VALUE; } virtual CPattern *CreatePattern(const ENUM_PATTERN_DIRECTION direction,const uint id,CBar *bar){ return NULL; } virtual ulong GetPatternCode(const ENUM_PATTERN_DIRECTION direction,const datetime time) const { return 0; } virtual CArrayObj*GetListPatterns(void) { return NULL; }
クラスのpublicセクションから、ドットパターン描画のプロパティを設定および取得するためのメソッドを削除します。
//--- (1) Set and (2) return the flag for drawing pattern icons as dots
void SetDrawingAsDots(const bool flag,const bool redraw);
bool IsDrawingAsDots(void) const { return this.m_draw_dots; }
パターンパラメータの配列を宣言し、新しい変数を処理するメソッドを追加し、CreateAndRefreshPatternList()仮想メソッドからmin_body_sizeパラメータの受け渡しを削除し、パラメトリックコンストラクタでは、MqlParam構造体配列を介してパターンプロパティの配列を渡すようにします。チャート上の既存のパターンをすべて再描画する新しいメソッドを宣言します。
public: MqlParam PatternParams[]; // Array of pattern parameters //--- Return itself CPatternControl *GetObject(void) { return &this; } //--- (1) Set and (2) return the pattern usage flag void SetUsed(const bool flag) { this.m_used=flag; } bool IsUsed(void) const { return this.m_used; } //--- (1) Set and (2) return the pattern drawing flag void SetDrawing(const bool flag) { this.m_drawing=flag; } bool IsDrawing(void) const { return this.m_drawing; } //--- Set the necessary percentage ratio of the candle body to the full size of the candle, //--- size of the (2) upper and (3) lower shadow to the candle size, (4) sizes of candles, (5) minimum size of the candle body void SetRatioBodyToCandleSizeValue(const double value) { this.m_ratio_body_to_candle_size=value; } void SetRatioLargerShadowToCandleSizeValue(const double value) { this.m_ratio_larger_shadow_to_candle_size=value; } void SetRatioSmallerShadowToCandleSizeValue(const double value) { this.m_ratio_smaller_shadow_to_candle_size=value; } void SetRatioCandleSizeValue(const double value) { this.m_ratio_candle_sizes=value; } void SetMinBodySize(const uint value) { this.m_min_body_size=value; } //--- Return the necessary percentage ratio of the candle body to the full size of the candle, //--- size of the (2) upper and (3) lower shadow to the candle size, (4) sizes of candles, (5) minimum size of the candle body double RatioBodyToCandleSizeValue(void) const { return this.m_ratio_body_to_candle_size; } double RatioLargerShadowToCandleSizeValue(void) const { return this.m_ratio_larger_shadow_to_candle_size; } double RatioSmallerShadowToCandleSizeValue(void) const { return this.m_ratio_smaller_shadow_to_candle_size; } double RatioCandleSizeValue(void) const { return this.m_ratio_candle_sizes; } int MinBodySize(void) const { return (int)this.m_min_body_size; } //--- Return object ID based on pattern search criteria virtual ulong ObjectID(void) const { return this.m_object_id; } //--- Return pattern (1) type, (2) timeframe, (3) symbol, (4) symbol Point, (5) symbol code ENUM_PATTERN_TYPE TypePattern(void) const { return this.m_type_pattern; } ENUM_TIMEFRAMES Timeframe(void) const { return this.m_timeframe; } string Symbol(void) const { return this.m_symbol; } double Point(void) const { return this.m_point; } ulong SymbolCode(void) const { return this.m_symbol_code; } //--- Set the (1) chart scale, (2) height, (3) width in pixels, (4) high, (5) low void SetChartScale(const int scale) { this.m_chart_scale=scale; } void SetChartHeightInPixels(const int height) { this.m_chart_height_px=height; } void SetChartWidthInPixels(const int width) { this.m_chart_width_px=width; } void SetChartPriceMax(const double price) { this.m_chart_price_max=price; } void SetChartPriceMin(const double price) { this.m_chart_price_min=price; } //--- Return the (1) chart scale, (2) height, (3) width in pixels, (4) high, (5) low int ChartScale(void) const { return this.m_chart_scale; } int ChartHeightInPixels(void) const { return this.m_chart_height_px; } int ChartWidthInPixels(void) const { return this.m_chart_width_px; } double ChartPriceMax(void) const { return this.m_chart_price_max; } double ChartPriceMin(void) const { return this.m_chart_price_min; } //--- Compare CPatternControl objects by all possible properties virtual int Compare(const CObject *node,const int mode=0) const; //--- Search for patterns and add found ones to the list of all patterns virtual int CreateAndRefreshPatternList(void); //--- Display patterns on the chart void DrawPatterns(const bool redraw=false); //--- Redraw patterns on the chart with a new size void RedrawPatterns(const bool redraw=false); //--- Protected parametric constructor protected: CPatternControl(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_PATTERN_STATUS status,const ENUM_PATTERN_TYPE type,CArrayObj *list_series,CArrayObj *list_patterns,const MqlParam ¶m[]); };
クラスのパラメトリックコンストラクタで、チャートの不足しているプロパティを取得し、コンストラクタに渡された配列からパターンパラメータを入力します。
//+------------------------------------------------------------------+ //| CPatternControl::Protected parametric constructor | //+------------------------------------------------------------------+ CPatternControl::CPatternControl(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_PATTERN_STATUS status,const ENUM_PATTERN_TYPE type,CArrayObj *list_series,CArrayObj *list_patterns,const MqlParam ¶m[]) : m_used(true),m_drawing(true) { this.m_type=OBJECT_DE_TYPE_SERIES_PATTERN_CONTROL; this.m_type_pattern=type; this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); this.m_timeframe=(timeframe==PERIOD_CURRENT ? ::Period() : timeframe); this.m_point=::SymbolInfoDouble(this.m_symbol,SYMBOL_POINT); this.m_object_id=0; this.m_list_series=list_series; this.m_list_all_patterns=list_patterns; for(int i=0;i<(int)this.m_symbol.Length();i++) this.m_symbol_code+=this.m_symbol.GetChar(i); this.m_chart_scale=(int)::ChartGetInteger(this.m_chart_id,CHART_SCALE); this.m_chart_height_px=(int)::ChartGetInteger(this.m_chart_id,CHART_HEIGHT_IN_PIXELS); this.m_chart_width_px=(int)::ChartGetInteger(this.m_chart_id,CHART_WIDTH_IN_PIXELS); this.m_chart_price_max=::ChartGetDouble(this.m_chart_id,CHART_PRICE_MAX); this.m_chart_price_min=::ChartGetDouble(this.m_chart_id,CHART_PRICE_MIN); //--- fill in the array of parameters with data from the array passed to constructor int count=::ArrayResize(this.PatternParams,::ArraySize(param)); for(int i=0;i<count;i++) { this.PatternParams[i].type = param[i].type; this.PatternParams[i].double_value = param[i].double_value; this.PatternParams[i].integer_value= param[i].integer_value; this.PatternParams[i].string_value = param[i].string_value; } }
パターンを検索し、見つかったパターンをすべてのパターンのリストに追加するメソッドに必要な修正を加えます。
//+------------------------------------------------------------------+ //| CPatternControl::Search for patterns and add | //| found ones to the list of all patterns | //+------------------------------------------------------------------+ int CPatternControl::CreateAndRefreshPatternList(void) { //--- If not used, leave if(!this.m_used) return 0; //--- Reset the timeseries event flag and clear the list of all timeseries pattern events this.m_is_event=false; this.m_list_events.Clear(); //--- Get the opening date of the last (current) bar datetime time_open=0; if(!::SeriesInfoInteger(this.Symbol(),this.Timeframe(),SERIES_LASTBAR_DATE,time_open)) return 0; //--- Get a list of all bars in the timeseries except the current one CArrayObj *list=CSelect::ByBarProperty(this.m_list_series,BAR_PROP_TIME,time_open,LESS); if(list==NULL || list.Total()==0) return 0; //--- "Mother" bar data structure MqlRates pattern_mother_bar_data={}; //--- Sort the resulting list by bar opening time list.Sort(SORT_BY_BAR_TIME); //--- In a loop from the latest bar, for(int i=list.Total()-1;i>=0;i--) { //--- get the next bar object from the list CBar *bar=list.At(i); if(bar==NULL) continue; //--- look for a pattern relative to the received bar ENUM_PATTERN_DIRECTION direction=this.FindPattern(bar.Time(),pattern_mother_bar_data); //--- If there is no pattern, go to the next bar if(direction==WRONG_VALUE) continue; //--- Pattern found on the current bar of the loop //--- unique pattern code = candle opening time + type + status + pattern direction + timeframe + timeseries symbol ulong code=this.GetPatternCode(direction,bar.Time()); //--- Set the pattern code to the sample this.m_pattern_instance.SetProperty(PATTERN_PROP_CODE,code); //--- Sort the list of all patterns by the unique pattern code this.m_list_all_patterns.Sort(SORT_BY_PATTERN_CODE); //--- search for a pattern in the list using a unique code int index=this.m_list_all_patterns.Search(&this.m_pattern_instance); //--- If there is no pattern equal to the sample in the list of all patterns if(index==WRONG_VALUE) { //--- Create the pattern object CPattern *pattern=this.CreatePattern(direction,this.m_list_all_patterns.Total(),bar); if(pattern==NULL) continue; //--- Sort the list of all patterns by time and insert the pattern into the list by its time this.m_list_all_patterns.Sort(SORT_BY_PATTERN_TIME); if(!this.m_list_all_patterns.InsertSort(pattern)) { delete pattern; continue; } //--- Add the pattern type to the list of pattern types of the bar object bar.AddPatternType(pattern.TypePattern()); //--- Add the pointer to the bar the pattern object is found on, together with the mother bar data pattern.SetPatternBar(bar); pattern.SetMotherBarData(pattern_mother_bar_data); //--- set the chart data to the pattern object pattern.SetChartHeightInPixels(this.m_chart_height_px); pattern.SetChartWidthInPixels(this.m_chart_width_px); pattern.SetChartScale(this.m_chart_scale); pattern.SetChartPriceMax(this.m_chart_price_max); pattern.SetChartPriceMin(this.m_chart_price_min); //--- If the drawing flag is set, draw the pattern label on the chart if(this.m_drawing) pattern.Draw(false); //--- Get the time of the penultimate bar in the collection list (timeseries bar with index 1) datetime time_prev=time_open-::PeriodSeconds(this.Timeframe()); //--- If the current bar in the loop is the bar with index 1 in the timeseries, create and send a message if(bar.Time()==time_prev) { // Here is where the message is created and sent } } } //--- Sort the list of all patterns by time and return the total number of patterns in the list this.m_list_all_patterns.Sort(SORT_BY_PATTERN_TIME); return m_list_all_patterns.Total(); }
メソッドには仮パラメータがなくなりました。新しいパターンを作成すると、チャートのプロパティがすぐに転送され、そのパターンに設定されます。パターン検索ループブロックの最後に、新しいパターンイベントを作成するための空白を挿入します。さらに、次の記事では、新しい時系列パターンの出現に関するイベントを送ります。
以下は、ビットマップオブジェクトの新しいサイズを使用してチャート上のパターンを再描画するメソッドです。
//+-------------------------------------------------------------------+ //|Redraw patterns on a chart with a new size of the bitmap object | //+-------------------------------------------------------------------+ void CPatternControl::RedrawPatterns(const bool redraw=false) { //--- Get a list of patterns controlled by the control object CArrayObj *list=this.GetListPatterns(); if(list==NULL || list.Total()==0) return; //--- Sort the obtained list by pattern time list.Sort(SORT_BY_PATTERN_TIME); //--- In a loop from the latest pattern, for(int i=list.Total()-1;i>=0;i--) { //--- get the next pattern object CPattern *obj=list.At(i); if(obj==NULL) continue; //--- Redraw the pattern bitmap object on the chart obj.Redraw(false); } //--- At the end of the cycle, redraw the chart if the flag is set if(redraw) ::ChartRedraw(this.m_chart_id); }
時系列パターンのリストを順に処理し、各パターンの再描画メソッドを呼び出すだけです。現状では最適化されていませんが、リスト内のすべてのパターンについて再描画がおこなわれます。とはいえ、チャートに表示されている履歴の一部だけを再描画する仕組みを後ほど実装する予定です。後でそれについて説明します。
次に、同じファイル内のさらに奥にあるパターン管理クラスを改善しましょう。次の文字列の出現箇所をすべて検索してみましょう。
const uint min_body_size
クラスメソッドの仮パラメータから削除します。最小サイズのパターンローソク足は、現在ではMqlParam構造体配列のパターンパラメータとともに渡される仕様となっています。
同じ変更が複数のクラスにまたがっているため、ここでは詳細に1つ1つ説明せず、変更箇所を色分けして示すだけにします。
ピンバーパターン管理クラス:
//+------------------------------------------------------------------+ //| Pin Bar pattern control class | //+------------------------------------------------------------------+ class CPatternControlPinBar : public CPatternControl { protected: //--- (1) Search for a pattern, return direction (or -1), //--- (2) create a pattern with a specified direction, //--- (3) create and return a unique pattern code //--- (4) return the list of patterns managed by the object virtual ENUM_PATTERN_DIRECTION FindPattern(const datetime series_bar_time,MqlRates &mother_bar_data) const; virtual CPattern *CreatePattern(const ENUM_PATTERN_DIRECTION direction,const uint id,CBar *bar); virtual ulong GetPatternCode(const ENUM_PATTERN_DIRECTION direction,const datetime time) const { //--- unique pattern code = candle opening time + type + status + pattern direction + timeframe + timeseries symbol return(time+PATTERN_TYPE_PIN_BAR+PATTERN_STATUS_PA+direction+this.Timeframe()+this.m_symbol_code); } virtual CArrayObj*GetListPatterns(void); //--- Create object ID based on pattern search criteria virtual ulong CreateObjectID(void); public: //--- Parametric constructor CPatternControlPinBar(const string symbol,const ENUM_TIMEFRAMES timeframe, CArrayObj *list_series,CArrayObj *list_patterns, const MqlParam ¶m[]) : CPatternControl(symbol,timeframe,PATTERN_STATUS_PA,PATTERN_TYPE_PIN_BAR,list_series,list_patterns,param) { this.m_min_body_size=(uint)this.PatternParams[0].integer_value; this.m_ratio_body_to_candle_size=this.PatternParams[1].double_value; this.m_ratio_larger_shadow_to_candle_size=this.PatternParams[2].double_value; this.m_ratio_smaller_shadow_to_candle_size=this.PatternParams[3].double_value; this.m_ratio_candle_sizes=0; this.m_object_id=this.CreateObjectID(); } };
...
//+------------------------------------------------------------------+ //| CPatternControlPinBar::Search for the pattern | //+------------------------------------------------------------------+ ENUM_PATTERN_DIRECTION CPatternControlPinBar::FindPattern(const datetime series_bar_time,MqlRates &mother_bar_data) const { //--- Get data for one bar by time CArrayObj *list=CSelect::ByBarProperty(this.m_list_series,BAR_PROP_TIME,series_bar_time,EQUAL); //--- If the list is empty, return -1 if(list==NULL || list.Total()==0) return WRONG_VALUE; //--- he size of the candle body should be less than or equal to RatioBodyToCandleSizeValue() (default 30%) of the entire candle size, //--- in this case, the body size should not be less than min_body_size list=CSelect::ByBarProperty(list,BAR_PROP_RATIO_BODY_TO_CANDLE_SIZE,this.RatioBodyToCandleSizeValue(),EQUAL_OR_LESS); list=CSelect::ByBarProperty(list,BAR_PROP_RATIO_BODY_TO_CANDLE_SIZE,this.m_min_body_size,EQUAL_OR_MORE); //--- If the list is empty - there are no patterns, return -1 if(list==NULL || list.Total()==0) return WRONG_VALUE; //--- Define the bullish pattern
はらみ線パターン管理クラス:
//+------------------------------------------------------------------+ //| Inside Bar pattern control class | //+------------------------------------------------------------------+ class CPatternControlInsideBar : public CPatternControl { private: //--- Check and return the presence of a pattern on two adjacent bars bool CheckInsideBar(const CBar *bar1,const CBar *bar0) const { //--- If empty bar objects are passed, return 'false' if(bar0==NULL || bar1==NULL) return false; //--- Return the fact that the bar on the right is completely within the dimensions of the bar on the left return(bar0.High()<bar1.High() && bar0.Low()>bar1.Low()); } bool FindMotherBar(CArrayObj *list,MqlRates &rates) const { bool res=false; if(list==NULL) return false; //--- In a loop through the list, starting from the bar to the left of the base one for(int i=list.Total()-2;i>0;i--) { //--- Get the pointers to two consecutive bars CBar *bar0=list.At(i); CBar *bar1=list.At(i-1); if(bar0==NULL || bar1==NULL) return false; //--- If the obtained bars represent a pattern if(CheckInsideBar(bar1,bar0)) { //--- set mother bar data to the MqlRates variable and set 'res' to 'true' this.SetBarData(bar1,rates); res=true; } //--- If there is no pattern, interrupt the loop else break; } //--- return the result return res; } protected: //--- (1) Search for a pattern, return direction (or -1), //--- (2) create a pattern with a specified direction, //--- (3) create and return a unique pattern code //--- (4) return the list of patterns managed by the object virtual ENUM_PATTERN_DIRECTION FindPattern(const datetime series_bar_time,MqlRates &mother_bar_data) const; virtual CPattern *CreatePattern(const ENUM_PATTERN_DIRECTION direction,const uint id,CBar *bar); virtual ulong GetPatternCode(const ENUM_PATTERN_DIRECTION direction,const datetime time) const { //--- unique pattern code = candle opening time + type + status + pattern direction + timeframe + timeseries symbol return(time+PATTERN_TYPE_INSIDE_BAR+PATTERN_STATUS_PA+PATTERN_DIRECTION_BOTH+this.Timeframe()+this.m_symbol_code); } virtual CArrayObj*GetListPatterns(void); //--- Create object ID based on pattern search criteria virtual ulong CreateObjectID(void); public: //--- Parametric constructor CPatternControlInsideBar(const string symbol,const ENUM_TIMEFRAMES timeframe, CArrayObj *list_series,CArrayObj *list_patterns,const MqlParam ¶m[]) : CPatternControl(symbol,timeframe,PATTERN_STATUS_PA,PATTERN_TYPE_INSIDE_BAR,list_series,list_patterns,param) { this.m_min_body_size=(uint)this.PatternParams[0].integer_value; this.m_ratio_body_to_candle_size=0; this.m_ratio_larger_shadow_to_candle_size=0; this.m_ratio_smaller_shadow_to_candle_size=0; this.m_ratio_candle_sizes=0; this.m_object_id=this.CreateObjectID(); } };
...
//+------------------------------------------------------------------+ //| CPatternControlInsideBar::Search for pattern | //+------------------------------------------------------------------+ ENUM_PATTERN_DIRECTION CPatternControlInsideBar::FindPattern(const datetime series_bar_time,MqlRates &mother_bar_data) const { //--- Get bar data up to and including the specified time
ピンバーおよびはらみ線の管理クラスと同様に、アウトサイドバーパターンを管理するためのクラスを実装します。
//+------------------------------------------------------------------+ //| Outside Bar pattern management class | //+------------------------------------------------------------------+ class CPatternControlOutsideBar : public CPatternControl { private: //--- Check and return the flag of compliance with the ratio of the candle body to its size relative to the specified value bool CheckProportions(const CBar *bar) const { return(bar.RatioBodyToCandleSize()>=this.RatioBodyToCandleSizeValue()); } //--- Return the ratio of nearby candles for the specified bar double GetRatioCandles(const CBar *bar) const { //--- If an empty object is passed, return 0 if(bar==NULL) return 0; //--- Get a list of bars with a time less than that passed to the method CArrayObj *list=CSelect::ByBarProperty(this.m_list_series,BAR_PROP_TIME,bar.Time(),LESS); if(list==NULL || list.Total()==0) return 0; //--- Set the flag of sorting by bar time for the list and list.Sort(SORT_BY_BAR_TIME); //--- get the pointer to the last bar in the list (closest to the one passed to the method) CBar *bar1=list.At(list.Total()-1); if(bar1==NULL) return 0; //--- Return the ratio of the sizes of one bar to another return(bar.Size()>0 ? bar1.Size()*100.0/bar.Size() : 0.0); } //--- Check and return the presence of a pattern on two adjacent bars bool CheckOutsideBar(const CBar *bar1,const CBar *bar0) const { //--- If empty bar objects are passed, or their types in direction are neither bullish nor bearish, or are the same - return 'false' if(bar0==NULL || bar1==NULL || bar0.TypeBody()==BAR_BODY_TYPE_NULL || bar1.TypeBody()==BAR_BODY_TYPE_NULL || bar0.TypeBody()==BAR_BODY_TYPE_CANDLE_ZERO_BODY || bar1.TypeBody()==BAR_BODY_TYPE_CANDLE_ZERO_BODY || bar0.TypeBody()==bar1.TypeBody()) return false; //--- Calculate the ratio of the specified candles and, if it is less than the specified one, return 'false' double ratio=(bar0.Size()>0 ? bar1.Size()*100.0/bar0.Size() : 0.0); if(ratio<this.RatioCandleSizeValue()) return false; //--- Return the fact that the bar body on the right completely covers the dimensions of the bar body on the left, //--- and the shadows of the bars are either equal, or the shadows of the bar on the right overlap the shadows of the bar on the left return(bar1.High()<=bar0.High() && bar1.Low()>=bar0.Low() && bar1.TopBody()<bar0.TopBody() && bar1.BottomBody()>bar0.BottomBody()); } protected: //--- (1) Search for a pattern, return direction (or -1), //--- (2) create a pattern with a specified direction, //--- (3) create and return a unique pattern code //--- (4) return the list of patterns managed by the object virtual ENUM_PATTERN_DIRECTION FindPattern(const datetime series_bar_time,MqlRates &mother_bar_data) const; virtual CPattern *CreatePattern(const ENUM_PATTERN_DIRECTION direction,const uint id,CBar *bar); virtual ulong GetPatternCode(const ENUM_PATTERN_DIRECTION direction,const datetime time) const { //--- unique pattern code = candle opening time + type + status + pattern direction + timeframe + timeseries symbol return(time+PATTERN_TYPE_OUTSIDE_BAR+PATTERN_STATUS_PA+direction+this.Timeframe()+this.m_symbol_code); } virtual CArrayObj*GetListPatterns(void); //--- Create object ID based on pattern search criteria virtual ulong CreateObjectID(void); public: //--- Parametric constructor CPatternControlOutsideBar(const string symbol,const ENUM_TIMEFRAMES timeframe, CArrayObj *list_series,CArrayObj *list_patterns, const MqlParam ¶m[]) : CPatternControl(symbol,timeframe,PATTERN_STATUS_PA,PATTERN_TYPE_OUTSIDE_BAR,list_series,list_patterns,param) { this.m_min_body_size=(uint)this.PatternParams[0].integer_value; // Minimum size of pattern candles this.m_ratio_candle_sizes=this.PatternParams[1].double_value; // Percentage of the size of the absorbing candle to the size of the absorbed one this.m_ratio_body_to_candle_size=this.PatternParams[2].double_value; // Percentage of full size to candle body size this.m_object_id=this.CreateObjectID(); } };
パターンを定義するための必要条件は、右側のバーの実体が左側のバーの実体の大きさを完全に覆っていること、そしてバーの髭は等しいか、右側のバーの髭が左側のバーの髭を覆っていることです。この判定はCheckOutsideBar()メソッドでおこなわれます。さらに、パターンに含まれるローソク足の実体の大きさが、ローソク足全体の大きさに対してどの程度の比率であるかを考慮する必要があります。このチェックはCheckProportions()メソッドで実施されます。
クラスのコンストラクタでは、パターンのステータスを「Price Action」、パターンタイプを「Outside Bar」と設定し、コンストラクタに渡された構造体配列から各パターンローソク足の比率をすべて設定します。
以下は、パターン検索条件に基づいてオブジェクトIDを作成するメソッドです。
//+------------------------------------------------------------------+ //| Create object ID based on pattern search criteria | //+------------------------------------------------------------------+ ulong CPatternControlOutsideBar::CreateObjectID(void) { ushort bodies=(ushort)this.RatioCandleSizeValue()*100; ushort body=(ushort)this.RatioBodyToCandleSizeValue()*100; ulong res=0; this.UshortToLong(bodies,0,res); return this.UshortToLong(body,1,res); }
ローソク足のサイズの割合およびローソク足の実体がローソク足全体に対する比率の2つの基準は、実数(パーセント)で指定され、100を超えることはありません。そこで、これらを100倍して整数値に変換し、拡張標準オブジェクトライブラリのUshortToLong()メソッドを使ってulong型のIDを生成します。このメソッドは、long型の指定ビットに対してushort値を埋め込みます。
//+------------------------------------------------------------------+ //| Pack a 'ushort' number to a passed 'long' number | //+------------------------------------------------------------------+ long CBaseObjExt::UshortToLong(const ushort ushort_value,const uchar to_byte,long &long_value) { if(to_byte>3) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_INDEX)); return 0; } return(long_value |= this.UshortToByte(ushort_value,to_byte)); } //+------------------------------------------------------------------+ //| Convert a 'ushort' value to a specified 'long' number byte | //+------------------------------------------------------------------+ long CBaseObjExt::UshortToByte(const ushort value,const uchar to_byte) const { return(long)value<<(16*to_byte); }
以下は、指定された方向でパターンを作成するメソッドです。
//+------------------------------------------------------------------+ //| Create a pattern with a specified direction | //+------------------------------------------------------------------+ CPattern *CPatternControlOutsideBar::CreatePattern(const ENUM_PATTERN_DIRECTION direction,const uint id,CBar *bar) { //--- If invalid indicator is passed to the bar object, return NULL if(bar==NULL) return NULL; //--- Fill the MqlRates structure with bar data MqlRates rates={0}; this.SetBarData(bar,rates); //--- Create a new Outside Bar pattern CPatternOutsideBar *obj=new CPatternOutsideBar(id,this.Symbol(),this.Timeframe(),rates,direction); if(obj==NULL) return NULL; //--- set the proportions of the candle the pattern was found on to the properties of the created pattern object obj.SetProperty(PATTERN_PROP_RATIO_BODY_TO_CANDLE_SIZE,bar.RatioBodyToCandleSize()); obj.SetProperty(PATTERN_PROP_RATIO_LOWER_SHADOW_TO_CANDLE_SIZE,bar.RatioLowerShadowToCandleSize()); obj.SetProperty(PATTERN_PROP_RATIO_UPPER_SHADOW_TO_CANDLE_SIZE,bar.RatioUpperShadowToCandleSize()); obj.SetProperty(PATTERN_PROP_RATIO_CANDLE_SIZES,this.GetRatioCandles(bar)); //--- set the search criteria of the candle the pattern was found on to the properties of the created pattern object obj.SetProperty(PATTERN_PROP_RATIO_BODY_TO_CANDLE_SIZE_CRITERION,this.RatioBodyToCandleSizeValue()); obj.SetProperty(PATTERN_PROP_RATIO_LARGER_SHADOW_TO_CANDLE_SIZE_CRITERION,this.RatioLargerShadowToCandleSizeValue()); obj.SetProperty(PATTERN_PROP_RATIO_SMALLER_SHADOW_TO_CANDLE_SIZE_CRITERION,this.RatioSmallerShadowToCandleSizeValue()); obj.SetProperty(PATTERN_PROP_RATIO_CANDLE_SIZES_CRITERION,this.RatioCandleSizeValue()); //--- Set the control object ID to the pattern object obj.SetProperty(PATTERN_PROP_CTRL_OBJ_ID,this.ObjectID()); //--- Return the pointer to a created object return obj; }
このメソッドは、「アウトサイドバー」パターンクラスの新しいオブジェクトを作成し、その比率と検索条件に関するデータを入力して、作成されたオブジェクトへのポインターを返します。
以下は、パターン検索メソッドです。
//+------------------------------------------------------------------+ //| CPatternControlOutsideBar::Search for pattern | //+------------------------------------------------------------------+ ENUM_PATTERN_DIRECTION CPatternControlOutsideBar::FindPattern(const datetime series_bar_time,MqlRates &mother_bar_data) const { //--- Get bar data up to and including the specified time CArrayObj *list=CSelect::ByBarProperty(this.m_list_series,BAR_PROP_TIME,series_bar_time,EQUAL_OR_LESS); //--- If the list is empty, return -1 if(list==NULL || list.Total()==0) return WRONG_VALUE; //--- Sort the list by bar opening time list.Sort(SORT_BY_BAR_TIME); //--- Get the latest bar from the list (base bar) CBar *bar_patt=list.At(list.Total()-1); if(bar_patt==NULL) return WRONG_VALUE; //--- In the loop from the next bar (mother), for(int i=list.Total()-2;i>=0;i--) { //--- Get the "mother" bar CBar *bar_prev=list.At(i); if(bar_prev==NULL) return WRONG_VALUE; //--- If the proportions of the bars do not match the pattern, return -1 if(!this.CheckProportions(bar_patt) || !this.CheckProportions(bar_prev)) return WRONG_VALUE; //--- check that the resulting two bars are a pattern. If not, return -1 if(!this.CheckOutsideBar(bar_prev,bar_patt)) return WRONG_VALUE; //--- Set the "mother" bar data this.SetBarData(bar_prev,mother_bar_data); //--- If the pattern is found at the previous step, determine and return its direction if(bar_patt.TypeBody()==BAR_BODY_TYPE_BULLISH) return PATTERN_DIRECTION_BULLISH; if(bar_patt.TypeBody()==BAR_BODY_TYPE_BEARISH) return PATTERN_DIRECTION_BEARISH; } //--- No patterns found - return -1 return WRONG_VALUE; }
メソッドロジックはコメントで説明されています。現在のバーの開始時刻がメソッドに渡されます。現在のバーを除くすべてのバーのリストを取得します(ゼロバーのパターンは探さない)。出来上がったバーのリストに基づくループで、近接する2つのバーごとに、それらがパターン基準に合致するかどうかを確認します。比率がパターンである場合は、パターンの方向を決定して返します。
以下は、オブジェクトが管理するパターンのリストを返すメソッドです。
//+------------------------------------------------------------------+ //| Returns the list of patterns managed by the object | //+------------------------------------------------------------------+ CArrayObj *CPatternControlOutsideBar::GetListPatterns(void) { CArrayObj *list=CSelect::ByPatternProperty(this.m_list_all_patterns,PATTERN_PROP_PERIOD,this.Timeframe(),EQUAL); list=CSelect::ByPatternProperty(list,PATTERN_PROP_SYMBOL,this.Symbol(),EQUAL); list=CSelect::ByPatternProperty(list,PATTERN_PROP_TYPE,PATTERN_TYPE_OUTSIDE_BAR,EQUAL); return CSelect::ByPatternProperty(list,PATTERN_PROP_CTRL_OBJ_ID,this.ObjectID(),EQUAL); }
全パターンの総合リストから、まずチャートの時間足でソートされたリストを取得し、次にそのリストから銘柄名でソートされたリストを取得します。さらに、そのリストを「アウトサイドバー」パターンのタイプでソートし、最後に制御オブジェクトIDでソートします。結果として、このメソッドはこのクラスが管理するパターンのみを含むリストを返します。
次に、パターン管理クラスを全面的に作り直す必要があります。詳細はこちらで確認してください。
これまでのように各パターンタイプごとに長く単調な処理メソッドを多数用意するのではなく、指定されたパターンを処理するためのいくつかのメソッドを作成します。その後、同じファイル内でパターン管理クラスから、以下のような類似した多数のメソッドを削除します。
... "Return the ... pattern control object" CPatternControl *GetObjControlPattern ...XXXXX(), ... "Set the flag for using the ... pattern and create a control object if it does not already exist" void SetUsedPattern ... XXXXX(const bool flag), ... "Return the flag of using the ... pattern" bool IsUsedPattern ...XXXXX(void), ... "Set the flag for drawing the ... pattern with dots" void SetDrawingAsDotsPattern ...XXXXX(const bool flag,const bool redraw), ... "Return the flag for drawing the ... pattern with dots" bool IsDrawingAsDotsPattern ...XXXXX(void), ... "Set ... pattern labels on the chart" void DrawPattern ...XXXXX(const bool redraw=false)
全体的に、このようなメソッドは非常に多く、各パターンごとに専用のメソッドが用意されています。その結果、コードは約1300行にも及びます。そこで、パターン管理クラスのすべてのメソッドを削除し、クラス全体を一新しましょう。これからは、扱うパターンを選択できる、いくつかの汎用的なパターン処理メソッドだけが存在する形にします。
こうして完全に書き直したクラスは、全メソッドを含めて以下のような構成になります。
//+------------------------------------------------------------------+ //| Pattern control class | //+------------------------------------------------------------------+ class CPatternsControl : public CBaseObjExt { private: CArrayObj m_list_controls; // List of pattern management controllers CArrayObj *m_list_series; // Pointer to the timeseries list CArrayObj *m_list_all_patterns; // Pointer to the list of all patterns //--- Timeseries data ENUM_TIMEFRAMES m_timeframe; // Timeseries timeframe string m_symbol; // Timeseries symbol public: //--- Return (1) timeframe, (2) timeseries symbol, (3) itself ENUM_TIMEFRAMES Timeframe(void) const { return this.m_timeframe; } string Symbol(void) const { return this.m_symbol; } CPatternsControl *GetObject(void) { return &this; } protected: //--- Create an object for managing a specified pattern CPatternControl *CreateObjControlPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[]) { switch(pattern) { case PATTERN_TYPE_HARAMI : return NULL; case PATTERN_TYPE_HARAMI_CROSS : return NULL; case PATTERN_TYPE_TWEEZER : return NULL; case PATTERN_TYPE_PIERCING_LINE : return NULL; case PATTERN_TYPE_DARK_CLOUD_COVER : return NULL; case PATTERN_TYPE_THREE_WHITE_SOLDIERS : return NULL; case PATTERN_TYPE_THREE_BLACK_CROWS : return NULL; case PATTERN_TYPE_SHOOTING_STAR : return NULL; case PATTERN_TYPE_HAMMER : return NULL; case PATTERN_TYPE_INVERTED_HAMMER : return NULL; case PATTERN_TYPE_HANGING_MAN : return NULL; case PATTERN_TYPE_DOJI : return NULL; case PATTERN_TYPE_DRAGONFLY_DOJI : return NULL; case PATTERN_TYPE_GRAVESTONE_DOJI : return NULL; case PATTERN_TYPE_MORNING_STAR : return NULL; case PATTERN_TYPE_MORNING_DOJI_STAR : return NULL; case PATTERN_TYPE_EVENING_STAR : return NULL; case PATTERN_TYPE_EVENING_DOJI_STAR : return NULL; case PATTERN_TYPE_THREE_STARS : return NULL; case PATTERN_TYPE_ABANDONED_BABY : return NULL; case PATTERN_TYPE_PIVOT_POINT_REVERSAL : return NULL; case PATTERN_TYPE_OUTSIDE_BAR : return new CPatternControlOutsideBar(this.m_symbol,this.m_timeframe,this.m_list_series,this.m_list_all_patterns,param); case PATTERN_TYPE_INSIDE_BAR : return new CPatternControlInsideBar(this.m_symbol,this.m_timeframe,this.m_list_series,this.m_list_all_patterns,param); case PATTERN_TYPE_PIN_BAR : return new CPatternControlPinBar(this.m_symbol,this.m_timeframe,this.m_list_series,this.m_list_all_patterns,param); case PATTERN_TYPE_RAILS : return NULL; //---PATTERN_TYPE_NONE default : return NULL; } } //--- Return an object for managing a specified pattern CPatternControl *GetObjControlPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[]) { //--- In a loop through the list of control objects, int total=this.m_list_controls.Total(); for(int i=0;i<total;i++) { //--- get the next object CPatternControl *obj=this.m_list_controls.At(i); //--- if this is not a pattern control object, go to the next one if(obj==NULL || obj.TypePattern()!=pattern) continue; //--- Check search conditions and return the result if(IsEqualMqlParamArrays(obj.PatternParams,param)) return obj; } //--- Not found - return NULL return NULL; } public: //--- Search and update all active patterns void RefreshAll(void) { //--- In a loop through the list of pattern control objects, int total=this.m_list_controls.Total(); for(int i=0;i<total;i++) { //--- get the next control object CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL) continue; //--- if this is a tester and the current chart sizes are not set, or are not equal to the current ones - we try to get and set them if(::MQLInfoInteger(MQL_TESTER)) { long int_value=0; double dbl_value=0; if(::ChartGetInteger(this.m_chart_id, CHART_SCALE, 0, int_value)) { if(obj.ChartScale()!=int_value) obj.SetChartScale((int)int_value); } if(::ChartGetInteger(this.m_chart_id, CHART_HEIGHT_IN_PIXELS, 0, int_value)) { if(obj.ChartHeightInPixels()!=int_value) obj.SetChartHeightInPixels((int)int_value); } if(::ChartGetInteger(this.m_chart_id, CHART_WIDTH_IN_PIXELS, 0, int_value)) { if(obj.ChartWidthInPixels()!=int_value) obj.SetChartWidthInPixels((int)int_value); } if(::ChartGetDouble(this.m_chart_id, CHART_PRICE_MAX, 0, dbl_value)) { if(obj.ChartPriceMax()!=dbl_value) obj.SetChartPriceMax(dbl_value); } if(::ChartGetDouble(this.m_chart_id, CHART_PRICE_MIN, 0, dbl_value)) { if(obj.ChartPriceMin()!=dbl_value) obj.SetChartPriceMin(dbl_value); } } //--- search and create a new pattern obj.CreateAndRefreshPatternList(); } } //--- Set chart parameters for all pattern management objects void SetChartPropertiesToPattCtrl(const double price_max,const double price_min,const int scale,const int height_px,const int width_px) { //--- In a loop through the list of pattern control objects, int total=this.m_list_controls.Total(); for(int i=0;i<total;i++) { //--- get the next control object CPatternControl *obj=this.m_list_controls.At(i); if(obj==NULL) continue; //--- If the object is received, set the chart parameters obj.SetChartHeightInPixels(height_px); obj.SetChartWidthInPixels(width_px); obj.SetChartScale(scale); obj.SetChartPriceMax(price_max); obj.SetChartPriceMin(price_min); } } //--- Set the flag for using the specified pattern and create a control object if it does not already exist void SetUsedPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const bool flag) { CPatternControl *obj=NULL; //--- Get the pointer to the object for managing the specified pattern obj=this.GetObjControlPattern(pattern,param); //--- If the pointer is received (the object exists), set the use flag if(obj!=NULL) obj.SetUsed(flag); //--- If there is no object and the flag is passed as 'true' else if(flag) { //--- Create a new pattern management object obj=this.CreateObjControlPattern(pattern,param); if(obj==NULL) return; //--- Add pointer to the created object to the list if(!this.m_list_controls.Add(obj)) { delete obj; return; } //--- Set the usage flag and pattern parameters to the control object obj.SetUsed(flag); obj.CreateAndRefreshPatternList(); } } //--- Return the flag of using the specified pattern bool IsUsedPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[]) { CPatternControl *obj=this.GetObjControlPattern(pattern,param); return(obj!=NULL ? obj.IsUsed() : false); } //--- Places marks of the specified pattern on the chart void DrawPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const bool redraw=false) { CPatternControl *obj=GetObjControlPattern(pattern,param); if(obj!=NULL) obj.DrawPatterns(redraw); } //--- Redraw the bitmap objects of the specified pattern on the chart void RedrawPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const bool redraw=false) { CPatternControl *obj=GetObjControlPattern(pattern,param); if(obj!=NULL) obj.RedrawPatterns(redraw); } //--- Constructor CPatternsControl(const string symbol,const ENUM_TIMEFRAMES timeframe,CArrayObj *list_timeseries,CArrayObj *list_all_patterns); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CPatternsControl::CPatternsControl(const string symbol,const ENUM_TIMEFRAMES timeframe,CArrayObj *list_timeseries,CArrayObj *list_all_patterns) { this.m_type=OBJECT_DE_TYPE_SERIES_PATTERNS_CONTROLLERS; this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); this.m_timeframe=(timeframe==PERIOD_CURRENT ? ::Period() : timeframe); this.m_list_series=list_timeseries; this.m_list_all_patterns=list_all_patterns; } //+------------------------------------------------------------------+
メソッドのロジックはコード内にコメントされていますが、強調表示されたコードブロックについて少し補足します。テスターのビジュアルモードでは、OnChartEvent()はほとんど役に立ちません。ただし、CHARTEVENT_CHART_CHANGEイベントによるチャートサイズの変化を検知し、新しいチャートサイズをパターン管理オブジェクトに書き込み、そこからパターンオブジェクトへ伝達する役割は担っています。しかしテスター上ではこの処理が機能しません。そこで、チャートサイズの変化は専用のテスター用コードブロックで監視し、変化があった際にはパターン管理オブジェクトへ新しいサイズ情報を反映させています。
さらに、同じファイル(\MQL5\Include\DoEasy\Objects\Series\SeriesDE.mqh)のコードには、CSeriesDE時系列クラスがあります。それを仕上げましょう。
時系列リスト内の任意のバーオブジェクトを検索するたびに、new演算子の助けを借りて新しいオブジェクトが作成されます。パラメータが設定され、同じパラメータを持つオブジェクトがリスト内で検索され、この新しく作成されたオブジェクトが削除されます。
//+------------------------------------------------------------------+ //| Return the bar object by time in the timeseries | //+------------------------------------------------------------------+ CBar *CSeriesDE::GetBar(const datetime time) { CBar *obj=new CBar(); if(obj==NULL) return NULL; obj.SetSymbolPeriod(this.m_symbol,this.m_timeframe,time); this.m_list_series.Sort(SORT_BY_BAR_TIME); int index=this.m_list_series.Search(obj); delete obj; return this.m_list_series.At(index); }
したがって、状況によっては、ループ内でオブジェクトの作成と削除が継続的に発生することがあります。この動作は正しくありません。検索用に単一のインスタンスオブジェクトを作成し、それを使用してパラメータを設定し、検索のパターンとして使用するのが適切です。この方法により、オブジェクトの継続的な再作成が不要になります。
検索用のバーオブジェクトのインスタンスを宣言します。
//+------------------------------------------------------------------+ //| Timeseries class | //+------------------------------------------------------------------+ class CSeriesDE : public CBaseObj { private: CBar m_bar_tmp; // Bar object for search ENUM_TIMEFRAMES m_timeframe; // Timeframe string m_symbol; // Symbol
そして、時系列内の時間ごとにバーオブジェクトを返すメソッドを書き直します。
//+------------------------------------------------------------------+ //| Return the bar object by time in the timeseries | //+------------------------------------------------------------------+ CBar *CSeriesDE::GetBar(const datetime time) { this.m_bar_tmp.SetSymbolPeriod(this.m_symbol,this.m_timeframe,time); this.m_list_series.Sort(SORT_BY_BAR_TIME); int index=this.m_list_series.Search(&this.m_bar_tmp); return this.m_list_series.At(index); }
ここで、新しいオブジェクトを作成して削除する代わりに、必要な検索パラメータを1つのインスタンスに設定し、パターンによってリスト内の同一インスタンスを検索します。オブジェクトはクラスコンストラクタで一度だけ作成され、再作成されることなく継続的に使用されます。
パターン管理クラスと同様に、各パターンを処理するためのメソッドの長いリストを削除し、目的のパターンを選択するいくつかのメソッドに置き換えます。
//+------------------------------------------------------------------+ //| Working with patterns | //+------------------------------------------------------------------+ //--- Set the flag for using the specified pattern and create a control object if it does not already exist void SetUsedPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const bool flag) { if(this.m_patterns_control!=NULL) this.m_patterns_control.SetUsedPattern(pattern,param,flag); } //--- Return the flag of using the specified pattern bool IsUsedPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[]) { return(this.m_patterns_control!=NULL ? this.m_patterns_control.IsUsedPattern(pattern,param) : false); } //--- Places marks of the specified pattern on the chart void DrawPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const bool redraw=false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.DrawPattern(pattern,param,redraw); } //--- Redraw the bitmap objects of the specified pattern on the chart void RedrawPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const bool redraw=false) { if(this.m_patterns_control!=NULL) this.m_patterns_control.RedrawPattern(pattern,param,redraw); } //--- Sets chart parameters for pattern management objects void SetChartPropertiesToPattCtrl(const double price_max,const double price_min,const int scale,const int height_px,const int width_px) { if(this.m_patterns_control!=NULL) this.m_patterns_control.SetChartPropertiesToPattCtrl(price_max,price_min,scale,height_px,width_px); }
これで、目的のパターンを選択するには、パターンごとにカスタムメソッドを使用するのではなく、メソッドパラメータでその型を指定するだけで十分です。
\MetaQuotes\MT5\MQL5\Include\DoEasy\Objects\Series\TimeSeriesDE.mqhの銘柄時系列クラスで、パターンを処理するメソッドを同様に変更します。
クラス本体の一番最後、ヘッダーの下
//--- Constructors CTimeSeriesDE(CArrayObj *list_all_patterns) { this.m_type=OBJECT_DE_TYPE_SERIES_SYMBOL; this.m_list_all_patterns=list_all_patterns; } CTimeSeriesDE(CArrayObj *list_all_patterns,const string symbol); //+------------------------------------------------------------------+ //| Methods for handling patterns | //+------------------------------------------------------------------+
パターンを処理するためのメソッド宣言の長いリストがあります。これらすべての宣言を、目的のパターンを選択していくつかのメソッドを宣言するように置き換えてみましょう。
//--- Constructors CTimeSeriesDE(CArrayObj *list_all_patterns) { this.m_type=OBJECT_DE_TYPE_SERIES_SYMBOL; this.m_list_all_patterns=list_all_patterns; } CTimeSeriesDE(CArrayObj *list_all_patterns,const string symbol); //+------------------------------------------------------------------+ //| Methods for handling patterns | //+------------------------------------------------------------------+ //--- Set the flag for using the specified pattern and create a control object if it does not already exist void SetUsedPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const ENUM_TIMEFRAMES timeframe,const bool flag); //--- Return the flag of using the specified Harami pattern bool IsUsedPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const ENUM_TIMEFRAMES timeframe); //--- Draw marks of the specified pattern on the chart void DrawPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const ENUM_TIMEFRAMES timeframe,const bool redraw=false); //--- Redraw the bitmap objects of the specified pattern on the chart void RedrawPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const ENUM_TIMEFRAMES timeframe,const bool redraw=false); //--- Set chart parameters for pattern management objects on the specified timeframe void SetChartPropertiesToPattCtrl(const ENUM_TIMEFRAMES timeframe,const double price_max,const double price_min,const int scale,const int height_px,const int width_px); };
クラス本体の外側、ファイルリストの一番最後、同じヘッダーの下に、宣言されたメソッドの実装が記述されます。この長いリスト全体を削除し、新しいメソッドに置き換えてみましょう。
//+------------------------------------------------------------------+ //| Handling timeseries patterns | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Set the flag of using the specified pattern | //| and create a control object if it does not exist yet | //+------------------------------------------------------------------+ void CTimeSeriesDE::SetUsedPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const ENUM_TIMEFRAMES timeframe,const bool flag) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetUsedPattern(pattern,param,flag); } //+------------------------------------------------------------------+ //| Return the flag of using the specified pattern | //+------------------------------------------------------------------+ bool CTimeSeriesDE::IsUsedPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const ENUM_TIMEFRAMES timeframe) { CSeriesDE *series=this.GetSeries(timeframe); return(series!=NULL ? series.IsUsedPattern(pattern,param) : false); } //+------------------------------------------------------------------+ //| Draw marks of the specified pattern on the chart | //+------------------------------------------------------------------+ void CTimeSeriesDE::DrawPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const ENUM_TIMEFRAMES timeframe,const bool redraw=false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.DrawPattern(pattern,param,redraw); } //+------------------------------------------------------------------+ //| Redraw the bitmap objects of the specified pattern on the chart | //+------------------------------------------------------------------+ void CTimeSeriesDE::RedrawPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const ENUM_TIMEFRAMES timeframe,const bool redraw=false) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.RedrawPattern(pattern,param,redraw); } //+------------------------------------------------------------------+ //| Set chart parameters for pattern management objects | //| on the specified timeframe | //+------------------------------------------------------------------+ void CTimeSeriesDE::SetChartPropertiesToPattCtrl(const ENUM_TIMEFRAMES timeframe,const double price_max,const double price_min,const int scale,const int height_px,const int width_px) { CSeriesDE *series=this.GetSeries(timeframe); if(series!=NULL) series.SetChartPropertiesToPattCtrl(price_max,price_min,scale,height_px,width_px); }
各メソッドは、パターンを処理するチャートの必要な時間枠と、パターン制御オブジェクトの選択に使用される必要なパラメータを受け取ります。次に、必要な時系列へのポインタを取得し、同じ名前の時系列クラスメソッドを呼び出した結果を返します。
時系列コレクションクラスファイル\MQL5\Include\DoEasy\Collections\TimeSeriesCollection.mqhにいくつか改善を加えてみましょう。
このクラスでは、チャートのサイズとその変更を受け取り、パターン管理クラスに送信します。チャートのサイズを保存するための新しい変数を追加しましょう。
//+------------------------------------------------------------------+ //| Symbol timeseries collection | //+------------------------------------------------------------------+ class CTimeSeriesCollection : public CBaseObjExt { private: CListObj m_list; // List of applied symbol timeseries CListObj m_list_all_patterns; // List of all patterns of all used symbol timeseries CChartObjCollection *m_charts; // Pointer to the chart collection double m_chart_max; // Chart maximum double m_chart_min; // Chart minimum int m_chart_scale; // Chart scale int m_chart_wpx; // Chart width in pixels int m_chart_hpx; // Chart height in pixels //--- Return the timeseries index by symbol name int IndexTimeSeries(const string symbol); public:
ヘッダーの下
//+------------------------------------------------------------------+ //| Handling timeseries patterns | //+------------------------------------------------------------------+
前のクラスと同様に、宣言されたメソッドを削除し、パターンタイプを示す新しいメソッドを追加します。
//--- Copy the specified double property of the specified timeseries of the specified symbol to the array //--- Regardless of the array indexing direction, copying is performed the same way as copying to a timeseries array bool CopyToBufferAsSeries(const string symbol,const ENUM_TIMEFRAMES timeframe, const ENUM_BAR_PROP_DOUBLE property, double &array[], const double empty=EMPTY_VALUE); //+------------------------------------------------------------------+ //| Handling timeseries patterns | //+------------------------------------------------------------------+ //--- Set the flag of using the specified pattern void SetUsedPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const string symbol,const ENUM_TIMEFRAMES timeframe,const bool flag); //--- Return the flag of using the specified pattern bool IsUsedPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const string symbol,const ENUM_TIMEFRAMES timeframe); //--- Draw marks of the specified pattern on the chart void DrawPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const string symbol,const ENUM_TIMEFRAMES timeframe,const bool redraw=false); //--- Redraw the bitmap objects of the specified pattern on the chart void RedrawPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const string symbol,const ENUM_TIMEFRAMES timeframe,const bool redraw=false); //--- Set chart parameters for pattern management objects on the specified symbol and timeframe void SetChartPropertiesToPattCtrl(const string symbol,const ENUM_TIMEFRAMES timeframe,const double price_max,const double price_min,const int scale,const int height_px,const int width_px);
クラス本体の最後でイベントハンドラを宣言します。
//--- Initialization void OnInit(CChartObjCollection *charts) { this.m_charts=charts; } //--- Event handler virtual void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Constructor CTimeSeriesCollection(void); };
クラスコンストラクタで、チャートの寸法を新しい変数に書き込みます。
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTimeSeriesCollection::CTimeSeriesCollection(void) { this.m_type=COLLECTION_SERIES_ID; this.m_list.Clear(); this.m_list.Sort(); this.m_list.Type(COLLECTION_SERIES_ID); this.m_list_all_patterns.Clear(); this.m_list_all_patterns.Sort(); this.m_list_all_patterns.Type(COLLECTION_SERIES_PATTERNS_ID); this.m_chart_scale=(int)::ChartGetInteger(this.m_chart_id,CHART_SCALE);//-1; this.m_chart_max=::ChartGetDouble(this.m_chart_id, CHART_PRICE_MAX); this.m_chart_min=::ChartGetDouble(this.m_chart_id, CHART_PRICE_MIN); this.m_chart_wpx=(int)::ChartGetInteger(this.m_chart_id,CHART_WIDTH_IN_PIXELS); this.m_chart_hpx=(int)::ChartGetInteger(this.m_chart_id,CHART_HEIGHT_IN_PIXELS); }
クラス本体の外側、ヘッダーの下
//+------------------------------------------------------------------+ //| Handling timeseries patterns | //+------------------------------------------------------------------+
それぞれの特定のパターンを処理するためのすべてのメソッドを削除します。削除されたメソッドの代わりに、パターンタイプを示す新しいメソッドを設定します。
//+------------------------------------------------------------------+ //| Handling timeseries patterns | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Set the flag of using the specified pattern | //| and create a control object if it does not exist yet | //+------------------------------------------------------------------+ void CTimeSeriesCollection::SetUsedPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const string symbol,const ENUM_TIMEFRAMES timeframe,const bool flag) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetUsedPattern(pattern,param,timeframe,flag); } //+------------------------------------------------------------------+ //| Return the flag of using the specified pattern | //+------------------------------------------------------------------+ bool CTimeSeriesCollection::IsUsedPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const string symbol,const ENUM_TIMEFRAMES timeframe) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); return(timeseries!=NULL ? timeseries.IsUsedPattern(pattern,param,timeframe) : false); } //+------------------------------------------------------------------+ //| Draw marks of the specified pattern on the chart | //+------------------------------------------------------------------+ void CTimeSeriesCollection::DrawPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const string symbol,const ENUM_TIMEFRAMES timeframe,const bool redraw=false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.DrawPattern(pattern,param,timeframe,redraw); } //+------------------------------------------------------------------+ //| Redraw the bitmap objects of the specified pattern on the chart | //+------------------------------------------------------------------+ void CTimeSeriesCollection::RedrawPattern(const ENUM_PATTERN_TYPE pattern,MqlParam ¶m[],const string symbol,const ENUM_TIMEFRAMES timeframe,const bool redraw=false) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.RedrawPattern(pattern,param,timeframe,redraw); } //+------------------------------------------------------------------+ //| Set chart parameters for pattern management objects | //| on the specified symbol and timeframe | //+------------------------------------------------------------------+ void CTimeSeriesCollection::SetChartPropertiesToPattCtrl(const string symbol,const ENUM_TIMEFRAMES timeframe,const double price_max,const double price_min,const int scale,const int height_px,const int width_px) { CTimeSeriesDE *timeseries=this.GetTimeseries(symbol); if(timeseries!=NULL) timeseries.SetChartPropertiesToPattCtrl(timeframe,price_max,price_min,scale,height_px,width_px); }
各メソッドは、パターンを処理するチャートの必要な時間枠とシンボル、およびパターン制御オブジェクトの選択に使用される必要なパラメータを受け取ります。次に、必要な時系列へのポインタを取得し、同じ名前の銘柄時系列クラスメソッドを呼び出した結果を返します。
イベントハンドラを実装しましょう。
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CTimeSeriesCollection::OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam) { //--- Get the current chart object CChartObj *chart=this.m_charts.GetChart(this.m_charts.GetMainChartID()); if(chart==NULL) return; //--- Get the main window object of the current chart CChartWnd *wnd=this.m_charts.GetChartWindow(chart.ID(),0); if(wnd==NULL) return; //--- If the chart is changed bool res=false; if(id==CHARTEVENT_CHART_CHANGE) { //--- control the change in the chart scale int scale=(int)::ChartGetInteger(chart.ID(),CHART_SCALE); if(this.m_chart_scale!=scale) { this.m_chart_scale=scale; res=true; } //--- control the change in the chart width in pixels int chart_wpx=(int)::ChartGetInteger(chart.ID(),CHART_WIDTH_IN_PIXELS); if(this.m_chart_wpx!=chart_wpx) { this.m_chart_wpx=chart_wpx; res=true; } //--- control the change in the chart height in pixels int chart_hpx=(int)::ChartGetInteger(chart.ID(),CHART_HEIGHT_IN_PIXELS); if(this.m_chart_hpx!=chart_hpx) { this.m_chart_hpx=chart_hpx; res=true; } //--- control the change in the chart maximum double chart_max=::ChartGetDouble(chart.ID(),CHART_PRICE_MAX); if(this.m_chart_max!=chart_max) { this.m_chart_max=chart_max; res=true; } //--- control the change in the chart minimum double chart_min=::ChartGetDouble(chart.ID(),CHART_PRICE_MIN); if(this.m_chart_min!=chart_min) { this.m_chart_min=chart_min; res=true; } //--- If there is at least one change if(res) { //--- Write new values of the chart properties to the pattern management objects this.SetChartPropertiesToPattCtrl(chart.Symbol(),chart.Timeframe(),chart_max,chart_min,scale,chart_hpx,chart_wpx); //--- Get a list of patterns on the current chart CArrayObj *list=CSelect::ByPatternProperty(this.GetListAllPatterns(),PATTERN_PROP_SYMBOL,chart.Symbol(),EQUAL); list=CSelect::ByPatternProperty(list,PATTERN_PROP_PERIOD,chart.Timeframe(),EQUAL); //--- If the list of patterns is received if(list!=NULL) { //--- In a loop by the list of patterns, int total=list.Total(); for(int i=0;i<total;i++) { //--- get the next pattern object CPattern *pattern=list.At(i); if(pattern==NULL) continue; //--- set the new chart data to the pattern object pattern.SetChartWidthInPixels(this.m_chart_wpx); pattern.SetChartHeightInPixels(this.m_chart_hpx); pattern.SetChartScale(this.m_chart_scale); pattern.SetChartPriceMax(this.m_chart_max); pattern.SetChartPriceMin(this.m_chart_min); //--- Redraw the pattern bitmap object with new dimensions pattern.Redraw(false); } //--- Update the chart ::ChartRedraw(chart.ID()); } } } }
ハンドラのロジック全体は、コードのコメントに完全に記述されています。ここで、チャートサイズの変更が検出されると、新しいサイズが時系列パターン管理クラスに渡され、新しいチャートサイズに応じてパターンが再描画されます。
ここで、\MQL5\Include\DoEasy\Engine.mqhにあるCEngineライブラリのメインクラスを変更してみましょう。
こちらのクラス本体にも、パターンを処理するための長いメソッドリストがあります。まず、パターンをドット形式で描画する役割を持つメソッドだけを削除しましょう。残りのメソッドは改善が必要で、パターンのパラメータをMqlParam構造体の配列で渡す仕様に変更します。まだ使用されていないメソッドでは、最低限パターンローソク足の最小サイズを送るための構造体フィールドのみを入力します。以下が例です。
//--- Set the flag for using the Harami pattern and create a control object if it does not already exist void SeriesSetUsedPatternHarami(const string symbol,const ENUM_TIMEFRAMES timeframe,const bool flag) { MqlParam param[]={}; if(::ArrayResize(param,1)==1) { param[0].type=TYPE_UINT; param[0].integer_value=0; } this.m_time_series.SetUsedPattern(PATTERN_TYPE_HARAMI,param,CorrectSymbol(symbol),CorrectTimeframe(timeframe),flag); }
まだ使用されていないパターン用の残りのメソッドはほぼ同じ内容なので、ここで詳しく触れる必要はありません。これらのメソッドは記事に添付されているファイルでご確認いただけます。
では、ライブラリ内ですでに作成されているパターン処理用のメソッドを見ていきましょう。
以下は、アウトサイドバー(エングルフィング)パターンを使用するためのフラグを設定し、コントロールオブジェクトがまだ存在しない場合はそれを作成するメソッドです。
//--- Set the flag for using the Pattern Outside and create a control object if it does not already exist void SeriesSetUsedPatternOutsideBar(const string symbol,const ENUM_TIMEFRAMES timeframe, const bool flag, // Price Action External Bar usage flag const double ratio_candles=70, // Percentage ratio of the size of the engulfing color to the size of the engulfed one const double ratio_body_to_shadows=80, // Percentage ratio of shadow sizes to candle body size const uint min_body_size=3) // Minimum candle body size { MqlParam param[]={}; if(::ArrayResize(param,3)==3) { param[0].type=TYPE_UINT; param[0].integer_value=min_body_size; //--- Percentage ratio of the size of the engulfing color to the size of the engulfed one param[1].type=TYPE_DOUBLE; param[1].double_value=ratio_candles; //--- Percentage ratio of shadow sizes to candle body size param[2].type=TYPE_DOUBLE; param[2].double_value=ratio_body_to_shadows; } this.m_time_series.SetUsedPattern(PATTERN_TYPE_OUTSIDE_BAR,param,CorrectSymbol(symbol),CorrectTimeframe(timeframe),flag); }
以下は、はらみ線パターンを使用するためのフラグを設定し、コントロールオブジェクトがまだ存在しない場合はそれを作成するメソッドです。
//--- Set the flag for using the Pattern Inside and create a control object if it does not already exist void SeriesSetUsedPatternInsideBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const bool flag,const uint min_body_size=3) { MqlParam param[]={}; if(::ArrayResize(param,1)==1) { param[0].type=TYPE_UINT; param[0].integer_value=min_body_size; } this.m_time_series.SetUsedPattern(PATTERN_TYPE_INSIDE_BAR,param,CorrectSymbol(symbol),CorrectTimeframe(timeframe),flag); }
このメソッドでは、すべてのパターンに共通する最小ローソク足サイズのプロパティを除いて、パターンにはカスタマイズ可能なプロパティがないため、構造体の1つのフィールドのみが入力されます。
以下は、ピンバーパターンを使用するためのフラグを設定し、コントロールオブジェクトがまだ存在しない場合はそれを作成するメソッドです。
//--- Set the flag for using the Pin Bar pattern and create a control object if it does not already exist void SeriesSetUsedPatternPinBar(const string symbol,const ENUM_TIMEFRAMES timeframe, const bool flag, // Price Action Pin Bar usage flag const double ratio_body=30, // Percentage ratio of the candle body to the full size of the candle const double ratio_larger_shadow=60, // Percentage ratio of the size of the larger shadow to the size of the candle const double ratio_smaller_shadow=30, // Percentage ratio of the size of the smaller shadow to the size of the candle const uint min_body_size=3) // Minimum candle body size { MqlParam param[]={}; if(::ArrayResize(param,4)==4) { param[0].type=TYPE_UINT; param[0].integer_value=min_body_size; //--- Percentage ratio of the candle body to the full size of the candle param[1].type=TYPE_DOUBLE; param[1].double_value=ratio_body; //--- Percentage ratio of the size of the larger shadow to the size of the candle param[2].type=TYPE_DOUBLE; param[2].double_value=ratio_larger_shadow; //--- Percentage ratio of the size of the smaller shadow to the size of the candle param[3].type=TYPE_DOUBLE; param[3].double_value=ratio_smaller_shadow; } this.m_time_series.SetUsedPattern(PATTERN_TYPE_PIN_BAR,param,CorrectSymbol(symbol),CorrectTimeframe(timeframe),flag); }
チャート上にパターンを表示するためのメソッドを見てみましょう。
- ライブラリに既に実装されているパターン用
- まだ作成されていないパターン用
//--- Draw Pattern Outside labels on the chart void SeriesDrawPatternOutsideBar(const string symbol,const ENUM_TIMEFRAMES timeframe, const double ratio_candles=70, // Percentage ratio of the size of the engulfing color to the size of the engulfed one const double ratio_body_to_shadows=80, // Percentage ratio of shadow sizes to candle body size const uint min_body_size=3, // Minimum candle body size const bool redraw=false) // Chart redraw flag { MqlParam param[]={}; if(::ArrayResize(param,3)==3) { param[0].type=TYPE_UINT; param[0].integer_value=min_body_size; //--- Percentage ratio of the size of the engulfing color to the size of the engulfed one param[1].type=TYPE_DOUBLE; param[1].double_value=ratio_candles; //--- Percentage ratio of shadow sizes to candle body size param[2].type=TYPE_DOUBLE; param[2].double_value=ratio_body_to_shadows; } this.m_time_series.DrawPattern(PATTERN_TYPE_OUTSIDE_BAR,param,CorrectSymbol(symbol),CorrectTimeframe(timeframe),redraw); } //--- Draw Inside Bar pattern labels on the chart void SeriesDrawPatternInsideBar(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint min_body_size=3,const bool redraw=false) { MqlParam param[]={}; if(::ArrayResize(param,1)==1) { param[0].type=TYPE_UINT; param[0].integer_value=min_body_size; } this.m_time_series.DrawPattern(PATTERN_TYPE_INSIDE_BAR,param,CorrectSymbol(symbol),CorrectTimeframe(timeframe),redraw); } //--- Draw Pin Bar pattern labels on the chart void SeriesDrawPatternPinBar(const string symbol,const ENUM_TIMEFRAMES timeframe, const double ratio_body=30, // Percentage ratio of the candle body to the full size of the candle const double ratio_larger_shadow=60, // Percentage ratio of the size of the larger shadow to the size of the candle const double ratio_smaller_shadow=30, // Percentage ratio of the size of the smaller shadow to the size of the candle const uint min_body_size=3, // Minimum candle body size const bool redraw=false) // Chart redraw flag { MqlParam param[]={}; if(::ArrayResize(param,4)==4) { param[0].type=TYPE_UINT; param[0].integer_value=min_body_size; //--- Percentage ratio of the candle body to the full size of the candle param[1].type=TYPE_DOUBLE; param[1].double_value=ratio_body; //--- Percentage ratio of the size of the larger shadow to the size of the candle param[2].type=TYPE_DOUBLE; param[2].double_value=ratio_larger_shadow; //--- Percentage ratio of the size of the smaller shadow to the size of the candle param[3].type=TYPE_DOUBLE; param[3].double_value=ratio_smaller_shadow; } this.m_time_series.DrawPattern(PATTERN_TYPE_PIN_BAR,param,CorrectSymbol(symbol),CorrectTimeframe(timeframe),redraw); if(redraw) ::ChartRedraw(); } //--- Draw Rails pattern labels on the chart void SeriesDrawPatternRails(const string symbol,const ENUM_TIMEFRAMES timeframe,const bool redraw=false) { MqlParam param[]={}; if(::ArrayResize(param,1)==1) { param[0].type=TYPE_UINT; param[0].integer_value=0; } this.m_time_series.DrawPattern(PATTERN_TYPE_RAILS,param,CorrectSymbol(symbol),CorrectTimeframe(timeframe),redraw); }
他のパターンに関する残りのメソッドは、まだ実装されていないRailsパターンのメソッドと同様であり、ここで取り上げる必要はありません。
クラス本体の最後でイベントハンドラを宣言します。
public: //--- Create and return the composite magic number from the specified magic number value, the first and second group IDs and the pending request ID uint SetCompositeMagicNumber(ushort magic_id,const uchar group_id1=0,const uchar group_id2=0,const uchar pending_req_id=0); //--- Event handler void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam); //--- Handling DoEasy library events void OnDoEasyEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- Working with events in the tester void EventsHandling(void); };
クラス本体の外側で実装しましょう。
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CEngine::OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam) { this.m_graph_objects.OnChartEvent(id,lparam,dparam,sparam); this.m_time_series.OnChartEvent(id,lparam,dparam,sparam); }
ここでは、まずグラフィカルオブジェクトコレクションクラスのイベントハンドラが呼ばれ、その後に時系列コレクションクラスのイベントハンドラが呼ばれます。
ライブラリのCEngine::OnDoEasyEvent()メソッド内、具体的には時系列イベント処理ブロックに、新しいパターンイベントを将来処理するためのブロックを追加します。
//--- Handling timeseries events else if(idx>SERIES_EVENTS_NO_EVENT && idx<SERIES_EVENTS_NEXT_CODE) { //--- "New bar" event if(idx==SERIES_EVENTS_NEW_BAR) { ::Print(DFUN,TextByLanguage("Новый бар на ","New Bar on "),sparam," ",TimeframeDescription((ENUM_TIMEFRAMES)dparam),": ",TimeToString(lparam)); CArrayObj *list=this.m_buffers.GetListBuffersWithID(); if(list!=NULL) { int total=list.Total(); for(int i=0;i<total;i++) { CBuffer *buff=list.At(i); if(buff==NULL) continue; string symbol=sparam; ENUM_TIMEFRAMES timeframe=(ENUM_TIMEFRAMES)dparam; if(buff.TypeBuffer()==BUFFER_TYPE_DATA || buff.IndicatorType()==WRONG_VALUE) continue; if(buff.Symbol()==symbol && buff.Timeframe()==timeframe ) { CSeriesDE *series=this.SeriesGetSeries(symbol,timeframe); if(series==NULL) continue; int count=::fmin((int)series.AvailableUsedData(),::fmin(buff.GetDataTotal(),buff.IndicatorBarsCalculated())); this.m_buffers.PreparingDataBufferStdInd(buff.IndicatorType(),buff.ID(),count); } } } } //--- "Bars skipped" event if(idx==SERIES_EVENTS_MISSING_BARS) { ::Print(DFUN,TextByLanguage("Пропущены бары на ","Missed bars on "),sparam," ",TimeframeDescription((ENUM_TIMEFRAMES)dparam),": ",(string)lparam); } //--- "New pattern" event if(idx==SERIES_EVENTS_PATTERN) { // New pattern event is handled here } }
ここはまだ空の状態ですが、将来的にパターンイベントを送信する際には、新しいパターンの出現をここで処理します。
これでライブラリの修正は完了です。次に、新しいパターンの検出とチャートの水平スケールの変更処理を確認しましょう。
テスト
テストを実行するには、前の記事のEAを使用して、新しい\MQL5\Experts\TestDoEasy\Part136\フォルダにTestDoEasy136.mq5として保存します。
設定からドットでパターンを描画するためのフラグを削除します。
sinput double InpPinBarRatioBody = 30.0; // Pin Bar Ratio Body to Candle size sinput double InpPinBarRatioLarger = 60.0; // Pin Bar Ratio Larger shadow to Candle size sinput double InpPinBarRatioSmaller= 30.0; // Pin Bar Ratio Smaller shadow to Candle size sinput bool InpDrawPatternsAsDots= true; // Draw Patterns as dots
「アウトサイドバー」パターンを検索するための設定とパラメータに、パターンを使用するためのフラグを追加します。
sinput ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; // Mode of used symbols list sinput string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; // List of used symbols (comma - separator) sinput ENUM_TIMEFRAMES_MODE InpModeUsedTFs = TIMEFRAMES_MODE_CURRENT; // Mode of used timeframes list sinput string InpUsedTFs = "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; // List of used timeframes (comma - separator) sinput ENUM_INPUT_YES_NO InpSearchPinBar = INPUT_YES; // Search for Pin Bar patterns sinput double InpPinBarRatioBody = 30.0; // Pin Bar Ratio Body to Candle size sinput double InpPinBarRatioLarger = 60.0; // Pin Bar Ratio Larger shadow to Candle size sinput double InpPinBarRatioSmaller= 30.0; // Pin Bar Ratio Smaller shadow to Candle size sinput ENUM_INPUT_YES_NO InpSearchInsideBar = INPUT_YES; // Search for Inside Bar patterns sinput ENUM_INPUT_YES_NO InpSearchOutsideBar = INPUT_YES; // Search for Outside Bar patterns sinput double InpOBRatioCandles = 50.0; // Outside Bar Ratio of sizes of neighboring candles sinput double InpOBRatioBodyToCandle= 50.0; // Outside Bar Ratio Body to Candle size sinput ENUM_INPUT_YES_NO InpUseBook = INPUT_NO; // Use Depth of Market sinput ENUM_INPUT_YES_NO InpUseMqlSignals = INPUT_NO; // Use signal service sinput ENUM_INPUT_YES_NO InpUseCharts = INPUT_NO; // Use Charts control sinput ENUM_INPUT_YES_NO InpUseSounds = INPUT_YES; // Use sounds
パターンの使用を設定するメソッドは、フラグが設定されるとすぐにパターンを検索して見つかったパターンを表示するため、テストでは、OnInit()ハンドラでフラグを設定するだけで、すぐにパターンの検索を開始し、チャートに表示できます。
//--- Clear the list of all patterns engine.GetListAllPatterns().Clear(); //--- Set the flag of using the Pin Bar pattern with the parameters specified in the settings engine.SeriesSetUsedPatternPinBar(NULL,PERIOD_CURRENT,(bool)InpSearchPinBar,InpPinBarRatioBody,InpPinBarRatioLarger,InpPinBarRatioSmaller); //--- Set the flag of using the Inside Bar pattern engine.SeriesSetUsedPatternInsideBar(NULL,PERIOD_CURRENT,(bool)InpSearchInsideBar); //--- Set the flag of using the Outside Bar pattern engine.SeriesSetUsedPatternOutsideBar(NULL,PERIOD_CURRENT,(bool)InpSearchOutsideBar,InpOBRatioCandles,InpOBRatioBodyToCandle); //--- ChartRedraw(); return(INIT_SUCCEEDED); }
OnChartEvent() EAハンドラで、Engineライブラリのメインオブジェクトに対してこのハンドラの呼び出しを追加します。
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- If working in the tester, exit if(MQLInfoInteger(MQL_TESTER)) return; //--- Handling mouse events if(id==CHARTEVENT_OBJECT_CLICK) { //--- Handle pressing the buttons in the panel if(StringFind(sparam,"BUTT_")>0) PressButtonEvents(sparam); } //--- Handling DoEasy library events if(id>CHARTEVENT_CUSTOM-1) { OnDoEasyEvent(id,lparam,dparam,sparam); } engine.OnChartEvent(id,lparam,dparam,sparam); //--- Chart change if(id==CHARTEVENT_CHART_CHANGE) { //--- Whenever the chart changes, hide all information panels //... ... ...
ライブラリイベントハンドラ:OnDoEasyEvent()関数
//+------------------------------------------------------------------+ //| Handling DoEasy library events | //+------------------------------------------------------------------+ void OnDoEasyEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { int idx=id-CHARTEVENT_CUSTOM; //--- Retrieve (1) event time milliseconds, (2) reason and (3) source from lparam, as well as (4) set the exact event time
時系列イベントを処理するブロックで、そのようなイベントが発生した場合に、見逃されたバーに関するメッセージを追加します。
//--- Handling timeseries events else if(idx>SERIES_EVENTS_NO_EVENT && idx<SERIES_EVENTS_NEXT_CODE) { //--- "New bar" event if(idx==SERIES_EVENTS_NEW_BAR) { Print(TextByLanguage("Новый бар на ","New Bar on "),sparam," ",TimeframeDescription((ENUM_TIMEFRAMES)dparam),": ",TimeToString(lparam)); } //--- "Bars skipped" event if(idx==SERIES_EVENTS_MISSING_BARS) { Print(TextByLanguage("Пропущены бары на ","Missing bars on "),sparam," ",TimeframeDescription((ENUM_TIMEFRAMES)dparam),": ",lparam); } } //--- Handle chart auto events
現時点では、これは技術的に特別な機能を提供するわけではなく、単に操作ログに見逃されたバーの本数を表示するだけです。しかし、たとえばEAがターミナルで稼働中に接続が切断された場合や、コンピューターがスリープモードに入り、その後復帰した場合でも、ライブラリがバーのスキップを適切に処理していることが確認できます。つまり、このようなイベントを検出した際に、必要な数のバーを再取得することで、欠落していた時系列データやパターン情報を復元できるようになります。この機能は将来的に実装される予定です。
それでは、EAをコンパイルして起動し、アウトサイドバーパターンを検索するために、以下のパラメータを設定してみましょう。
できるだけ多くのパターンが見つかるように、ローソク足の比率に意図的に小さな値を設定します。
ローソク足の比率が通常の値(50%以上)の場合、パターンはより正確になりますが、非常にまれです。
起動後、アウトサイドバーパターンが検出され、表示されます。
パターンが見つかったことがわかります。チャートのサイズを変更すると、パターンアイコンのサイズも変わります。
次の段階
次回のプライスフォーメーションに関する記事では、引き続きさまざまなパターンを作成し、それらが出現した際にイベントを送信する処理を実装していきます。
作成されたファイルはすべて記事に添付されており、自習やテスト用にダウンロードすることができます。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/14710





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