MT5でパネルEAを改修(キー操作機能追加、テスターでの動作機能追加)する【MQLプログラミングの基礎】
1.キーボード操作によるエントリー・決済機能を追加
ファイルの新規作成はせず、以前の記事「MT5版のボタン操作でエントリーするEAを作成する【MQLプログラミングの基礎】」で作成した、「TradePanel.mq5」を改修して機能を追加します。
※この記事はMQL5の内容です。
チャートにセットすると、下記のように表示されます。

ファイル「TradePanel.mq5」をMetaEditorで開き、編集して機能を追加していきます。
改修するので、8行目の「#property version “1.00”」の1.00を1.01にします。
OnChartEvent関数内のif文(if (id == CHARTEVENT_OBJECT_CLICK) {・・・})の下へ、下記を追記します。
if (id == CHARTEVENT_KEYUP) {
}
追加した上記のif文内に、下記を追記します。
string key = ShortToString(TranslateKey((int)lparam));
キーボードを押したときに出力されるキーコード(lparam)を、TranslateKey関数でUnicode文字に変換し、ShortToString関数で文字列にしてkeyへ代入しています。
「B」キーを押したときに買いエントリーするよう、下記を「string key = ShortToString(TranslateKey((int)lparam));」の下に追記します。
if (key == "b") {
double price = SymbolInfoDouble(NULL, SYMBOL_ASK);
double sl = NormalizeDouble(price - SL * _Point * 10, _Digits);
double tp = NormalizeDouble(price + TP * _Point * 10, _Digits);
Entry(ORDER_TYPE_BUY, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
}
if文のカッコ内は、Buyボタンを押したときの回路と同じです。
同様に、「S」キーを押したときに売りエントリー、「E」キーを押したときに決済するよう、下記を追記します。
if (key == "s") {
double price = SymbolInfoDouble(NULL, SYMBOL_BID);
double sl = NormalizeDouble(price + SL * _Point * 10, _Digits);
double tp = NormalizeDouble(price - TP * _Point * 10, _Digits);
Entry(ORDER_TYPE_SELL, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
}
if (key == "e") {
Exit();
}
コンパイルしてチャートをクリックした後、「B」「S」「E」キーを押すと、トレード操作ができることを確認できます。
2.ビジュアルモード時の動作機能追加
MT5メニューの「表示」から「ストラテジーテスター」をクリックし、「概要」タブで「単一」を選択後、「設定」タブの「エキスパート」で「TradePanel.ex5」を選択すると、本ツールのバックテストができます。
「チャート、指標、取引を表示するビジュアルモード」にチェックを入れて、「スタート」ボタンを押すとビジュアルモードでのテストが実行されます。

現在の状態では、ビジュアルモード実行時に「Sell」「Buy」「Exit」ボタンを押しても反応しません。
「Auto」ボタンを押すと、指定したインジケーターの条件成立時にエントリーすることはできます。
以降の作業で「Sell」「Buy」「Exit」ボタンが機能するように改修していきます。
ボタン操作時に、ボタンを押した状態を表示するため、OnTimer関数内に下記を追記します。
if (MQLInfoInteger(MQL_VISUAL_MODE)) return;
ビジュアルモード実行時は、OnTimer関数が機能しないようにしています。
OnTick関数内の一番下に、下記を追記します。
if (!MQLInfoInteger(MQL_VISUAL_MODE)) return;
ビジュアルモードが実行されていない場合は、上記の位置で処理が終了します。
ビジュアルモード時のみ実行したい処理を、以降に追記していきます。
各ボタンが押されたことを記憶するために、下記を追記します。
static bool clickBuy = false, clickSell = false, clickExit = false;
Buyボタンを押したとき、エントリーするよう、下記を追記します。
if (ObjectGetInteger(0, PREFIX + "Buy", OBJPROP_STATE) && !clickBuy) {
double price = SymbolInfoDouble(NULL, SYMBOL_ASK);
double sl = NormalizeDouble(price - SL * _Point * 10, _Digits);
double tp = NormalizeDouble(price + TP * _Point * 10, _Digits);
Entry(ORDER_TYPE_BUY, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
clickBuy = true;
} else {
ObjectSetInteger(0, PREFIX + "Buy", OBJPROP_STATE, false);
clickBuy = false;
}
ボタンクリック記憶用のclickBuyが「false」の状態でボタンを押すと、買いエントリーが実行されます。
同時にclickBuyを「true」にして、クリックされたことを記憶します。
次回のティック配信時、押されたボタンを戻し、clickBuyを「false」に戻します。
同様に、Sellボタンで売りエントリー、Exitボタンで決済をするよう、下記を追記します。
if (ObjectGetInteger(0, PREFIX + "Sell", OBJPROP_STATE) && !clickSell) {
double price = SymbolInfoDouble(NULL, SYMBOL_BID);
double sl = NormalizeDouble(price + SL * _Point * 10, _Digits);
double tp = NormalizeDouble(price - TP * _Point * 10, _Digits);
Entry(ORDER_TYPE_SELL, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
clickSell = true;
} else {
ObjectSetInteger(0, PREFIX + "Sell", OBJPROP_STATE, false);
clickSell = false;
}
if (ObjectGetInteger(0, PREFIX + "Exit", OBJPROP_STATE) && !clickExit) {
Exit();
clickExit = true;
} else {
ObjectSetInteger(0, PREFIX + "Exit", OBJPROP_STATE, false);
clickExit = false;
}
Autoボタンを押したときに色が変化するよう、下記を追記します。
bool state = ObjectGetInteger(0, PREFIX + "Auto", OBJPROP_STATE);
ObjectSetInteger(0, PREFIX + "Auto", OBJPROP_BGCOLOR, state ? clrLimeGreen : clrGray);
上記の作業までで今回のツールは完成です。
コンパイル後にストラテジーテスターのビジュアルモードをスタートすると、ビジュアルモードが実行されているチャート上でボタン操作によるエントリー・決済ができるようになります。

3.ソースコード
今回、作成したソースコードは下記の通りです。
//+------------------------------------------------------------------+
//| TradePanel.mq5 |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.01"
#define PREFIX MQLInfoString(MQL_PROGRAM_NAME) + "_"
input int MAGIC = 1; // マジックナンバー
input double LOT = 0.1; // ロット
input double TP = 50; // TP [pips]
input double SL = 50; // SL [pips]
input double SLIP = 1; // 許容スリッページ [pips]
input string COMMENT = ""; // コメント
input group ""
input group "【インジケーター】"
input string NAME = "MA_Cross_Sign.ex5"; // ファイル名
input int BUFFER_BUY = 2; // 買いサインバッファー番号
input int BUFFER_SELL = 3; // 売りサインバッファー番号
int handle;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
ButtonCreate(0, PREFIX + "Sell", 0, 0, 20, 80, 40, CORNER_LEFT_UPPER, "Sell", "Arial Black", 20, clrWhite, clrDodgerBlue, clrNONE);
ButtonCreate(0, PREFIX + "Buy", 0, 80, 20, 80, 40, CORNER_LEFT_UPPER, "Buy", "Arial Black", 20, clrWhite, clrRed, clrNONE);
ButtonCreate(0, PREFIX + "Exit", 0, 0, 60, 160, 40, CORNER_LEFT_UPPER, "Exit", "Arial Black", 20, clrBlack, clrGold, clrNONE);
ButtonCreate(0, PREFIX + "Auto", 0, 0, 100, 160, 40, CORNER_LEFT_UPPER, "Auto", "Arial Black", 20, clrWhite, clrGray, clrNONE);
//--- create timer
EventSetMillisecondTimer(100);
handle = iCustom(NULL, 0, NAME);
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, PREFIX);
//--- destroy timer
EventKillTimer();
IndicatorRelease(handle);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//---
double buy[], sell[];
ArraySetAsSeries(buy, true);
ArraySetAsSeries(sell, true);
CopyBuffer(handle, BUFFER_BUY, 0, 2, buy);
CopyBuffer(handle, BUFFER_SELL, 0, 2, sell);
if (ObjectGetInteger(0, PREFIX + "Auto", OBJPROP_STATE)) {
static datetime timeEntry;
if (buy[1] != EMPTY_VALUE && iTime(NULL, 0, 0) > timeEntry) {
double price = SymbolInfoDouble(NULL, SYMBOL_ASK);
double sl = NormalizeDouble(price - SL * _Point * 10, _Digits);
double tp = NormalizeDouble(price + TP * _Point * 10, _Digits);
Entry(ORDER_TYPE_BUY, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
timeEntry = iTime(NULL, 0, 0);
}
if (sell[1] != EMPTY_VALUE && iTime(NULL, 0, 0) > timeEntry) {
double price = SymbolInfoDouble(NULL, SYMBOL_BID);
double sl = NormalizeDouble(price + SL * _Point * 10, _Digits);
double tp = NormalizeDouble(price - TP * _Point * 10, _Digits);
Entry(ORDER_TYPE_SELL, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
timeEntry = iTime(NULL, 0, 0);
}
}
if (!MQLInfoInteger(MQL_VISUAL_MODE)) return;
static bool clickBuy = false, clickSell = false, clickExit = false;
if (ObjectGetInteger(0, PREFIX + "Buy", OBJPROP_STATE) && !clickBuy) {
double price = SymbolInfoDouble(NULL, SYMBOL_ASK);
double sl = NormalizeDouble(price - SL * _Point * 10, _Digits);
double tp = NormalizeDouble(price + TP * _Point * 10, _Digits);
Entry(ORDER_TYPE_BUY, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
clickBuy = true;
} else {
ObjectSetInteger(0, PREFIX + "Buy", OBJPROP_STATE, false);
clickBuy = false;
}
if (ObjectGetInteger(0, PREFIX + "Sell", OBJPROP_STATE) && !clickSell) {
double price = SymbolInfoDouble(NULL, SYMBOL_BID);
double sl = NormalizeDouble(price + SL * _Point * 10, _Digits);
double tp = NormalizeDouble(price - TP * _Point * 10, _Digits);
Entry(ORDER_TYPE_SELL, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
clickSell = true;
} else {
ObjectSetInteger(0, PREFIX + "Sell", OBJPROP_STATE, false);
clickSell = false;
}
if (ObjectGetInteger(0, PREFIX + "Exit", OBJPROP_STATE) && !clickExit) {
Exit();
clickExit = true;
} else {
ObjectSetInteger(0, PREFIX + "Exit", OBJPROP_STATE, false);
clickExit = false;
}
bool state = ObjectGetInteger(0, PREFIX + "Auto", OBJPROP_STATE);
ObjectSetInteger(0, PREFIX + "Auto", OBJPROP_BGCOLOR, state ? clrLimeGreen : clrGray);
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
//---
if (MQLInfoInteger(MQL_VISUAL_MODE)) return;
static bool stateBuy = false, stateSell = false, stateExit = false;
if (stateBuy) ObjectSetInteger(0, PREFIX + "Buy", OBJPROP_STATE, false);
stateBuy = ObjectGetInteger(0, PREFIX + "Buy", OBJPROP_STATE);
if (stateSell) ObjectSetInteger(0, PREFIX + "Sell", OBJPROP_STATE, false);
stateSell = ObjectGetInteger(0, PREFIX + "Sell", OBJPROP_STATE);
if (stateExit) ObjectSetInteger(0, PREFIX + "Exit", OBJPROP_STATE, false);
stateExit = ObjectGetInteger(0, PREFIX + "Exit", OBJPROP_STATE);
ChartRedraw();
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int32_t id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if (id == CHARTEVENT_OBJECT_CLICK) {
if (sparam == PREFIX + "Buy") {
double price = SymbolInfoDouble(NULL, SYMBOL_ASK);
double sl = NormalizeDouble(price - SL * _Point * 10, _Digits);
double tp = NormalizeDouble(price + TP * _Point * 10, _Digits);
Entry(ORDER_TYPE_BUY, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
}
if (sparam == PREFIX + "Sell") {
double price = SymbolInfoDouble(NULL, SYMBOL_BID);
double sl = NormalizeDouble(price + SL * _Point * 10, _Digits);
double tp = NormalizeDouble(price - TP * _Point * 10, _Digits);
Entry(ORDER_TYPE_SELL, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
}
if (sparam == PREFIX + "Exit") {
Exit();
}
if (sparam == PREFIX + "Auto") {
bool state = ObjectGetInteger(0, sparam, OBJPROP_STATE);
ObjectSetInteger(0, sparam, OBJPROP_BGCOLOR, state ? clrLimeGreen : clrGray);
ChartRedraw();
}
}
if (id == CHARTEVENT_KEYUP) {
string key = ShortToString(TranslateKey((int)lparam));
if (key == "b") {
double price = SymbolInfoDouble(NULL, SYMBOL_ASK);
double sl = NormalizeDouble(price - SL * _Point * 10, _Digits);
double tp = NormalizeDouble(price + TP * _Point * 10, _Digits);
Entry(ORDER_TYPE_BUY, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
}
if (key == "s") {
double price = SymbolInfoDouble(NULL, SYMBOL_BID);
double sl = NormalizeDouble(price + SL * _Point * 10, _Digits);
double tp = NormalizeDouble(price - TP * _Point * 10, _Digits);
Entry(ORDER_TYPE_SELL, LOT, price, sl, tp, int(SLIP * 10), MAGIC, COMMENT);
}
if (key == "e") {
Exit();
}
}
}
//+------------------------------------------------------------------+
//| ボタンを作成する |
//+------------------------------------------------------------------+
bool ButtonCreate(const long chart_ID=0, // チャート識別子
const string name="Button", // ボタン名
const int sub_window=0, // サブウィンドウ番号
const int x=0, // X 座標
const int y=0, // Y 座標
const int width=50, // ボタンの幅
const int height=18, // ボタンの高さ
const ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER, // アンカーに使用されるチャートのコーナー
const string text="Button", // テキスト
const string font="Arial", // フォント
const int font_size=10, // フォントサイズ
const color clr=clrBlack, // テキストの色
const color back_clr=C'236,233,216', // 背景色
const color border_clr=clrNONE, // 境界線の色
const bool state=false, // 押される/放される
const bool back=false, // 背景で表示する
const bool selection=false, // 強調表示して移動
const bool hidden=true, // オブジェクトリストに隠す
const long z_order=0) // マウスクリックの優先順位
{
//--- エラー値をリセットする
ResetLastError();
//--- ボタンを作成する
if(!ObjectCreate(chart_ID,name,OBJ_BUTTON,sub_window,0,0))
{
Print(__FUNCTION__,
": failed to create the button! Error code = ",GetLastError());
return(false);
}
//--- ボタン座標を設定する
ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x);
ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y);
//--- ボタンサイズを設定する
ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width);
ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height);
//--- ポイント座標が相対的に定義されているチャートのコーナーを設定
ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner);
//--- テキストを設定する
ObjectSetString(chart_ID,name,OBJPROP_TEXT,text);
//--- テキストフォントを設定する
ObjectSetString(chart_ID,name,OBJPROP_FONT,font);
//--- フォントサイズを設定する
ObjectSetInteger(chart_ID,name,OBJPROP_FONTSIZE,font_size);
//--- テキストの色を設定する
ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);
//--- 背景色を設定する
ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr);
//--- 境界線の色を設定する
ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_COLOR,border_clr);
//--- 前景(false)または背景(true)に表示
ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);
//--- ボタンの状態を設定する
ObjectSetInteger(chart_ID,name,OBJPROP_STATE,state);
//--- マウスでのボタンを移動させるモードを有効(true)か無効(false)にする
ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);
ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);
//--- オブジェクトリストのグラフィックオブジェクトを非表示(true)か表示(false)にする
ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden);
//--- チャートのマウスクリックのイベントを受信するための優先順位を設定する
ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order);
//--- 実行成功
return(true);
}
//+------------------------------------------------------------------+
//| エントリー関数 |
//+------------------------------------------------------------------+
void Entry(ENUM_ORDER_TYPE type, double lot, double price, double sl, double tp, int slip, int magic, string comment)
{
ENUM_ORDER_TYPE_FILLING filling;
long mode = SymbolInfoInteger(NULL, SYMBOL_FILLING_MODE);
if ((mode & SYMBOL_FILLING_IOC) != 0) filling = ORDER_FILLING_IOC;
else if ((mode & SYMBOL_FILLING_FOK) != 0) filling = ORDER_FILLING_FOK;
else filling = ORDER_FILLING_RETURN;
//--- リクエストと結果の宣言と初期化
MqlTradeRequest request = {};
MqlTradeResult result = {};
//--- リクエストのパラメータ
request.action = TRADE_ACTION_DEAL; // 取引操作タイプ
request.symbol = Symbol(); // シンボル
request.volume = lot; // ロットのボリューム
request.type = type; // 注文タイプ
request.price = price; // 発注価格
request.sl = sl; // SL (指定する為に行を追加)
request.tp = tp; // TP (指定する為に行を追加)
request.deviation = slip; // 価格からの許容偏差
request.type_filling = filling; // 注文充填
request.magic = magic; // 注文のMagicNumber
//--- リクエストの送信
if(!OrderSend(request,result))
PrintFormat("OrderSend error %d",GetLastError()); // リクエストの送信が失敗した場合、エラーコードを出力する
else PlaySound("ok.wav");
}
//+------------------------------------------------------------------+
//| 全てのポジションを決済 |
//+------------------------------------------------------------------+
void Exit()
{
ENUM_ORDER_TYPE_FILLING filling;
long mode = SymbolInfoInteger(NULL, SYMBOL_FILLING_MODE);
if ((mode & SYMBOL_FILLING_IOC) != 0) filling = ORDER_FILLING_IOC;
else if ((mode & SYMBOL_FILLING_FOK) != 0) filling = ORDER_FILLING_FOK;
else filling = ORDER_FILLING_RETURN;
//--- 結果とリクエストの宣言
MqlTradeRequest request;
MqlTradeResult result;
int total=PositionsTotal(); // 保有ポジション数
//--- 全ての保有ポジションの取捨
for(int i=total-1; i>=0; i--)
{
//--- 注文のパラメータ
ulong position_ticket=PositionGetTicket(i); // ポジションチケット
string position_symbol=PositionGetString(POSITION_SYMBOL); // シンボル
int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // 小数点以下の桁数
ulong magic=PositionGetInteger(POSITION_MAGIC); // ポジションのMagicNumber
double volume=PositionGetDouble(POSITION_VOLUME); // ポジションボリューム
ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // ポジションタイプ
//--- ポジション情報の出力
PrintFormat("#%I64u %s %s %.2f %s [%I64d]",
position_ticket,
position_symbol,
EnumToString(type),
volume,
DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits),
magic);
//--- MagicNumberが一致している場合
if(magic==MAGIC)
{
//--- リクエストと結果の値のゼロ化
ZeroMemory(request);
ZeroMemory(result);
//--- 操作パラメータの設定
request.action =TRADE_ACTION_DEAL; // 取引操作タイプ
request.position =position_ticket; // ポジションチケット
request.symbol =position_symbol; // シンボル
request.volume =volume; // ポジションボリューム
request.deviation=int(SLIP * 10); // 価格からの許容偏差
request.type_filling = filling; // 注文充填
request.magic =MAGIC; // ポジションのMagicNumber
//--- ポジションタイプによる注文タイプと価格の設定
if(type==POSITION_TYPE_BUY)
{
request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID);
request.type =ORDER_TYPE_SELL;
}
else
{
request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK);
request.type =ORDER_TYPE_BUY;
}
//--- 決済情報の出力
PrintFormat("Close #%I64d %s %s",position_ticket,position_symbol,EnumToString(type));
//--- リクエストの送信
if(!OrderSend(request,result))
PrintFormat("OrderSend error %d",GetLastError()); // リクエストの送信に失敗した場合、エラーコードを出力
else PlaySound("ok.wav");
//--- 操作情報
PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order);
//---
}
}
}
//+------------------------------------------------------------------+
本記事の監修者・HT FX
2013年にFXを開始し、その後専業トレーダーへ。2014年からMT4/MT5のカスタムインジケーターの開発に取り組む。ブログでは100本を超えるインジケーターを無料公開。投資スタイルは自作の秒足インジケーターを利用したスキャルピング。
EA(自動売買)を学びたい方へオススメコンテンツ

OANDAではEA(自動売買)を稼働するプラットフォームMT4/MT5の基本的な使い方について、画像や動画付きで詳しく解説しています。MT4/MT5のインストールからEAの設定方法までを詳しく解説しているので、初心者の方でもスムーズにEA運用を始めることが可能です。またOANDAの口座をお持ちであれば、独自開発したオリジナルインジケーターを無料で利用することもできます。EA運用をお考えであれば、ぜひ口座開設をご検討ください。
本ホームページに掲載されている事項は、投資判断の参考となる情報の提供を目的としたものであり、投資の勧誘を目的としたものではありません。投資方針、投資タイミング等は、ご自身の責任において判断してください。本サービスの情報に基づいて行った取引のいかなる損失についても、当社は一切の責を負いかねますのでご了承ください。また、当社は、当該情報の正確性および完全性を保証または約束するものでなく、今後、予告なしに内容を変更または廃止する場合があります。なお、当該情報の欠落・誤謬等につきましてもその責を負いかねますのでご了承ください。