MQL5の数学関数を完全網羅。NormalizeDouble・MathRound・MathMax・MathMin・乱数生成・三角関数など、EA開発で使うすべての数学関数をコード付きで解説します。価格の正規化、ロット計算、pip計算、統計分析まで、実践的なパターンを豊富に紹介。MQL4との違いも併記しています。
目次
MQL5数学関数の基礎知識
数学関数はEA開発のあらゆる場面で使用されます。価格の正規化、ロットサイズの計算、リスク管理の数値処理、統計分析など、正確な数値計算がEAの品質を決定します。
MQL4とMQL5の数学関数の違い
| 項目 | MQL4 | MQL5 |
|---|---|---|
| 関数プレフィックス | MathAbsとfabsの両方可(C標準ライブラリ名) | MathXxx形式のみ |
| NormalizeDouble | 対応 | 対応(同じ) |
| MathIsValidNumber | 対応 | 対応(NaN/Inf検出) |
| MathRand範囲 | 0〜32767 | 0〜32767(同じ) |
| 統計関数 | なし(自作が必要) | MathStatライブラリあり |
| MathSwap | なし | 対応(バイトスワップ) |
NormalizeDouble — 価格の正規化(最重要)
浮動小数点数を指定した桁数に丸めます。EA開発で最も重要な数学関数であり、注文価格・SL・TPの設定前に必ず使用する必要があります。
double NormalizeDouble(
double value, // 正規化する値
int digits // 小数点以下の桁数
);
// _Digits: 現在のシンボルの小数桁数
// USDJPY: _Digits=3 (3桁)、EURUSD: _Digits=5 (5桁)
// 基本使用
double price = 150.12345678;
double normalized = NormalizeDouble(price, 3); // 150.123
// 注文前の価格正規化(必須パターン)
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double sl = NormalizeDouble(ask - 500 * _Point, _Digits);
double tp = NormalizeDouble(ask + 1000 * _Point, _Digits);
// ロットサイズの正規化
double lot = NormalizeDouble(calculatedLot, 2); // 小数点2桁に丸め
重要
NormalizeDouble()を使わずにOrderSendを呼ぶと、TRADE_RETCODE_INVALID_PRICE(10015)エラーが発生する場合があります。注文価格、SL、TPは必ず正規化してからOrderSend/trade.Buy/trade.Sellに渡してください。
// 浮動小数点の比較(直接比較は危険)
double a = 1.1 + 2.2; // 3.3000000000000003(誤差あり)
double b = 3.3;
if(a == b) Print("等しい"); // 一致しない可能性あり!
// NormalizeDoubleを使った安全な比較
if(NormalizeDouble(a - b, 8) == 0)
Print("等しい"); // 確実に判定
// 比較ヘルパー関数
bool IsEqual(double a, double b, int digits = 8)
{ return NormalizeDouble(a - b, digits) == 0; }
bool IsGreater(double a, double b, int digits = 8)
{ return NormalizeDouble(a - b, digits) > 0; }
bool IsLess(double a, double b, int digits = 8)
{ return NormalizeDouble(a - b, digits) < 0; }
// 使用例
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double level = 150.000;
if(IsGreater(price, level))
Print("価格がレベルを超えました");MathAbs — 絶対値
double MathAbs(double value);
// pips計算(方向に関係なく差分を取得)
double priceDiff = SymbolInfoDouble(_Symbol, SYMBOL_BID) - entryPrice;
double pipsDiff = MathAbs(priceDiff) / _Point;
Print("エントリーからの距離: ", pipsDiff, " points");
// 利益率の絶対値
double profitPct = (currentBalance - initialBalance) / initialBalance * 100;
if(MathAbs(profitPct) > 10)
Print("大きな変動: ", profitPct, "%");MathMax / MathMin — 最大値・最小値
double MathMax(double value1, double value2);
double MathMin(double value1, double value2);
// ロットサイズの上下限制約
double CalcLot(double riskAmount, double slPips)
{
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lot = riskAmount / (slPips * tickValue / tickSize);
lot = MathFloor(lot / lotStep) * lotStep; // ステップ単位に丸め
lot = MathMax(lot, minLot); // 最小ロット以上
lot = MathMin(lot, maxLot); // 最大ロット以下
return NormalizeDouble(lot, 2);
}
// SLの最小幅を保証
double sl = entryPrice - slPips * _Point;
double minSL = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
double actualSL = MathMax(MathAbs(entryPrice - sl), minSL);
// 3つ以上の値から最大値を取得
double Max3(double a, double b, double c)
{ return MathMax(a, MathMax(b, c)); }
double Min3(double a, double b, double c)
{ return MathMin(a, MathMin(b, c)); }MathFloor / MathCeil / MathRound — 丸め関数
double MathFloor(double value); // 切り捨て(小さい方の整数)
double MathCeil(double value); // 切り上げ(大きい方の整数)
double MathRound(double value); // 四捨五入
// 比較表
// 値 Floor Ceil Round
// 2.3 2 3 2
// 2.7 2 3 3
// -2.3 -3 -2 -2
// -2.7 -3 -2 -3
Print(MathFloor(2.7)); // 2.0
Print(MathCeil(2.3)); // 3.0
Print(MathRound(2.5)); // 3.0(0.5は切り上げ)
// ロットステップへの丸め
double RoundToLotStep(double lot)
{
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
return MathFloor(lot / step) * step; // 切り捨て(安全側)
}
// pips単位への丸め
double RoundToPips(double price, int pipDigit)
{
double pipSize = MathPow(10, -pipDigit);
return MathRound(price / pipSize) * pipSize;
}
// N分足の時間への丸め(時間足の開始時刻を計算)
datetime RoundToTimeframe(datetime time, int minutes)
{
return (datetime)(MathFloor((double)time / (minutes * 60)) * minutes * 60);
}MathPow / MathSqrt / MathLog / MathExp — べき乗・対数・指数
double MathPow(double base, double exponent); // base^exponent
double MathSqrt(double value); // 平方根
double MathLog(double value); // 自然対数 (ln)
double MathLog10(double value); // 常用対数 (log10)
double MathExp(double value); // e^value
// 複利計算
double CompoundReturn(double initial, double ratePerTrade, int trades)
{
return initial * MathPow(1 + ratePerTrade, trades);
}
// 標準偏差の計算
double StandardDeviation(double &values[], int count)
{
if(count <= 1) return 0;
double sum = 0;
for(int i = 0; i < count; i++) sum += values[i];
double mean = sum / count;
double sumSqDiff = 0;
for(int i = 0; i < count; i++)
sumSqDiff += MathPow(values[i] - mean, 2);
return MathSqrt(sumSqDiff / (count - 1)); // 不偏標準偏差
}
// 対数リターンの計算
double LogReturn(double priceNow, double pricePrev)
{
if(pricePrev <= 0) return 0;
return MathLog(priceNow / pricePrev);
}
// ボラティリティの年率換算(日次リターンから)
double AnnualizedVol(double dailyStdDev)
{
return dailyStdDev * MathSqrt(252); // 252営業日
}MathMod — 剰余演算
double MathMod(double value, double divisor);
// N本おきに処理を実行
int barCount = 0;
void OnTick()
{
static datetime lastBar = 0;
datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
if(currentBar == lastBar) return;
lastBar = currentBar;
barCount++;
// 5本おきにログ出力
if(MathMod(barCount, 5) == 0)
Print("5本ごとのチェックポイント: bar=", barCount);
}
// 角度の正規化(0〜360度)
double NormalizeAngle(double angle)
{
double result = MathMod(angle, 360);
if(result < 0) result += 360;
return result;
}三角関数(MathSin / MathCos / MathTan / MathArctan 等)
double MathSin(double value); // 正弦(ラジアン入力)
double MathCos(double value); // 余弦
double MathTan(double value); // 正接
double MathArcsin(double value); // 逆正弦
double MathArccos(double value); // 逆余弦
double MathArctan(double value); // 逆正接
// 度数法→ラジアン変換
double DegToRad(double degrees) { return degrees * M_PI / 180.0; }
double RadToDeg(double radians) { return radians * 180.0 / M_PI; }
// トレンド角度の計算(移動平均の傾き)
double CalcTrendAngle(double &ma[], int period)
{
// ma[0]=最新, ma[period-1]=最古
double priceChange = ma[0] - ma[period - 1];
double timeChange = (double)period;
// 正規化(通貨ペアの値幅にスケーリング)
double avgPrice = (ma[0] + ma[period - 1]) / 2.0;
double normalizedChange = priceChange / avgPrice * 10000; // pips換算的
double angleRad = MathArctan(normalizedChange / timeChange);
return RadToDeg(angleRad);
}
// サイクル分析(サイン波フィッティング)
double SineWave(int bar, double period, double amplitude, double phase)
{
return amplitude * MathSin(2 * M_PI * bar / period + DegToRad(phase));
}MathRand / MathSrand — 乱数生成
int MathRand(); // 0〜32767の乱数を生成
void MathSrand(int seed); // 乱数シードを設定
// 初期化(OnInitで1回だけ呼ぶ)
int OnInit()
{
MathSrand(GetTickCount()); // ティックカウントでシード初期化
return INIT_SUCCEEDED;
}
// 指定範囲の整数乱数
int RandomInt(int min, int max)
{
return min + MathRand() % (max - min + 1);
}
// 指定範囲の浮動小数点乱数
double RandomDouble(double min, double max)
{
return min + (max - min) * MathRand() / 32767.0;
}
// ランダムエントリーのテスト(EA開発のベースライン)
void OnTick()
{
// ランダムにBuy/Sellを決定(ベースラインテスト用)
if(MathRand() % 2 == 0)
Print("Random: Buy signal");
else
Print("Random: Sell signal");
}
// モンテカルロシミュレーション
double MonteCarloDrawdown(double &returns[], int simulations)
{
int count = ArraySize(returns);
if(count == 0) return 0;
double maxDrawdown = 0;
for(int sim = 0; sim < simulations; sim++)
{
double equity = 10000;
double peak = equity;
double dd = 0;
for(int i = 0; i < count; i++)
{
int randomIdx = MathRand() % count;
equity += returns[randomIdx];
peak = MathMax(peak, equity);
dd = MathMax(dd, (peak - equity) / peak * 100);
}
maxDrawdown = MathMax(maxDrawdown, dd);
}
return maxDrawdown;
}MathRand()は0〜32767の範囲しか生成しません。より大きな範囲や高品質な乱数が必要な場合は、MathRand()を複数回呼び出して組み合わせるか、暗号学的乱数ではないことを認識して使用してください。
MathIsValidNumber — 数値の有効性チェック
bool MathIsValidNumber(double value);
// NaN(Not a Number)やInfinity(無限大)でなければtrue
// 0除算のチェック
double SafeDivide(double a, double b)
{
if(b == 0) return 0;
double result = a / b;
if(!MathIsValidNumber(result))
{
Print("警告: 無効な計算結果 (NaN/Inf)");
return 0;
}
return result;
}
// インジケーター値の検証
void OnTick()
{
double buffer[];
ArraySetAsSeries(buffer, true);
if(CopyBuffer(handle, 0, 0, 1, buffer) < 1) return;
if(!MathIsValidNumber(buffer[0]))
{
Print("インジケーター値が無効です");
return;
}
// 安全に使用可能
double value = buffer[0];
}
// EMPTY_VALUE のチェック(インジケーターの描画されない部分)
if(buffer[0] == EMPTY_VALUE || !MathIsValidNumber(buffer[0]))
Print("データなし");実践パターン:EA開発での数学関数活用
パターン1:リスクベースのロット計算
input double RiskPercent = 2.0; // リスク(口座残高の%)
double CalcLotByRisk(double slDistance)
{
if(slDistance <= 0) return 0;
// 口座情報
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskMoney = balance * RiskPercent / 100.0;
// ティック情報
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
// ティック数
double ticks = slDistance / tickSize;
// ロット計算
double lot = riskMoney / (ticks * tickValue);
// ロットステップに丸め(切り捨て = 安全側)
lot = MathFloor(lot / lotStep) * lotStep;
// 上下限制約
lot = MathMax(lot, minLot);
lot = MathMin(lot, maxLot);
// 証拠金チェック
double margin;
if(!OrderCalcMargin(ORDER_TYPE_BUY, _Symbol, lot,
SymbolInfoDouble(_Symbol, SYMBOL_ASK), margin))
return minLot;
double freeMargin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
if(margin > freeMargin * 0.8) // 余裕証拠金の80%まで
{
lot = lot * (freeMargin * 0.8 / margin);
lot = MathFloor(lot / lotStep) * lotStep;
lot = MathMax(lot, minLot);
}
return NormalizeDouble(lot, 2);
}
// 使用例
void OnTick()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double slDistance = 500 * _Point; // 500 points
double lot = CalcLotByRisk(slDistance);
Print("リスク", RiskPercent, "%でのロット: ", lot);
}パターン2:pip計算ユーティリティ
// pipサイズを自動判定
double GetPipSize()
{
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
if(digits == 3 || digits == 5)
return _Point * 10; // 3桁/5桁ブローカー → 1pip = 10 points
else
return _Point; // 2桁/4桁ブローカー → 1pip = 1 point
}
// 価格差をpipsに変換
double PriceToPips(double priceDistance)
{
return MathAbs(priceDistance) / GetPipSize();
}
// pipsを価格差に変換
double PipsToPrice(double pips)
{
return pips * GetPipSize();
}
// pip値(1pipあたりの口座通貨額)
double GetPipValue(double lots)
{
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
return tickValue / tickSize * GetPipSize() * lots;
}
// 使用例
void OnTick()
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double spread = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - bid;
double spreadPips = PriceToPips(spread);
double pipValue = GetPipValue(0.1);
PrintFormat("Spread=%.1f pips, 0.1lot時のpip値=%.2f %s",
spreadPips, pipValue, AccountInfoString(ACCOUNT_CURRENCY));
}パターン3:統計計算(平均・標準偏差・相関係数)
// 平均値
double CalcMean(double &arr[], int count)
{
if(count <= 0) return 0;
double sum = 0;
for(int i = 0; i < count; i++) sum += arr[i];
return sum / count;
}
// 標準偏差
double CalcStdDev(double &arr[], int count)
{
if(count <= 1) return 0;
double mean = CalcMean(arr, count);
double sumSq = 0;
for(int i = 0; i < count; i++)
sumSq += MathPow(arr[i] - mean, 2);
return MathSqrt(sumSq / (count - 1));
}
// 相関係数(ピアソン)
double CalcCorrelation(double &x[], double &y[], int count)
{
if(count <= 1) return 0;
double meanX = CalcMean(x, count);
double meanY = CalcMean(y, count);
double sumXY = 0, sumX2 = 0, sumY2 = 0;
for(int i = 0; i < count; i++)
{
double dx = x[i] - meanX;
double dy = y[i] - meanY;
sumXY += dx * dy;
sumX2 += dx * dx;
sumY2 += dy * dy;
}
double denominator = MathSqrt(sumX2 * sumY2);
if(denominator == 0) return 0;
return sumXY / denominator;
}
// シャープレシオの計算
double CalcSharpeRatio(double &returns[], int count, double riskFreeRate = 0)
{
double mean = CalcMean(returns, count);
double stddev = CalcStdDev(returns, count);
if(stddev == 0) return 0;
return (mean - riskFreeRate) / stddev;
}
// プロフィットファクター
double CalcProfitFactor(double &profits[], int count)
{
double grossProfit = 0, grossLoss = 0;
for(int i = 0; i < count; i++)
{
if(profits[i] > 0) grossProfit += profits[i];
else grossLoss += MathAbs(profits[i]);
}
if(grossLoss == 0) return grossProfit > 0 ? 999.99 : 0;
return grossProfit / grossLoss;
}
// Zスコア(現在の値が平均からどれだけ離れているか)
double CalcZScore(double value, double mean, double stddev)
{
if(stddev == 0) return 0;
return (value - mean) / stddev;
}パターン4:ATRベースの動的SL/TP計算
input double SL_ATR_Multi = 1.5; // SL = ATR × 倍率
input double TP_ATR_Multi = 3.0; // TP = ATR × 倍率
input int ATR_Period = 14;
int atrHandle;
int OnInit()
{
atrHandle = iATR(_Symbol, PERIOD_CURRENT, ATR_Period);
if(atrHandle == INVALID_HANDLE) return INIT_FAILED;
return INIT_SUCCEEDED;
}
void CalcDynamicSLTP(bool isBuy, double entryPrice, double &sl, double &tp)
{
double atr[];
ArraySetAsSeries(atr, true);
if(CopyBuffer(atrHandle, 0, 0, 1, atr) < 1) return;
double atrValue = atr[0];
double slDistance = NormalizeDouble(atrValue * SL_ATR_Multi, _Digits);
double tpDistance = NormalizeDouble(atrValue * TP_ATR_Multi, _Digits);
// 最小ストップレベルの確認
double minStop = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
slDistance = MathMax(slDistance, minStop);
tpDistance = MathMax(tpDistance, minStop);
if(isBuy)
{
sl = NormalizeDouble(entryPrice - slDistance, _Digits);
tp = NormalizeDouble(entryPrice + tpDistance, _Digits);
}
else
{
sl = NormalizeDouble(entryPrice + slDistance, _Digits);
tp = NormalizeDouble(entryPrice - tpDistance, _Digits);
}
PrintFormat("ATR=%.5f SL距離=%.5f TP距離=%.5f RR=1:%.1f",
atrValue, slDistance, tpDistance, TP_ATR_Multi / SL_ATR_Multi);
}数学関数一覧リファレンス
| 関数 | 機能 | EA開発での主な用途 |
|---|---|---|
| NormalizeDouble | 桁数指定で丸め | 注文価格・SL・TPの正規化 |
| MathAbs | 絶対値 | pips計算、差分の取得 |
| MathMax / MathMin | 最大値 / 最小値 | ロット制約、SL最小幅 |
| MathFloor | 切り捨て | ロットステップへの丸め |
| MathCeil | 切り上げ | - |
| MathRound | 四捨五入 | pips表示の丸め |
| MathPow | べき乗 | 複利計算、統計計算 |
| MathSqrt | 平方根 | 標準偏差、ボラティリティ |
| MathLog / MathLog10 | 対数 | 対数リターン、スケーリング |
| MathExp | 指数関数 | 確率計算 |
| MathMod | 剰余 | 周期的処理、N本おきの判定 |
| MathSin / MathCos | 三角関数 | サイクル分析、角度計算 |
| MathArctan | 逆正接 | トレンド角度の計算 |
| MathRand / MathSrand | 乱数生成 | モンテカルロ、ランダムテスト |
| MathIsValidNumber | NaN/Inf検出 | 計算結果の安全性チェック |
- NormalizeDoubleは注文前に必ず使用。忘れるとINVALID_PRICEエラーの原因に
- 浮動小数点の直接比較(==)は危険。NormalizeDoubleで差分を比較する
- ロット計算はMathFloor(切り捨て)で安全側に丸める
- MathIsValidNumberでNaN/Infをチェックし、計算の安全性を確保
- 統計計算(標準偏差・相関・シャープレシオ)でEAの品質を定量評価
プロが開発したEAをお探しの方は → シストレ.COM EA一覧





