固定ロット数は分かりやすいが適切ではない
仕掛ける時にロット数を設定する必要があります。一番シンプルなのは固定ロット数です。口座にいくらあるかは関係なく、常に一定のロット数を投入する方法ですね。
固定ロット数は分かりやすくて良い面もあるのですが、資金効率、資金管理の面から見て良いとは言えません。
資金効率、資金管理の面から見て、より好ましいのは資産の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の値によって後続の処理を制御する必要があるが、簡単のため、ここでは無視
}
