戦略が決まったら次に考えること — EAに組み込む実用コードパーツ集
エントリー・エグジットの戦略ロジックは決まった。でも、いざコードにしてみると「注文が通らない」「ポジションの有無を確認できていない」「スプレッドが広い時間帯でも入ってしまう」といった問題が出てくる。
この記事は、そういった「戦略の外側」にある実装問題を解消するためのコードパーツ集です。それぞれ独立して使えるので、今のEAに必要な部分だけ取り出してください。
注意: このページで紹介するコードはすべて教育目的のサンプルです。そのままリアル口座に使わず、必ずデモ口座で十分にテストしてから使用してください。
1. 4桁/5桁業者対応のpips計算
MT4の業者によって、価格の桁数が異なります。USDJPY なら4桁業者は 109.50、5桁業者は 109.500 と表示されます。SL/TPをpips単位で計算する際、この違いを吸収しておかないと意図した幅にならないことがあります。
OnInit() 内でこの処理を一度書いておけば、以降は pipValue を使い回せます。
// OnInit() 内で設定
double pipValue;
if(Digits == 5 || Digits == 3) // 5桁業者(EURUSD=5桁, USDJPY=3桁)
pipValue = Point * 10;
else // 4桁業者(EURUSD=4桁, USDJPY=2桁)
pipValue = Point;
SL/TPへの使い方:
// 買いエントリー時に20pipsのSL、40pipsのTPを設定する例
double entryPrice = Ask;
double sl = entryPrice - 20 * pipValue;
double tp = entryPrice + 40 * pipValue;
AIへの組み込みプロンプト例:
以下のpipValue計算コードを私のEAのOnInit()関数に追加してください。
また、コード内でSL/TPをpipsで計算している箇所をすべて
pipValueを使った書き方に統一してください。
【追加するコード】
(上記のpipValue計算コードを貼り付ける)
【現在のEAコード全文】
(コード全体をここに貼り付ける)
2. 安全なOrderSend(エラー時リトライ付き)
標準の OrderSend は、リクオートや価格変動が起きるとエラーを返してそのまま終了します。これを放置すると、条件が成立しているのに注文が入らないケースが発生します。
以下の SafeOrderSend は、リトライ可能なエラーの場合だけ最大3回まで再試行します。
// SafeOrderSend — リトライ付きの注文送信関数
// 戻り値: 成功時はチケット番号(>=0)、失敗時は-1
int SafeOrderSend(string symbol, int cmd, double lots, double price,
int slippage, double sl, double tp,
string comment, int magic)
{
int ticket = -1;
int retries = 3; // 最大リトライ回数
for(int i = 0; i < retries; i++)
{
ticket = OrderSend(symbol, cmd, lots, price, slippage, sl, tp,
comment, magic, 0,
cmd == OP_BUY ? clrBlue : clrRed);
if(ticket >= 0) break; // 成功したらループを抜ける
int err = GetLastError();
Print("OrderSendエラー(", i + 1, "回目): ", err);
// リトライ可能なエラーは待機して再試行
if(err == ERR_REQUOTE || err == ERR_PRICE_CHANGED || err == ERR_OFF_QUOTES)
{
Sleep(500); // 0.5秒待つ
RefreshRates(); // 最新レートを取得
}
else break; // それ以外のエラーはリトライしない
}
return ticket;
}
使う際は既存の OrderSend(...) をそのまま SafeOrderSend(...) に置き換えるだけです。引数の構造は同じです。
AIへの組み込みプロンプト例:
以下のSafeOrderSend関数を私のEAに組み込んでください。
コード内でOrderSendを使っている箇所をすべてSafeOrderSendに置き換えてください。
関数定義はOnStart/OnTickの外側(グローバル領域)に配置してください。
【SafeOrderSendのコード】
(上記のコードを貼り付ける)
【現在のEAコード全文】
(コード全体をここに貼り付ける)
3. ポジション確認(HasPosition / GetPositionTicket)
「すでにポジションがある場合は新規エントリーしない」という制御は、ほぼすべてのEAで必要になります。以下の2つの関数をセットで用意しておくと便利です。
// HasPosition — 指定タイプのポジションが存在するか確認
// orderType: OP_BUY または OP_SELL
// magic: このEAのマジックナンバー
bool HasPosition(int orderType, int magic)
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() &&
OrderMagicNumber() == magic &&
OrderType() == orderType)
return true;
}
}
return false;
}
// GetPositionTicket — 指定タイプのポジションのチケット番号を取得
// 存在しない場合は -1 を返す
int GetPositionTicket(int orderType, int magic)
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() &&
OrderMagicNumber() == magic &&
OrderType() == orderType)
return OrderTicket();
}
}
return -1;
}
OnTick() 内での使い方:
// 買いポジションがなければエントリーする例
if(!HasPosition(OP_BUY, MagicNumber))
{
// エントリー処理
}
// 決済時にチケット番号を取得する例
int ticket = GetPositionTicket(OP_BUY, MagicNumber);
if(ticket >= 0)
{
// クローズ処理
}
AIへの組み込みプロンプト例:
以下のHasPositionとGetPositionTicket関数を私のEAに追加してください。
OnTick()内の新規エントリー処理の前に「すでにポジションがある場合はスキップ」
という制御を入れてください。マジックナンバーは既存のMagicNumber変数を使ってください。
【追加する関数コード】
(上記のコードを貼り付ける)
【現在のEAコード全文】
(コード全体をここに貼り付ける)
4. 安全なOrderClose(リトライ付き)
決済時も注文送信と同様にリクオートが発生することがあります。以下の SafeOrderClose は、決済失敗時にリトライ処理を挟みます。
// SafeOrderClose — リトライ付きの決済関数
// 戻り値: 成功時はtrue、失敗時はfalse
bool SafeOrderClose(int ticket, double lots, int slippage)
{
bool result = false;
int retries = 3; // 最大リトライ回数
for(int i = 0; i < retries; i++)
{
// チケットを選択できなければ即終了
if(!OrderSelect(ticket, SELECT_BY_TICKET)) break;
// ポジションタイプに応じた決済価格を取得
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
result = OrderClose(ticket, lots, closePrice, slippage, clrYellow);
if(result) break; // 成功したらループを抜ける
int err = GetLastError();
Print("OrderCloseエラー(", i + 1, "回目): ", err);
if(err == ERR_REQUOTE || err == ERR_PRICE_CHANGED)
{
Sleep(500);
RefreshRates();
}
else break; // リトライ不可のエラーは即終了
}
return result;
}
使い方:
int ticket = GetPositionTicket(OP_BUY, MagicNumber);
if(ticket >= 0)
{
bool closed = SafeOrderClose(ticket, OrderLots(), 3);
if(!closed) Print("決済に失敗しました。チケット: ", ticket);
}
AIへの組み込みプロンプト例:
以下のSafeOrderClose関数を私のEAに追加してください。
コード内でOrderCloseを使っている箇所をSafeOrderCloseに置き換えてください。
【SafeOrderCloseのコード】
(上記のコードを貼り付ける)
【現在のEAコード全文】
(コード全体をここに貼り付ける)
5. スプレッドチェック
指標発表直後や流動性が薄い時間帯はスプレッドが拡大します。そのままエントリーするとスプレッド分だけ不利な価格から取引が始まります。以下の関数でエントリー前にスプレッドを確認できます。
// IsSpreadTooWide — スプレッドが上限を超えていたらtrueを返す
// maxSpreadPips: 許容する最大スプレッド(pips単位)
// pipValue: パーツ1で計算したpipValue
bool IsSpreadTooWide(int maxSpreadPips, double pipValue)
{
double currentSpread = (Ask - Bid) / pipValue; // 現在のスプレッドをpipsに変換
if(currentSpread > maxSpreadPips)
{
Print("スプレッド過大: ", currentSpread, "pips(上限: ", maxSpreadPips, "pips)");
return true;
}
return false;
}
extern変数と組み合わせた使い方:
// パラメーター宣言(ファイル冒頭のextern変数に追加)
extern int MaxSpreadPips = 3; // スプレッド上限(pips)
// OnTick() 内のエントリー判定の前に追加
if(IsSpreadTooWide(MaxSpreadPips, pipValue)) return;
MaxSpreadPips を extern にしておくと、MT4のEA設定画面からバックテスト中でも値を変えられます。通貨ペアや時間足によって適切な値が変わるため、最初は 3 か 5 程度から試してください。
AIへの組み込みプロンプト例:
以下のIsSpreadTooWide関数を私のEAに追加してください。
OnTick()内のエントリー処理の直前にスプレッドチェックを挟み、
スプレッドが過大な場合はreturnでスキップするようにしてください。
また、MaxSpreadPipsという名前のextern変数(初期値3)を追加してください。
【IsSpreadTooWideのコード】
(上記のコードを貼り付ける)
【現在のEAコード全文】
(コード全体をここに貼り付ける)
6. 取引時間フィルター
「東京時間だけトレードしたい」「ロンドンフィックス前後は避けたい」といった時間帯の制限は、以下の関数で実装できます。
// IsOutsideTradingHours — 指定した取引時間帯の外ならtrueを返す
// startHour: 取引開始時刻(サーバー時間、0〜23)
// endHour: 取引終了時刻(サーバー時間、0〜23)
bool IsOutsideTradingHours(int startHour, int endHour)
{
int currentHour = TimeHour(TimeCurrent());
if(startHour < endHour)
{
// 日をまたがない場合(例: 9時〜15時)
return (currentHour < startHour || currentHour >= endHour);
}
else
{
// 日をまたぐ場合(例: 22時〜6時)
return (currentHour < startHour && currentHour >= endHour);
}
}
使い方:
// 東京時間(9時〜15時)のみエントリーする例
if(IsOutsideTradingHours(9, 15)) return;
// 22時〜6時(日をまたぐ)のみエントリーする例
if(IsOutsideTradingHours(22, 6)) return;
注意点として、TimeCurrent() はMT4サーバーの時刻を返します。業者によってサーバー時刻(GMT+2やGMT+3など)が異なるため、実際の日本時間との差分を考慮して時間を設定してください。使っている業者のサーバー時刻のオフセットはMT4のマーケットウォッチで確認できます。
AIへの組み込みプロンプト例:
以下のIsOutsideTradingHours関数を私のEAに追加してください。
OnTick()内のエントリー処理の直前に時間帯チェックを挟み、
取引時間外の場合はreturnでスキップするようにしてください。
開始時間・終了時間はextern変数(TradingStartHour, TradingEndHour)で
指定できるようにしてください。
【IsOutsideTradingHoursのコード】
(上記のコードを貼り付ける)
【現在のEAコード全文】
(コード全体をここに貼り付ける)
7. 主要エラーコード早見表
GetLastError() が返す番号と対処法のまとめです。Print ログにエラーコードが出たときの参照用にしてください。
| コード | 定数 | 意味 | 対応 |
|---|---|---|---|
| 4 | ERR_SERVER_BUSY | サーバー混雑 | 少し待ってリトライ |
| 129 | ERR_INVALID_PRICE | 無効な価格 | RefreshRates後リトライ |
| 130 | ERR_INVALID_STOPS | SL/TPが無効 | SL/TP値を見直す |
| 135 | ERR_PRICE_CHANGED | 価格変動 | RefreshRates後リトライ |
| 136 | ERR_OFF_QUOTES | 気配値なし | しばらく待機 |
| 138 | ERR_REQUOTE | リクオート | RefreshRates後リトライ |
| 145 | ERR_TRADE_MODIFY_DENIED | 変更不可(SL/TPが近すぎる等) | ストップレベルを確認 |
| 148 | ERR_TRADE_TOO_MANY_ORDERS | 注文数上限 | 一部を決済してから発注 |
エラーコード 130 と 145 はリトライで解決しません。SL/TP の計算ロジック自体を見直す必要があります。業者ごとに「ストップレベル(最小SL/TP距離)」が設定されており、それを下回るとエラーになります。
まとめ
これらのパーツは戦略ロジックとは独立した「共通インフラ」です。一度整えておけば、別のEAを作るときにもそのまま流用できます。
AIへの指示の仕方は各パーツに添えたプロンプト例をそのまま使えます。「関数コードを貼り付ける → 自分のEAコードを貼り付ける → 組み込んでください」——この流れで、AIが適切な場所に挿入したコードを返してくれます。
関連記事
免責事項
本記事で紹介するEA・コードは教育目的で作成されたものです。実際の取引で利益を保証するものではありません。EAの運用は必ずデモ口座で十分にテストした上で、自己責任で行ってください。FX取引にはリスクが伴います。
次のステップ
まずデモで動作確認 → OKなら本番運用、が安全です。
デモ口座でEAを回す手順関連記事
> install MT4
> deploy EA
> start VPS monitor
EA自動売買にVPSが必要な理由と選び方【初心者向け完全ガイド】
EA(自動売買)を24時間安定稼働させるためにVPSが必要な理由を解説。自宅PCとの違い、VPSの選び方、MT4セットアップの流れまでまとめました。
> install MT4
> deploy EA
> start VPS monitor
MT4ストラテジーテスターの使い方 — バックテスト結果の読み方完全ガイド
MT4のストラテジーテスターでバックテストを実行し、PF・最大DD・勝率などの指標を正しく読み解く方法を解説。良い成績と悪い成績の基準も明示します。
> install MT4
> deploy EA
> start VPS monitor
MetaEditorの使い方 — AIのコードをMT4で動かすまで【超入門】
AIが出力したMQL4コードをMT4で動かすまでの手順を図解で解説。MetaEditorを開いてコピペしてコンパイルするだけ。3分で完了します。
> install MT4
> deploy EA
> start VPS monitor
国内MT4/MT5対応FX業者を比較 — EA運用に最適な口座は?
EA(自動売買)を稼働するために必要な国内MT4/MT5対応FX業者を比較。選び方のポイントも解説。