固定ロット数は分かりやすいが適切ではない
仕掛ける時にロット数を設定する必要があります。一番シンプルなのは固定ロット数です。口座にいくらあるかは関係なく、常に一定のロット数を投入する方法ですね。
固定ロット数は分かりやすくて良い面もあるのですが、資金効率、資金管理の面から見て良いとは言えません。
資金効率、資金管理の面から見て、より好ましいのは資産のN%のリスクでロット数を決める方法です。例えば、100万円で2%のリスクとした場合、損切り時に最大2万円の損失になるようにロット数を決めるわけです。
こうすることで、(勝てば)複利を効かすことができますし、(負ければ)ロット数を減らすことができます。100万円の時に0.1ロットなら問題ないでしょうが、仮に20万円まで減った時に0.1ロットではハイレバ過ぎます。
豊嶋久道氏の「FXメタトレーダー実践プログラミング」にもロット数計算の関数が掲載されていますが、レバレッジ倍率をベースにしたもので、個人的には使いにくいという印象です。通常、仕掛ける時に「よし、今度はレバレッジ2倍で注文しよう!」って考えないですよね。レバレッジ倍率はあくまで結果であって、ロット数を決める際に使うものではないというのが私の考えです。
※豊嶋氏の名誉のために書かせていただきますが、上記の書籍は非常に素晴らしい内容(特に構成が素敵です)で、これからMQL4を学ばれる方にとってはバイブルとも言える書籍です。事実、私もこの書籍でMQL4の基礎を学び、現在に至っています。
まず、ライブラリーを作ります(ライブラリーの作り方はよく使う関数をライブラリー化 を参照)。ファイル名は「LotSizing.mqh」にしました。
中身は以下の通りです。
//+------------------------------------------------------------------+ //| LotSizing.mqh | //| Copyright (c) 2015, りゅーき | //| https://autofx100.com/ | //+------------------------------------------------------------------+ #property copyright "Copyright (c) 2015, りゅーき" #property link "https://autofx100.com/" #property version "1.00" //+------------------------------------------------------------------+ //|【関数】資産のN%のリスクのロット数を計算する | //| | //|【引数】 IN OUT 引数名 説明 | //| --------------------------------------------------------- | //| ○ aFunds 資金 | //| AccountFreeMargin() | //| AccountBalance() | //| ○ aSymbol 通貨ペア | //| ○ aStopLossPips 損切り値(pips) | //| ○ aRiskPercent リスク率(%) | //| | //|【戻値】ロット数 | //| | //|【備考】計算した結果、最小ロット数未満になる場合、-1を返す | //+------------------------------------------------------------------+ double calcLotSizeRiskPercent(double aFunds, string aSymbol, double aStopLossPips, double aRiskPercent) { // 取引対象の通貨を1ロット売買した時の1ポイント(pipsではない!)当たりの変動額 double tickValue = MarketInfo(aSymbol, MODE_TICKVALUE); // tickValueは最小価格単位で計算されるため、3/5桁業者の場合、10倍しないと1pipsにならない if(MarketInfo(aSymbol, MODE_DIGITS) == 3 || MarketInfo(aSymbol, MODE_DIGITS) == 5){ tickValue *= 10.0; } double riskAmount = aFunds * (aRiskPercent / 100.0); double lotSize = riskAmount / (aStopLossPips * tickValue); double lotStep = MarketInfo(aSymbol, MODE_LOTSTEP); // ロットステップ単位未満は切り捨て // 0.123⇒0.12(lotStep=0.01の場合) // 0.123⇒0.1 (lotStep=0.1の場合) lotSize = MathFloor(lotSize / lotStep) * lotStep; // 証拠金ベースの制限 double margin = MarketInfo(aSymbol, MODE_MARGINREQUIRED); if(margin > 0.0){ double accountMax = aFunds / margin; accountMax = MathFloor(accountMax / lotStep) * lotStep; if(lotSize > accountMax){ lotSize = accountMax; } } // 最大ロット数、最小ロット数対応 double minLots = MarketInfo(aSymbol, MODE_MINLOT); double maxLots = MarketInfo(aSymbol, MODE_MAXLOT); if(lotSize < minLots){ // 仕掛けようとするロット数が最小単位に満たない場合、 // そのまま仕掛けると過剰リスクになるため、エラーに lotSize = -1.0; }else if(lotSize > maxLots){ lotSize = maxLots; } return(lotSize); }
コードのコメントにも記載していますが、まず押さえるべきポイントはtickValueです。
// 取引対象の通貨を1ロット売買した時の1ポイント(pipsではない!)当たりの変動額 double tickValue = MarketInfo(aSymbol, MODE_TICKVALUE);
これは、取引対象の通貨を1ロット売買した時の1ポイント(pipsではない!)当たりの変動額を求める関数です。これに損切り値を掛けることで、その値幅における1ロット当たりの損失額が分かります。
MODE_TICKVALUEについて誤解されている記事(有名どころですと、MT4用EA開発時代 - ロット数の計算(サイジング)プログラミングについて【改訂版】や[EA Tip 8] 常に資金の2%で注文する)がありますが、「[MQL4] 資産の4%のリスクで取引する(リンク先消滅)」にもある通り、正しくはpipsではなくポイントです。MQL4はpipsという単位を理解できません。内部での管理はポイントで行われていますので、くれぐれもご注意ください。
次のコードも分かりにくいかもしれません。損切り値はpipsで考えていますので、単位を揃えるためにtickValueをポイントからpipsに変換する必要があります。3/5桁業者の場合、1ポイント=0.1pipsでした。つまり、tickValueを10倍することで、取引対象の通貨を1ロット売買した時の1pips当たりの変動額になります。
// tickValueは最小価格単位で計算されるため、3/5桁業者の場合、10倍しないと1pipsにならない if(MarketInfo(aSymbol, MODE_DIGITS) == 3 || MarketInfo(aSymbol, MODE_DIGITS) == 5){ tickValue *= 10.0; }
riskAmountは資金(余剰証拠金にするか口座残高にするかはお好みで)の何パーセントの損失額になります。100万円の2%ならriskAmountは2万円ということです。それを損切りの値幅における1ロット当たりの損失額で割ることでロット数が求められます。
double riskAmount = aFunds * (aRiskPercent / 100.0); double lotSize = riskAmount / (aStopLossPips * tickValue);
この段階のlotSizeは、上記の計算結果によっては、0.123753…のようにロットステップの単位になっていない場合があります。そのため、以下の処理でロットステップの単位に揃えています。切り捨てにしているのは、過剰ロット数を避けてリスクを減らすためです。
// ロットステップ単位未満は切り捨て // 0.123⇒0.12(lotStep=0.01の場合) // 0.123⇒0.1 (lotStep=0.1の場合) lotSize = MathFloor(lotSize / lotStep) * lotStep;
以降の処理はロット数計算における異常時の対応で、本質から外れます。詳しくはFXメタトレーダー実践プログラミングへの補足(3)/CalculateLotsの改良?をご覧ください。
なお、本関数を作成するにあたって、FXメタトレーダー実践プログラミングへの補足(3)/CalculateLotsの改良?と「[MQL4] 資産の4%のリスクで取引する(リンク先消滅)」を参考にさせていただきました。
EA本体に記述する内容は以下の通りです。UseVariableLotSizeFlgで固定ロットか変動ロットかを切り替えられるようにしています。また、RiskPercentは何パーセントのリスクを負うかを設定するパラメータです。
//+------------------------------------------------------------------+ //| ライブラリ | //+------------------------------------------------------------------+ // … #include <Original/LotSizing.mqh> // …
//+------------------------------------------------------------------+ //| EAパラメータ設定情報 | //+------------------------------------------------------------------+ // … extern bool UseVariableLotSizeFlg = true; extern double FixLotSize = 0.01; extern double RiskPercent = 2.0; // …
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // … // ----------------------------------------------------------------- // ロット数計算 // ----------------------------------------------------------------- if(UseVariableLotSizeFlg){ double lotSize = calcLotSizeRiskPercent(AccountFreeMargin(), Symbol(), InitialSL_Pips, RiskPercent); }else{ lotSize = FixLotSize; } // … }
サンプルEA
イメージが沸きづらいと思いますので、サンプルEAを載せておきます。
//+------------------------------------------------------------------+ //| Sample.mq4 | //| Copyright (c) 2015, りゅーき | //| https://autofx100.com/ | //+------------------------------------------------------------------+ #property copyright "Copyright (c) 2015, りゅーき" #property link "https://autofx100.com/" #property version "1.00" //+------------------------------------------------------------------+ //| ライブラリ | //+------------------------------------------------------------------+ #include <stderror.mqh> #include <stdlib.mqh> #include <WinUser32.mqh> #include <Original/Basic.mqh> #include <Original/DateAndTime.mqh> #include <Original/LotSizing.mqh> #include <Original/OrderHandle.mqh> #include <Original/OrderReliable.mqh> //+------------------------------------------------------------------+ //| EAパラメータ設定情報 | //+------------------------------------------------------------------+ extern string Note01 = "=== General =================================================="; extern int MagicNumber = 7777777; extern int SlippagePips = 5; extern string Comments = ""; extern string Note01_1 = "--- Risk % lot -----------------------------------------------"; extern bool UseVariableLotSizeFlg = true; extern double FixLotSize = 0.01; extern double RiskPercent = 2.0; extern string Note02 = "=== Exit ====================================================="; extern double InitialSL_Pips = 50.0; //+------------------------------------------------------------------+ //| グローバル変数 | //+------------------------------------------------------------------+ // 共通 double gPipsPoint = 0.0; int gSlippage = 0.0; color gArrowColor[6] = {Blue, Red, Blue, Red, Blue, Red}; //BUY: Blue, SELL: Red //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { gPipsPoint = currencyUnitPerPips(Symbol()); gSlippage = getSlippage(Symbol(), SlippagePips); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // ----------------------------------------------------------------- // ロット数計算 // ----------------------------------------------------------------- if(UseVariableLotSizeFlg){ double lotSize = calcLotSizeRiskPercent(AccountFreeMargin(), Symbol(), InitialSL_Pips, RiskPercent); }else{ lotSize = FixLotSize; } if(lotSize < 0){ return; } // ----------------------------------------------------------------- // 仕掛け // ----------------------------------------------------------------- // 成行注文 int ticket = orderSendReliableRange(Symbol(), OP_BUY, lotSize, Ask, gSlippage, InitialSL_Pips, 0.0, Comments, MagicNumber, 0, gArrowColor[OP_BUY]); // 本来はticketの値によって後続の処理を制御する必要があるが、簡単のため、ここでは無視 }