MQL5 数学関数 完全ガイド|NormalizeDouble・MathRound・MathMax・乱数・統計計算

MQL5の数学関数を完全網羅。NormalizeDouble・MathRound・MathMax・MathMin・乱数生成・三角関数など、EA開発で使うすべての数学関数をコード付きで解説します。価格の正規化、ロット計算、pip計算、統計分析まで、実践的なパターンを豊富に紹介。MQL4との違いも併記しています。

目次

MQL5数学関数の基礎知識

数学関数はEA開発のあらゆる場面で使用されます。価格の正規化、ロットサイズの計算、リスク管理の数値処理、統計分析など、正確な数値計算がEAの品質を決定します。

MQL4とMQL5の数学関数の違い

項目MQL4MQL5
関数プレフィックスMathAbsとfabsの両方可(C標準ライブラリ名)MathXxx形式のみ
NormalizeDouble対応対応(同じ)
MathIsValidNumber対応対応(NaN/Inf検出)
MathRand範囲0〜327670〜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));
  }

三角関数の入力はすべてラジアンです。度数法から変換する場合はdegrees * M_PI / 180.0を使用してください。M_PIは定数(3.14159...)として定義済みです。

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乱数生成モンテカルロ、ランダムテスト
MathIsValidNumberNaN/Inf検出計算結果の安全性チェック
  • NormalizeDoubleは注文前に必ず使用。忘れるとINVALID_PRICEエラーの原因に
  • 浮動小数点の直接比較(==)は危険。NormalizeDoubleで差分を比較する
  • ロット計算はMathFloor(切り捨て)で安全側に丸める
  • MathIsValidNumberでNaN/Infをチェックし、計算の安全性を確保
  • 統計計算(標準偏差・相関・シャープレシオ)でEAの品質を定量評価

プロが開発したEAをお探しの方は → シストレ.COM EA一覧

シストレ.COMで実績のあるEAが使い放題!/
無料会員登録はこちら
FX自動売買でEAを探すなら
シストレ.COM
実績あるEAが無料
厳格な審査を乗り越えた実績のあるEAが無料で使えます!
自由な口座選び
有料版を購入し柔軟な口座選びが可能!
フォワードテスト公開
全EAのフォワードテスト結果を公開中!
FX初心者も安心
初心者の方も安心して取引を始められます。
多様なEA選択肢
様々な種別のEAをご用意!自分の手法にあった取引が可能です。
信頼のFX会社と提携
人気のFX会社と提携中!様々なFX会社から選べます。
1分で登録完了!EA探すならシストレ.COM!/
無料会員登録はこちら
【FX自動売買】システムトレード完全ガイド|Forex Guide

この記事が気に入ったら
フォローしてね!

お役立ち情報をシェアする
  • URLをコピーしました!
目次