「なんとなく火曜日だけ負けが多い気がする」というような感覚を、
なんとなくのまま放置していないでしょうか。
MT4の決済履歴には、いつ決済したポジションが勝ちだったか負けだったかが、
すべて記録されています。
この記事では、`OrdersHistoryTotal`と、
`OrderSelect(MODE_HISTORY)`で、
過去の決済履歴を1件ずつ取り出し、
`TimeDayOfWeek`で曜日ごとに勝率を集計したうえで、
勝率の低い曜日だけ新規エントリーを自動で止めるフィルターEAの作り方を解説します。
・決済履歴から曜日別の勝率を集計する方法
・TimeDayOfWeekで曜日ごとに勝敗を仕分ける仕組み
・勝率の低い曜日だけ取引を止めるフィルターEAの設計
・実際に動くサンプルコード全体
OrdersHistoryTotalとは何か
「口座の取引履歴から、決済済みの注文が何件あるか」を知るための関数です。
OrdersTotalとの違い
`OrdersTotal()`が「今持っている、または出している注文の数」
を返すのに対し、`OrdersHistoryTotal()`は「すでに決済・削除された、
過去の注文の数」を返します。
対象は、ターミナルの「口座履歴」タブに読み込まれている
範囲に限られる点に注意してください。
なぜ曜日別の勝率が見えていないのか
多くのトレーダーは、口座全体の勝率や損益は確認していても、「曜日ごとに」
区切って集計したことがない場合がほとんどです。
過去の決済履歴はすでに端末の中にあるため、
集計するだけで、感覚ではなく数字で偏りを確認できます。
OrderSelect(MODE_HISTORY)で決済済み注文を1件ずつ取り出す
決済履歴を読むには、
`OrderSelect`の第3引数に`MODE_HISTORY`を指定します。
OrderSelectの引数の意味
bool OrderSelect(int index, int select, int pool = MODE_TRADES);
第2引数の`SELECT_BY_POS`はインデックス指定、
第3引数の`MODE_HISTORY`は「決済済み・削除済みの履歴プールから選ぶ」
という意味です。
デフォルトの`MODE_TRADES`のままだと、保有中の注文しか見られません。
入出金操作(OP_BALANCE)を除外する必要がある
決済履歴には、実際の売買注文だけでなく、
入金・出金などの残高操作も`OP_BALANCE`という種別で
混ざって記録されています。
これを除外しないと、勝率の集計が正しく計算できません。
`OrderType()`の戻り値が
`OP_BUY`(0)または`OP_SELL`(1)のものだけを、
実際の売買注文として数える設計にします。
それ以外(OP_BALANCE等)はスキップします。
TimeDayOfWeekで曜日ごとに勝敗を仕分ける
決済時刻を曜日に変換し、7つの箱に振り分けて集計します。
TimeDayOfWeekの戻り値
int TimeDayOfWeek(datetime date);
引数に渡した日時が、0(日曜)〜6(土曜)のどの曜日かをint型で返します。
`OrderCloseTime()`(決済時刻)をそのまま渡せば、
決済した曜日が分かります。
勝ち負けの判定基準
このコードでは、
`OrderProfit() + OrderSwap()`と
`OrderCommission()`の合計が
プラスかどうかで、
その注文を「勝ち」と数えます。
売買損益だけでなく、スワップ・手数料まで含めた実質の損益で判定する設計です。
| 関数 | 役割 |
|---|---|
| OrdersHistoryTotal() | 決済済み注文の総数を取得 |
| OrderSelect(i, SELECT_BY_POS, MODE_HISTORY) | i番目の決済済み注文を選択 |
| OrderCloseTime() | 決済時刻を取得 |
| TimeDayOfWeek(決済時刻) | 決済時刻の曜日(0〜6)を取得 |
| OrderProfit()+OrderSwap()+OrderCommission() | 実質の損益を計算 |
曜日別勝率を集計して表示する部分のコード
7つの曜日それぞれについて、取引数と勝ち数を配列に貯めていきます。
void ShowWeekdayWinRate()
{
int totalCount[7];
int winCount[7];
ArrayInitialize(totalCount, 0);
ArrayInitialize(winCount, 0);
int historyTotal = OrdersHistoryTotal();
for(int i = 0; i < historyTotal; i++)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
continue;
if(OrderType() != OP_BUY && OrderType() != OP_SELL)
continue;
int wday = TimeDayOfWeek(OrderCloseTime());
totalCount[wday]++;
if(OrderProfit() + OrderSwap() + OrderCommission() > 0)
winCount[wday]++;
}
string names[7] = {"日","月","火","水","木","金","土"};
string text = "曜日別の勝率(全" + IntegerToString(historyTotal) + "件中の売買注文)\n";
for(int d = 0; d < 7; d++)
{
if(totalCount[d] == 0)
{
text += names[d] + "曜: 取引なし\n";
continue;
}
double rate = (double)winCount[d] / totalCount[d] * 100.0;
text += names[d] + "曜: " + DoubleToString(rate, 1) + "% (" +
IntegerToString(winCount[d]) + "/" + IntegerToString(totalCount[d]) + ")\n";
}
Comment(text);
}
このコードをEAの`OnInit()`から呼び出すだけで、
チャート上に曜日別の勝率がコメント表示され、
自分の口座で本当にどの曜日が弱いのかを、数字で確認できます。
勝率が低い曜日だけ新規エントリーを止めるフィルターの設計
集計結果を見て「この曜日はやめておこう」と決めたら、
その判断を`bool`の入力パラメータとしてEAに渡す設計にします。
なぜ自動判定ではなく手動フラグにするのか
「勝率が◯%未満なら自動でブロックする」という完全自動化も一見便利ですが、
取引件数が少ない曜日はたまたま数件負けただけで極端な勝率になりがちです。
集計結果は必ず一度自分の目で確認し、件数が十分にある曜日だけを、
フラグで手動ブロックする設計にしています。
TimeDayOfWeekで現在の曜日を判定する
bool IsTodayBlocked()
{
int wday = TimeDayOfWeek(TimeCurrent());
switch(wday)
{
case 0: return BlockSunday;
case 1: return BlockMonday;
case 2: return BlockTuesday;
case 3: return BlockWednesday;
case 4: return BlockThursday;
case 5: return BlockFriday;
case 6: return BlockSaturday;
}
return false;
}
`OnTick()`の冒頭でこの関数がtrueを返したら、
新規エントリーの処理そのものへ進まずに抜ける設計にします。
実際に動くフィルターEAのコード全体
曜日別勝率の表示と、曜日ブロック機能を組み込んだ、
シンプルな移動平均線クロスEAの全体です。
//+------------------------------------------------------------------+
//| WeekdayWinRateFilterEA.mq4 |
//| Copyright 2026, FX-EA System Project Creator |
//| https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property strict
input bool ShowWeekdayStats = true; //起動時に曜日別勝率をコメント表示する
input bool BlockSunday = false;
input bool BlockMonday = false;
input bool BlockTuesday = false;
input bool BlockWednesday = false;
input bool BlockThursday = false;
input bool BlockFriday = false;
input bool BlockSaturday = false;
input int FastMAPeriod = 5;
input int SlowMAPeriod = 20;
input double LotSize = 0.1;
input int StopLossPips = 300;
input int TakeProfitPips = 600;
input int MagicNumber = 20260930;
int OnInit()
{
if(ShowWeekdayStats)
ShowWeekdayWinRate();
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
}
void OnTick()
{
if(HasOpenPosition())
return;
if(IsTodayBlocked())
return;
int signal = GetMASignal();
if(signal == 1)
OpenPosition(OP_BUY);
else if(signal == -1)
OpenPosition(OP_SELL);
}
bool IsTodayBlocked()
{
int wday = TimeDayOfWeek(TimeCurrent());
switch(wday)
{
case 0: return BlockSunday;
case 1: return BlockMonday;
case 2: return BlockTuesday;
case 3: return BlockWednesday;
case 4: return BlockThursday;
case 5: return BlockFriday;
case 6: return BlockSaturday;
}
return false;
}
void ShowWeekdayWinRate()
{
int totalCount[7];
int winCount[7];
ArrayInitialize(totalCount, 0);
ArrayInitialize(winCount, 0);
int historyTotal = OrdersHistoryTotal();
for(int i = 0; i < historyTotal; i++)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
continue;
if(OrderType() != OP_BUY && OrderType() != OP_SELL)
continue;
int wday = TimeDayOfWeek(OrderCloseTime());
totalCount[wday]++;
if(OrderProfit() + OrderSwap() + OrderCommission() > 0)
winCount[wday]++;
}
string names[7] = {"日","月","火","水","木","金","土"};
string text = "曜日別の勝率(全" + IntegerToString(historyTotal) + "件中の売買注文)\n";
for(int d = 0; d < 7; d++)
{
if(totalCount[d] == 0)
{
text += names[d] + "曜: 取引なし\n";
continue;
}
double rate = (double)winCount[d] / totalCount[d] * 100.0;
text += names[d] + "曜: " + DoubleToString(rate, 1) + "% (" +
IntegerToString(winCount[d]) + "/" + IntegerToString(totalCount[d]) + ")\n";
}
Comment(text);
}
bool HasOpenPosition()
{
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
return true;
}
}
return false;
}
int GetMASignal()
{
double fastPrev = iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
double slowPrev = iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
double fastPrev2 = iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);
double slowPrev2 = iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);
if(fastPrev2 <= slowPrev2 && fastPrev > slowPrev)
return 1;
if(fastPrev2 >= slowPrev2 && fastPrev < slowPrev)
return -1;
return 0;
}
void OpenPosition(int type)
{
double price = (type == OP_BUY) ? Ask : Bid;
double point = Point;
if(Digits == 3 || Digits == 5)
point *= 10;
double sl, tp;
if(type == OP_BUY)
{
sl = price - StopLossPips * point;
tp = price + TakeProfitPips * point;
}
else
{
sl = price + StopLossPips * point;
tp = price - TakeProfitPips * point;
}
int ticket = OrderSend(Symbol(), type, LotSize, price, 3, sl, tp,
"WeekdayFilterEA", MagicNumber, 0,
(type == OP_BUY) ? clrBlue : clrRed);
if(ticket < 0)
Print("OrderSend failed. Error=", GetLastError());
}
MetaEditorでコンパイル・動作確認する手順
- MetaEditorで「ファイル」→「新規作成」→「Expert Advisor」で「WeekdayWinRateFilterEA」を作成する
- ウィザードの中身を削除し、上のコードを丸ごと貼り付ける
- F7キーでコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
- まず`ShowWeekdayStats=true`・全曜日`false`のままデモ口座に適用し、コメント欄に表示される曜日別勝率を確認する
- 勝率が低い曜日が見つかったら、その曜日の入力パラメータだけを`true`に変更して再適用する
よくあるエラーと注意点
このEAを実際に試したときに出やすい、
表示・判定まわりの注意点をまとめます。
全曜日「取引なし」と表示される
MT4の「口座履歴」タブの表示期間が短い設定になっていると、
`OrdersHistoryTotal()`が取得できる件数も少なくなります。
端末の「口座履歴」タブを右クリックし、表示期間を「すべての履歴」に変更してください。
取引件数が少ない曜日の勝率を鵜呑みにしない
・1〜2件しか取引がない曜日の勝率(0%や100%)は、統計として意味を持ちません
・最低でも10件以上の取引がある曜日から、傾向として参考にしてください
・値動きの傾向は相場環境によって変わるため、集計結果を過信せず定期的に見直してください
MQL5で同じ集計をしたい場合
MQL5では`OrdersHistoryTotal()`ではなく、
`HistorySelect()`で期間を指定してから、
`HistoryDealsTotal()`で件数を取得し、
`HistoryDealGetInteger(ticket, DEAL_TIME)`で、
決済時刻を取得する書き方に変わります。
具体的な書き方は、既刊の決済履歴取得の記事で解説しています。
よくある質問
曜日別勝率フィルターEAについて、
よく検索される疑問を3つにまとめて回答します。
曜日別の勝率はどうやって調べればいいですか?
感覚ではなく、
`OrdersHistoryTotal`と、
`OrderSelect(MODE_HISTORY)`で
決済履歴を1件ずつ取り出し、
`OrderCloseTime()`を`TimeDayOfWeek`に渡して
曜日ごとに勝敗を集計すれば、数字として確認できます。
時間帯(何時台)ごとの集計もできますか?
できます。
`TimeDayOfWeek`の代わりに、
`TimeHour(OrderCloseTime())`を使えば、
0〜23時のどの時間帯に決済したかで同じように集計できます。
曜日と時間帯を組み合わせて、より細かく分析することも可能です。
勝率が低い曜日は自動で検出してブロックできませんか?
技術的には可能ですが、この記事ではあえて手動フラグにしています。
取引件数が少ない曜日はブレが大きく、
自動判定だと誤った曜日をブロックしてしまう可能性があるため、
必ず件数を確認したうえで人が判断する設計にしています。
【MQL4】OrdersHistoryTotalで曜日別の勝率を集計し、負けやすい曜日だけ新規エントリーを止めるフィルターEAの作り方:まとめ
`OrdersHistoryTotal`・
`OrderSelect(MODE_HISTORY)`・
`TimeDayOfWeek`を組み合わせれば、
「なんとなく火曜日が弱い気がする」という感覚を、
自分の口座の決済履歴という一次データで確認できます。
集計結果をそのまま自動化するのではなく、
取引件数を確認したうえで手動フラグとしてEAに反映する設計にすることで、
少数サンプルによる誤判定を避けられます。
コメント