MQL5で口座履歴から獲得Pipsを計算することは、EA開発やパフォーマンス分析において重要ですが、いくつかの課題があります。この記事では、直近の取引の獲得Pipsを正確に計算する方法を解説します。
WAN
MQL4とMQL5で一番仕様が違うといっても過言ではない部分が口座履歴
この記事を参考にしてこの壁を乗り切ってくださいね(‘ω’)
1. MQL5の取引履歴システムの概要
MQL5の口座履歴は主に以下の要素で構成されています:
- ポジション(Position)
- 約定(Deal)
- 注文(Order)
これらの関係を理解することが、正確なPips計算の鍵となります。
2. 獲得Pips計算の問題点
- ポジションと約定の不一致:一つのポジションに複数の約定が存在する場合がある
- 部分決済:一つのポジションが複数回に分けて決済される場合の処理
- 手動決済:EAで開いたポジションが手動で決済された場合の処理
- 時間の不一致:ポジションの開始時間と約定の時間が厳密には一致しない場合がある
- ポジションタイプと約定タイプの関係:買いポジションの決済はSELL約定、売りポジションの決済はBUY約定となる
3. 解決策とコード実装
以下のアプローチで直近の取引のPipsを計算します:
- ポジションタイプに基づいて適切な決済約定(ENTRY_OUT)を見つける
- 対応するエントリー約定(ENTRY_IN)を特定
- 開始価格と決済価格を使用してPipsを計算
//+------------------------------------------------------------------+
//| CalculateLastDealPips.mq5 |
//+------------------------------------------------------------------+
#property copyright "ワンダフルFXライフ"
#property link "https://wonderfulfxlife.com"
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
//---
Print(CalculateLastDealPips(_Symbol, 0, POSITION_TYPE_BUY));
}
// 最後の取引で得たPips数を計算する関数
// symbol: 通貨ペアのシンボル(例: "EURUSD")
// magicNumber: マジックナンバー(エントリーを識別するための一意の番号)
// positionType: ポジションの種類(POSITION_TYPE_BUY または POSITION_TYPE_SELL)
double CalculateLastDealPips(string symbol, int magicNumber, ENUM_POSITION_TYPE positionType)
{
// 口座履歴を選択する(0から現在時刻まで。履歴がなければ、0を返す)
if(!HistorySelect(0, TimeCurrent()))
return 0;
// 履歴の取引の合計数を取得
int totalDeals = HistoryDealsTotal();
// 取引履歴を逆順にループ(最新の取引からチェック)
for(int i = totalDeals - 1; i >= 0; i--)
{
// i番目の取引チケット番号を取得
ulong dealTicket = HistoryDealGetTicket(i);
if(dealTicket <= 0)
continue; // 取引が無効な場合はスキップ
// 取引シンボルが指定されたものと異なる場合はスキップ
if(HistoryDealGetString(dealTicket, DEAL_SYMBOL) != symbol)
continue;
// 取引のマジックナンバーが指定されたものと異なる場合はスキップ
if(HistoryDealGetInteger(dealTicket, DEAL_MAGIC) != magicNumber)
continue;
// 取引の種類(買いまたは売り)を取得
ENUM_DEAL_TYPE dealType = (ENUM_DEAL_TYPE)HistoryDealGetInteger(dealTicket, DEAL_TYPE);
// 取引のエントリー(新規か決済か)を取得
ENUM_DEAL_ENTRY dealEntry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(dealTicket, DEAL_ENTRY);
// 現在のポジションが買いの場合、決済取引が売りであることを確認
// または、売りポジションの場合は、決済取引が買いであることを確認
if((positionType == POSITION_TYPE_BUY && dealType == DEAL_TYPE_SELL && dealEntry == DEAL_ENTRY_OUT) ||
(positionType == POSITION_TYPE_SELL && dealType == DEAL_TYPE_BUY && dealEntry == DEAL_ENTRY_OUT))
{
// 決済時の価格を取得
double closePrice = HistoryDealGetDouble(dealTicket, DEAL_PRICE);
// ポジションIDを取得(同じポジションに属する取引を識別するため)
long positionId = HistoryDealGetInteger(dealTicket, DEAL_POSITION_ID);
// 決済取引の前に行われた新規取引(エントリー)を探す
for(int j = i - 1; j >= 0; j--)
{
// j番目の取引チケット番号を取得
ulong openDealTicket = HistoryDealGetTicket(j);
if(openDealTicket <= 0)
continue; // 取引が無効な場合はスキップ
// エントリー取引(新規ポジション)が同じポジションIDかを確認
if(HistoryDealGetInteger(openDealTicket, DEAL_POSITION_ID) == positionId &&
(ENUM_DEAL_ENTRY)HistoryDealGetInteger(openDealTicket, DEAL_ENTRY) == DEAL_ENTRY_IN)
{
// 新規取引時の価格を取得
double openPrice = HistoryDealGetDouble(openDealTicket, DEAL_PRICE);
// Pipsを計算(買いの場合はクローズ価格からオープン価格を引く、売りの場合は逆)
double pips = (positionType == POSITION_TYPE_BUY) ? (closePrice - openPrice) : (openPrice - closePrice);
// Pipsの値を正規化して返す(通貨ペアの特性に応じて調整)
return NormalizePips(pips, symbol);
}
}
}
}
// 条件に合う取引がない場合は0を返す
return 0;
}
// Pips値をシンボルに応じて正規化する関数
// pips: 計算されたPips値
// symbol: 通貨ペアのシンボル(例: "EURUSD")
double NormalizePips(double pips, string symbol)
{
// 通貨ペアの小数点以下の桁数を取得
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
// 1ポイントの値を取得(価格の最小単位)
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
// もし桁数が3または5の場合(例: JPY関連のペアやUSDJPY)、Pips値を10倍する
if(digits == 3 || digits == 5)
pips *= 10;
// Pipsをポイント単位で割り、正規化した値を返す
return pips / point;
}
//+------------------------------------------------------------------+
4. 注意点
- ポジションタイプと約定タイプの関係に注意(買いポジションの決済はSELL約定)
- 部分決済の場合、最後の決済部分のみが考慮される
- 大量の履歴データがある場合、処理時間に注意
- 様々なシナリオでテストし、計算結果の正確性を確認
この方法を使用することで、MQL5で直近の取引の獲得Pipsを正確に計算できます。ただし、全体的なパフォーマンス評価には、より包括的なアプローチが必要な場合があります。