MQL5パネルEA・チャートUI実務ノート|表示系と判定系を分ける設計
MQL5でパネルEAや裁量補助EAを作る場合、チャート上にボタン、ラベル、ステータス表示、価格ライン、状態パネルなどを配置することがあります。
こうしたチャートUIは、利用者にとって分かりやすい反面、設計を間違えるとEA本体の不安定化につながります。ボタンを押した瞬間に直接発注してしまう、表示ラベルの文字を判定に使ってしまう、Objectが残って誤参照される、毎tickで表示を作り直して重くなる、といった問題が起きやすくなります。
パネルEA・チャートUIで重要なのは、表示系と判定系を分けることです。UIは状態を表示し、操作要求を受け付ける層です。売買判定、発注、決済、リスク制御は、UIとは別のruntime stateやexecution層で扱う必要があります。
この記事では、MQL5パネルEA・チャートUIを安全に扱うための設計判断を、開発実務ノートとして整理します。完成EAソース全文、外部シート制御ロジック、認証ロジック、endpoint、token、URL実値、実運用set値、中核Entry条件は公開しません。
本記事は、投資判断や売買指示ではなく、MQL5のパネルEA・チャートUIを設計・検証・サポートしやすくするための技術記事です。
- この記事で扱う実務課題
- なぜ表示系と判定系を分ける必要があるのか
- パネルEAで分けるべき責務
- OnChartEventから直接発注しない
- UI requestとは何か
- runtime stateを表示の真実にする
- Object名prefixと所有範囲を決める
- 実コード例1:UI requestを構造体で扱う
- 実コード例2:OnChartEventではrequest化だけ行う
- 実コード例3:runtime stateからパネル表示を作る
- 実コード例4:UI requestをexecution前のgateへ渡す
- 表示更新を毎tickで重くしない
- UI操作ログで確認するポイント
- Object cleanupとOnDeinitの考え方
- よくある失敗例
- 導入前・開発依頼前に整理する情報
- 関連する技術講座・確認ページ
- FAQ
- 関連する開発実務ノート
- まとめ
この記事で扱う実務課題
| 項目 | この記事で扱う内容 | 扱わない内容 |
|---|---|---|
| 目的 | パネルEA・チャートUIで表示系と判定系を分ける設計 | 利益を狙う売買ロジック、具体的なEntry条件 |
| 主な対象 | OBJ_LABEL、OBJ_BUTTON、OnChartEvent、UI request、runtime state、Object管理 | 完成EAソース全文、実運用コード全文 |
| 設計観点 | 誤操作防止、表示崩れ防止、Object残存対策、ログ追跡、販売後サポート | 認証ロジック、外部制御、実運用set値 |
| コード例 | 一般化したUI request、PanelState、OnChartEvent、表示更新例 | 実発注処理、実Entry条件、独自優位性のある判定式 |
| 読者 | MQL5開発者、パネルEAを依頼したい人、検証担当者、販売前チェック担当者 | 売買判断や推奨設定を知りたい人 |
なぜ表示系と判定系を分ける必要があるのか
パネルEAでは、画面に表示されている情報と、EA内部の真の状態が一致しているように見えます。しかし、表示はあくまで結果です。EAの判定や実行の真実は、runtime state、position snapshot、entry candidate、execution resultなどで管理するべきです。
表示系と判定系を混ぜると、次のような問題が起きます。
| 問題 | 起きる原因 | 分けるべき責務 |
|---|---|---|
| 表示はREADYなのに発注しない | UI表示とruntime stateがずれている | UI / runtime |
| ボタンを押したら即発注される | OnChartEventから直接OrderSendしている | UI request / execution |
| 古いラインを判定に使う | 残存Objectをruntime stateとして扱っている | Object表示 / 判定状態 |
| 毎tickで重くなる | 表示Objectを毎回削除・再作成している | render / maintenance |
| 他EAや手動ラインを消す | Object名prefixやowner判定がない | Object管理 / cleanup |
| ログを見ても操作理由が分からない | UI操作、request、execution結果が同じログになっている | UI / request / execution / log |
UIは、EA内部状態を表示するための層です。UIの文字やObject状態を読み返して売買判定に使う設計は避けるべきです。
パネルEAで分けるべき責務
パネルEAでは、通常のEA設計に加えて、UI関係の責務を明確に分けます。
| 責務 | 役割 | 混同すると起きる問題 |
|---|---|---|
| runtime state | EA内部の真の状態を保持する | 表示と内部状態がずれる |
| UI render | runtime stateを画面に表示する | 表示更新失敗が判定に影響する |
| UI request | クリック操作を操作要求として受け取る | ボタン連打や誤クリックが直接発注につながる |
| execution | 発注・決済・modifyを実行する | UI操作と注文結果が混ざる |
| risk / gate | 実行前の安全確認を行う | クリックだけでrisk確認を飛ばす |
| Object lifecycle | Object作成・更新・削除・残存監査を扱う | Object残存や誤削除が起きる |
| log | 操作、表示、実行、失敗理由を記録する | 問い合わせ時に原因を追えない |
特に重要なのは、UI requestとexecutionを分けることです。ボタンを押したことと、発注してよいことは別です。
OnChartEventから直接発注しない
OnChartEventは、チャート上のクリックやキー操作などを受け取るためのイベント関数です。パネルEAでは、OBJ_BUTTONをクリックした時にOnChartEventで処理することがあります。
ただし、OnChartEventから直接OrderSendや決済処理へ進む設計は避けるべきです。
| 直接実行する設計 | 起きやすい問題 |
|---|---|
| クリック直後にOrderSendする | spread、保有上限、duplicate gate、risk確認を飛ばしやすい |
| クリック連打をそのまま処理する | 重複発注や連続決済につながる |
| UI操作と注文結果を同じログにする | 操作要求なのか実行結果なのか分からなくなる |
| ボタン表示だけで許可状態を判断する | 内部状態と表示がずれた時に誤操作につながる |
安全な設計では、OnChartEventでは「ユーザーが何を要求したか」だけを記録し、実行前確認は別の層で行います。
UI requestとは何か
UI requestとは、ボタン操作やパネル操作を、即実行ではなく操作要求として保持する考え方です。
たとえば、BUYボタンが押された場合、すぐにOrderSendするのではなく、「BUY_REQUESTが発生した」としてruntimeに渡します。その後、execution層へ進む前に、AutoTrading、spread、ポジション状態、重複発注防止、risk条件などを確認します。
| 段階 | 役割 | ログ例 |
|---|---|---|
| UI click | ユーザー操作を受ける | BUTTON_CLICK |
| UI request | 操作要求として保存する | UI_REQUEST_SET |
| gate / risk | 実行してよいか確認する | UI_REQUEST_BLOCK / GATE_PASS |
| execution | 実行処理へ渡す | ROUTE_TO_EXECUTION |
| result | 結果を確認する | ORDER_RESULT / CLOSE_RESULT |
このように分けると、クリックされたが実行前条件で止まったのか、実行へ進んだがOrderSendで失敗したのかを切り分けやすくなります。
runtime stateを表示の真実にする
パネル表示は、runtime stateから作るべきです。チャート上のラベル文字を読み返してEAの状態を決める構造は避けます。
たとえば、画面に「READY」と表示されているかどうかではなく、EA内部のruntimeReady、tradeAllowed、entryBlockedReason、positionStateなどをもとに表示を作ります。
| 扱い | 推奨 | 理由 |
|---|---|---|
| 判定の真実 | runtime state | 表示遅延やObject残存の影響を受けにくい |
| 表示 | runtime stateから生成 | 内部状態と画面表示を対応させやすい |
| 操作 | UI requestとして受ける | クリックと実行を分けられる |
| ログ | UI、request、executionを分ける | 問い合わせ時に原因を追いやすい |
UIは状態を見せるためのものです。UIを状態の保存場所として使うと、表示崩れやObject残存がEAの判定に影響してしまいます。
Object名prefixと所有範囲を決める
チャートUIを使う場合、Object名のprefixを決めることが重要です。
prefixがないと、自EAのObject、他EAのObject、手動で引いたラインを区別しにくくなります。その結果、他EAや利用者のObjectを誤って削除・更新する可能性があります。
| Object分類 | 扱い | 注意点 |
|---|---|---|
| 自EAのUI Object | prefixで識別して管理する | ボタン、ラベル、パネル枠など |
| 自EAの表示ライン | 表示専用か判定参照対象かを分ける | 平均ライン、TP目安、Trail目安など |
| 他EAのObject | 原則触らない | prefix不一致なら対象外 |
| 手動Object | 原則触らない | 利用者の作業内容を壊さない |
| 残存Object | startup auditやresidual ignoreで扱う | 無条件に実ロジックへ使わない |
Object cleanupでは、「消したいものを全部消す」のではなく、「自EA所有が明確なものだけを対象にする」ことが重要です。
実コード例1:UI requestを構造体で扱う
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。
// 学習用に一般化したUI request例です。
// 実運用EAの完成ソースではありません。
enum UiRequestType
{
UI_REQUEST_NONE = 0,
UI_REQUEST_BUY,
UI_REQUEST_SELL,
UI_REQUEST_CANCEL
};
struct UiRequest
{
UiRequestType type;
datetime requestedAt;
string reason;
bool handled;
};
UiRequest g_uiRequest;
void ClearUiRequest()
{
g_uiRequest.type = UI_REQUEST_NONE;
g_uiRequest.requestedAt = 0;
g_uiRequest.reason = "NONE";
g_uiRequest.handled = true;
}
void SetUiRequest(UiRequestType type, string reason)
{
g_uiRequest.type = type;
g_uiRequest.requestedAt = TimeCurrent();
g_uiRequest.reason = reason;
g_uiRequest.handled = false;
Print("DIAG/UI_REQUEST: event=SET",
" type=", (int)type,
" reason=", reason);
}この例では、クリック操作を即実行せず、UiRequestとして保持しています。発注や決済へ進むかどうかは、別のgateやexecution層で判断します。
実コード例2:OnChartEventではrequest化だけ行う
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。
// 学習用に一般化したOnChartEvent例です。
// 実運用EAの完成ソースではありません。
string UiPrefix()
{
return "EAFC_UI_";
}
string UiObjectName(string part)
{
return UiPrefix() + part;
}
bool IsOwnUiObject(string name)
{
return (StringFind(name, UiPrefix()) == 0);
}
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(id != CHARTEVENT_OBJECT_CLICK)
return;
if(!IsOwnUiObject(sparam))
return;
Print("DIAG/UI: event=BUTTON_CLICK",
" object=", sparam);
if(sparam == UiObjectName("BuyButton"))
{
SetUiRequest(UI_REQUEST_BUY, "BUY_BUTTON_CLICK");
return;
}
if(sparam == UiObjectName("SellButton"))
{
SetUiRequest(UI_REQUEST_SELL, "SELL_BUTTON_CLICK");
return;
}
if(sparam == UiObjectName("CancelButton"))
{
SetUiRequest(UI_REQUEST_CANCEL, "CANCEL_BUTTON_CLICK");
return;
}
Print("DIAG/UI: event=CLICK_IGNORED",
" reason=UNKNOWN_OBJECT",
" object=", sparam);
}この例では、OnChartEventから直接OrderSendや決済処理へ進んでいません。自EAのUI Objectかを確認し、対象ボタンに応じてUI requestを作るだけにしています。
実コード例3:runtime stateからパネル表示を作る
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。
// 学習用に一般化したPanelState例です。
// 実運用EAの完成ソースではありません。
struct PanelState
{
bool runtimeReady;
bool tradeAllowed;
string modeText;
string statusText;
string lastReason;
};
PanelState BuildPanelState()
{
PanelState state;
state.runtimeReady = true;
state.tradeAllowed = true;
state.modeText = "MONITOR";
state.statusText = "READY";
state.lastReason = "OK";
return state;
}
void TracePanelState(const PanelState &state)
{
Print("DIAG/UI_RENDER: event=STATE",
" ready=", state.runtimeReady,
" trade_allowed=", state.tradeAllowed,
" status=", state.statusText,
" reason=", state.lastReason);
}PanelStateは、表示のために必要な状態だけをまとめた構造です。画面上のラベル文字を読み返して判定するのではなく、runtime stateから表示内容を作ります。
実コード例4:UI requestをexecution前のgateへ渡す
以下は、設計判断を理解するために一般化した学習用コード例です。完成EAのソース全文ではありません。実発注処理や中核Entry条件は含めていません。
// 学習用に一般化したUI request処理例です。
// 実運用EAの完成ソースではありません。
bool CanProcessUiRequest(const UiRequest &request)
{
if(request.type == UI_REQUEST_NONE)
return false;
if(request.handled)
return false;
// 実際のEAでは、AutoTrading、spread、position、
// duplicate gate、riskなどを別責務で確認します。
return true;
}
void ProcessUiRequest()
{
if(!CanProcessUiRequest(g_uiRequest))
return;
Print("DIAG/UI_REQUEST: event=PROCESS_START",
" type=", (int)g_uiRequest.type,
" reason=", g_uiRequest.reason);
if(g_uiRequest.type == UI_REQUEST_BUY)
{
Print("DIAG/UI_REQUEST: event=ROUTE",
" target=BUY_EXECUTION_CHECK");
}
else if(g_uiRequest.type == UI_REQUEST_SELL)
{
Print("DIAG/UI_REQUEST: event=ROUTE",
" target=SELL_EXECUTION_CHECK");
}
else if(g_uiRequest.type == UI_REQUEST_CANCEL)
{
Print("DIAG/UI_REQUEST: event=ROUTE",
" target=CANCEL_REQUEST");
}
ClearUiRequest();
}UI requestを処理する場合も、すぐに発注へ進むのではなく、execution前の確認へ渡します。クリック操作、発注前gate、OrderSend結果を分けることで、不具合調査がしやすくなります。
表示更新を毎tickで重くしない
パネルEAでは、表示更新の頻度にも注意が必要です。
OnTickごとにすべてのObjectを削除して再作成すると、チャートがちらつき、MT5が重くなり、Object残存や表示崩れの原因になります。
| 避けたい設計 | 推奨する設計 |
|---|---|
| 毎tickで全Objectを削除・再作成する | 初回作成後はテキストや色だけ差分更新する |
| 毎tickで全Objectを走査する | OnTimerや状態変化時のmaintenanceへ分ける |
| 表示失敗でEA本体を止める | 表示はbest-effortにし、売買処理と分ける |
| 毎tickでUI_RENDERログを大量に出す | 状態変化時または一定間隔でsummary化する |
| 座標やサイズを毎回再設定する | 初期化時に設定し、必要時だけ更新する |
表示更新は重要ですが、売買判定や既存ポジション管理より優先されるべきではありません。表示系は軽く、追跡可能で、失敗しても本体処理へ過度に波及しない設計が安全です。
UI操作ログで確認するポイント
パネルEAの不具合調査では、UI操作、request化、実行前確認、execution結果を分けてログで追えることが重要です。
| ログ区分 | ログ例 | 確認できること |
|---|---|---|
| UI | BUTTON_CLICK、CLICK_IGNORED | どのObjectがクリックされたか |
| UI_REQUEST | SET、PROCESS_START、BLOCK、ROUTE | 操作がrequest化され、処理対象になったか |
| GATE | GATE_PASS、GATE_BLOCK | 実行前安全確認を通過したか |
| RISK | RISK_BLOCK、LOT_INVALID | risk条件で止めたか |
| ORDER | ORDER_SEND_OK、ORDER_SEND_FAIL | 注文結果 |
| UI_RENDER | STATE、UPDATE、SKIP | 表示更新の状態 |
| OBJECT_AUDIT | CREATE_OK、CREATE_FAIL、RESIDUAL_IGNORE | Object作成・残存・監査状況 |
「ボタンを押したのに動かない」という相談でも、UIクリックが認識されていないのか、request化されたがGATEで止まったのか、executionで失敗したのかは別です。
Object cleanupとOnDeinitの考え方
パネルEAでは、EA削除時にパネル部品を消したい場合があります。
自EA所有が明確なUI Objectであれば、OnDeinitで整理しやすい対象です。ただし、価格ラインや残存監査対象のObjectまで広範囲に消す設計は避けるべきです。
| Object種別 | OnDeinitでの扱い | 理由 |
|---|---|---|
| 自EAのボタン | 削除候補 | UI部品として所有範囲が明確なため |
| 自EAのステータスラベル | 削除候補 | 表示専用で再作成しやすいため |
| 平均ライン・Trailライン | 方針次第 | 表示専用かロジック参照対象かで扱いが変わるため |
| 手動ライン | 削除しない | 利用者の作業内容を壊さないため |
| 他EAのObject | 削除しない | 他EAへ影響するため |
OnDeinitで何を消すかは、Object名prefix、Object種別、所有範囲を基準にします。広範囲のObject走査や全削除は、EA削除時の不安定化につながる場合があります。
よくある失敗例
失敗1:OnChartEventから直接OrderSendする
クリックイベントから直接発注すると、spread、risk、duplicate gate、position stateの確認を飛ばしやすくなります。OnChartEventではUI requestを作るまでに留め、実行はexecution層へ渡します。
失敗2:表示ラベルの文字を判定に使う
ラベルは表示結果です。判定の真実はruntime stateで管理し、ラベルはそこから生成するべきです。
失敗3:毎tickでObjectを作り直す
毎tickでObjectDeleteとObjectCreateを繰り返すと、ちらつき、重さ、残存Objectの原因になります。初回作成後は差分更新を基本にします。
失敗4:Object名prefixがない
prefixがないと、自EAのObject、他EAのObject、手動Objectを区別できません。誤削除や誤更新を防ぐため、Object名の命名規則を決めます。
失敗5:UI表示失敗でEA本体を止める
表示更新失敗と売買処理の失敗は分けるべきです。表示はbest-effort、判定とexecutionはruntime stateで管理します。
失敗6:ボタン連打対策がない
ボタン連打をそのまま処理すると、重複requestや重複発注につながります。UI requestのhandled管理、cooldown、duplicate gateを使います。
失敗7:表示系とObject lifecycleを分けていない
パネル部品、価格ライン、残存Object、ロジック参照Objectは扱いが違います。すべてを同じcleanup方針にすると、表示崩れや誤参照につながります。
導入前・開発依頼前に整理する情報
パネルEAやチャートUI付きEAを開発・改修する場合、次の情報を整理しておくと仕様確認がスムーズになります。
- 表示したい項目
- ボタン操作の種類
- クリック後に即実行するのか、確認段階を挟むのか
- BUY / SELL / CANCEL / RESETなどの操作範囲
- UI requestの有効期限
- ボタン連打対策
- runtime stateとして保持する項目
- UIに表示する項目
- Object名prefix
- OnDeinitで削除するUI Objectの範囲
- 残す価格ラインと消すパネル部品の違い
- UI操作ログの粒度
- スマホ・VPS・複数チャートでの表示確認方針
パネルEAは見た目の仕様が目立ちますが、実務上は「どの操作がどのrequestになり、どのgateを通り、どのexecutionへ進むか」を先に整理することが重要です。
関連する技術講座・確認ページ
| ページ | 確認できること |
|---|---|
| 技術講座ハブ | MQL5やMT5開発に関する講座一覧 |
| MQL5チャートオブジェクト・UI表示完全ガイド | OBJ_LABEL、OBJ_BUTTON、チャートUIの基本 |
| MQL5デバッグ・ログファースト開発完全ガイド | ログ確認、デバッグ、原因調査の基本 |
| MT5開発依頼前に用意する資料まとめ | 仕様書、setファイル、ログ、スクリーンショットの整理 |
| 不具合報告・調査依頼について | 不具合相談前に送る情報 |
| よくある質問 | 導入、確認、相談前のFAQ |
| 免責事項・リスク説明 | 投資判断、損益、利用上の注意に関する確認 |
FAQ
パネルEAではボタンを押したらすぐ発注する設計でもよいですか?
避けた方が安全です。クリック操作はUI requestとして受け取り、spread、position、duplicate gate、riskなどの確認を通してからexecutionへ渡す設計が安全です。
表示ラベルの文字をEA内部の判定に使ってよいですか?
避けるべきです。表示ラベルはruntime stateを表示した結果です。判定の真実はruntime state、position snapshot、entry candidateなどで管理します。
OnChartEventでは何をするべきですか?
OnChartEventでは、クリックされたObjectを確認し、UI requestを作るところまでに留めるのが安全です。発注・決済・risk判断は別の責務へ分けます。
Object名にprefixは必要ですか?
必要です。prefixがないと、自EAのObject、他EAのObject、手動Objectを区別できず、誤削除や誤更新の原因になります。
EA削除時にパネル部品は消してよいですか?
自EA所有が明確なUI Objectであれば削除候補になります。ただし、価格ラインや残存監査対象のObjectまで広範囲に消す設計は慎重に判断する必要があります。
表示更新は毎tickで行ってよいですか?
内容によりますが、毎tickでObject削除・再作成するのは避けるべきです。初回作成後は差分更新、状態変化時更新、一定間隔更新などに分ける方が安全です。
パネル表示が失敗したらEA本体も停止すべきですか?
通常は分けて考えます。表示系の失敗と売買処理の失敗は別です。表示はbest-effort、判定やexecutionはruntime stateで管理します。
実装コードがなくてもパネルEAの相談はできますか?
可能です。表示項目、操作ボタン、クリック後の挙動、確認段階の有無、Object cleanup方針、ログに残したい項目が整理されていれば、相談を進めやすくなります。
関連する開発実務ノート
パネルEAやチャートUIを扱う場合は、表示系だけでなく、EA本体の責務分離、ログ設計、長時間稼働、商品化前のUI整理もあわせて確認しておくと安全です。
- MQL5 EA設計実務ノート|signal・execution・risk・exitを分ける理由
- MQL5ログファースト開発実務ノート|不具合調査しやすいEA・インジの作り方
- MQL5長時間稼働EA実務ノート|フリーズ・重い・オブジェクト残存を防ぐ考え方
- MQL5商品化・配布前チェック実務ノート|Inputs・HELP・Snapshot・manual・UserLive化の基本
まとめ
MQL5パネルEA・チャートUIでは、見た目を作ることよりも、表示系と判定系を分けることが重要です。
特に、次の4点を分けると、誤操作、表示崩れ、重複発注、Object残存、不具合調査の問題を減らしやすくなります。
- runtime state:EA内部の真の状態を保持する
- UI render:runtime stateを画面へ表示する
- UI request:クリック操作を即実行せず、操作要求として扱う
- execution / risk:発注・決済・安全確認をUIとは別責務で扱う
OnChartEventは、クリックイベントを受ける入口です。そこから直接OrderSendや決済へ進むのではなく、UI requestを作り、別のexecution層で安全確認してから処理する構成が安全です。
また、Object名prefix、Object lifecycle、OnDeinitでのcleanup範囲、表示更新頻度、UI操作ログを設計しておくことで、販売後サポートや不具合調査もしやすくなります。
完成EAの中核ロジックを公開しなくても、パネルEA・チャートUIの責務分離を理解しておくことで、開発依頼、既存EA改修、検証、不具合調査、販売後サポートの品質を上げやすくなります。

