固定ロット数は分かりやすいが適切ではない
仕掛ける時にロット数を設定する必要があります。一番シンプルなのは固定ロット数です。口座にいくらあるかは関係なく、常に一定のロット数を投入する方法ですね。
固定ロット数は分かりやすくて良い面もあるのですが、資金効率、資金管理の面から見て良いとは言えません。
資金効率、資金管理の面から見て、より好ましいのは資産のN%のリスクでロット数を決める方法です。例えば、100万円で2%のリスクとした場合、損切り時に最大2万円の損失になるようにロット数を決めるわけです。
こうすることで、(勝てば)複利を効かすことができますし、(負ければ)ロット数を減らすことができます。100万円の時に0.1ロットなら問題ないでしょうが、仮に20万円まで減った時に0.1ロットではハイレバ過ぎます。
豊嶋久道氏の「FXメタトレーダー実践プログラミング」にもロット数計算の関数が掲載されていますが、レバレッジ倍率をベースにしたもので、個人的には使いにくいという印象です。通常、仕掛ける時に「よし、今度はレバレッジ2倍で注文しよう!」って考えないですよね。レバレッジ倍率はあくまで結果であって、ロット数を決める際に使うものではないというのが私の考えです。
※豊嶋氏の名誉のために書かせていただきますが、上記の書籍は非常に素晴らしい内容(特に構成が素敵です)で、これからMQL4を学ばれる方にとってはバイブルとも言える書籍です。事実、私もこの書籍でMQL4の基礎を学び、現在に至っています。
まず、ライブラリーを作ります(ライブラリーの作り方はよく使う関数をライブラリー化 を参照)。ファイル名は「LotSizing.mqh」にしました。
中身は以下の通りです。
01 | //+------------------------------------------------------------------+ |
02 | //| LotSizing.mqh | |
03 | //| Copyright (c) 2015, りゅーき | |
04 | //| https://autofx100.com/ | |
05 | //+------------------------------------------------------------------+ |
06 | #property copyright "Copyright (c) 2015, りゅーき" |
07 | #property link "https://autofx100.com/" |
08 | #property version "1.00" |
09 |
10 | //+------------------------------------------------------------------+ |
11 | //|【関数】資産のN%のリスクのロット数を計算する | |
12 | //| | |
13 | //|【引数】 IN OUT 引数名 説明 | |
14 | //| --------------------------------------------------------- | |
15 | //| ○ aFunds 資金 | |
16 | //| AccountFreeMargin() | |
17 | //| AccountBalance() | |
18 | //| ○ aSymbol 通貨ペア | |
19 | //| ○ aStopLossPips 損切り値(pips) | |
20 | //| ○ aRiskPercent リスク率(%) | |
21 | //| | |
22 | //|【戻値】ロット数 | |
23 | //| | |
24 | //|【備考】計算した結果、最小ロット数未満になる場合、-1を返す | |
25 | //+------------------------------------------------------------------+ |
26 | double calcLotSizeRiskPercent( double aFunds, string aSymbol, double aStopLossPips, double aRiskPercent) |
27 | { |
28 | // 取引対象の通貨を1ロット売買した時の1ポイント(pipsではない!)当たりの変動額 |
29 | double tickValue = MarketInfo(aSymbol, MODE_TICKVALUE); |
30 |
31 | // tickValueは最小価格単位で計算されるため、3/5桁業者の場合、10倍しないと1pipsにならない |
32 | if (MarketInfo(aSymbol, MODE_DIGITS) == 3 || MarketInfo(aSymbol, MODE_DIGITS) == 5){ |
33 | tickValue *= 10.0; |
34 | } |
35 |
36 | double riskAmount = aFunds * (aRiskPercent / 100.0); |
37 |
38 | double lotSize = riskAmount / (aStopLossPips * tickValue); |
39 |
40 | double lotStep = MarketInfo(aSymbol, MODE_LOTSTEP); |
41 |
42 | // ロットステップ単位未満は切り捨て |
43 | // 0.123⇒0.12(lotStep=0.01の場合) |
44 | // 0.123⇒0.1 (lotStep=0.1の場合) |
45 | lotSize = MathFloor(lotSize / lotStep) * lotStep; |
46 |
47 | // 証拠金ベースの制限 |
48 | double margin = MarketInfo(aSymbol, MODE_MARGINREQUIRED); |
49 | |
50 | if (margin > 0.0){ |
51 | double accountMax = aFunds / margin; |
52 |
53 | accountMax = MathFloor(accountMax / lotStep) * lotStep; |
54 |
55 | if (lotSize > accountMax){ |
56 | lotSize = accountMax; |
57 | } |
58 | } |
59 |
60 | // 最大ロット数、最小ロット数対応 |
61 | double minLots = MarketInfo(aSymbol, MODE_MINLOT); |
62 | double maxLots = MarketInfo(aSymbol, MODE_MAXLOT); |
63 |
64 | if (lotSize < minLots){ |
65 | // 仕掛けようとするロット数が最小単位に満たない場合、 |
66 | // そのまま仕掛けると過剰リスクになるため、エラーに |
67 | lotSize = -1.0; |
68 | } else if (lotSize > maxLots){ |
69 | lotSize = maxLots; |
70 | } |
71 |
72 | return (lotSize); |
73 | } |
コードのコメントにも記載していますが、まず押さえるべきポイントはtickValueです。
1 | // 取引対象の通貨を1ロット売買した時の1ポイント(pipsではない!)当たりの変動額 |
2 | 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当たりの変動額になります。
1 | // tickValueは最小価格単位で計算されるため、3/5桁業者の場合、10倍しないと1pipsにならない |
2 | if (MarketInfo(aSymbol, MODE_DIGITS) == 3 || MarketInfo(aSymbol, MODE_DIGITS) == 5){ |
3 | tickValue *= 10.0; |
4 | } |
riskAmountは資金(余剰証拠金にするか口座残高にするかはお好みで)の何パーセントの損失額になります。100万円の2%ならriskAmountは2万円ということです。それを損切りの値幅における1ロット当たりの損失額で割ることでロット数が求められます。
1 | double riskAmount = aFunds * (aRiskPercent / 100.0); |
2 | double lotSize = riskAmount / (aStopLossPips * tickValue); |
この段階のlotSizeは、上記の計算結果によっては、0.123753…のようにロットステップの単位になっていない場合があります。そのため、以下の処理でロットステップの単位に揃えています。切り捨てにしているのは、過剰ロット数を避けてリスクを減らすためです。
1 | // ロットステップ単位未満は切り捨て |
2 | // 0.123⇒0.12(lotStep=0.01の場合) |
3 | // 0.123⇒0.1 (lotStep=0.1の場合) |
4 | lotSize = MathFloor(lotSize / lotStep) * lotStep; |
以降の処理はロット数計算における異常時の対応で、本質から外れます。詳しくはFXメタトレーダー実践プログラミングへの補足(3)/CalculateLotsの改良?をご覧ください。
なお、本関数を作成するにあたって、FXメタトレーダー実践プログラミングへの補足(3)/CalculateLotsの改良?と「[MQL4] 資産の4%のリスクで取引する(リンク先消滅)」を参考にさせていただきました。
EA本体に記述する内容は以下の通りです。UseVariableLotSizeFlgで固定ロットか変動ロットかを切り替えられるようにしています。また、RiskPercentは何パーセントのリスクを負うかを設定するパラメータです。
1 | //+------------------------------------------------------------------+ |
2 | //| ライブラリ | |
3 | //+------------------------------------------------------------------+ |
4 | // … |
5 | #include <Original/LotSizing.mqh> |
6 | // … |
1 | //+------------------------------------------------------------------+ |
2 | //| EAパラメータ設定情報 | |
3 | //+------------------------------------------------------------------+ |
4 | // … |
5 | extern bool UseVariableLotSizeFlg = true ; |
6 | extern double FixLotSize = 0.01; |
7 | extern double RiskPercent = 2.0; |
8 | // … |
01 | //+------------------------------------------------------------------+ |
02 | //| Expert tick function | |
03 | //+------------------------------------------------------------------+ |
04 | void OnTick() |
05 | { |
06 | // … |
07 | // ----------------------------------------------------------------- |
08 | // ロット数計算 |
09 | // ----------------------------------------------------------------- |
10 | if (UseVariableLotSizeFlg){ |
11 | double lotSize = calcLotSizeRiskPercent(AccountFreeMargin(), Symbol(), InitialSL_Pips, RiskPercent); |
12 | } else { |
13 | lotSize = FixLotSize; |
14 | } |
15 | // … |
16 | } |
サンプルEA
イメージが沸きづらいと思いますので、サンプルEAを載せておきます。
01 | //+------------------------------------------------------------------+ |
02 | //| Sample.mq4 | |
03 | //| Copyright (c) 2015, りゅーき | |
04 | //| https://autofx100.com/ | |
05 | //+------------------------------------------------------------------+ |
06 | #property copyright "Copyright (c) 2015, りゅーき" |
07 | #property link "https://autofx100.com/" |
08 | #property version "1.00" |
09 |
10 | //+------------------------------------------------------------------+ |
11 | //| ライブラリ | |
12 | //+------------------------------------------------------------------+ |
13 | #include <stderror.mqh> |
14 | #include <stdlib.mqh> |
15 | #include <WinUser32.mqh> |
16 | #include <Original/Basic.mqh> |
17 | #include <Original/DateAndTime.mqh> |
18 | #include <Original/LotSizing.mqh> |
19 | #include <Original/OrderHandle.mqh> |
20 | #include <Original/OrderReliable.mqh> |
21 |
22 | //+------------------------------------------------------------------+ |
23 | //| EAパラメータ設定情報 | |
24 | //+------------------------------------------------------------------+ |
25 | extern string Note01 = "=== General ==================================================" ; |
26 | extern int MagicNumber = 7777777; |
27 | extern int SlippagePips = 5; |
28 | extern string Comments = "" ; |
29 | extern string Note01_1 = "--- Risk % lot -----------------------------------------------" ; |
30 | extern bool UseVariableLotSizeFlg = true ; |
31 | extern double FixLotSize = 0.01; |
32 | extern double RiskPercent = 2.0; |
33 |
34 | extern string Note02 = "=== Exit =====================================================" ; |
35 | extern double InitialSL_Pips = 50.0; |
36 |
37 | //+------------------------------------------------------------------+ |
38 | //| グローバル変数 | |
39 | //+------------------------------------------------------------------+ |
40 | // 共通 |
41 | double gPipsPoint = 0.0; |
42 | int gSlippage = 0.0; |
43 | color gArrowColor[6] = {Blue, Red, Blue, Red, Blue, Red}; //BUY: Blue, SELL: Red |
44 |
45 | //+------------------------------------------------------------------+ |
46 | //| Expert initialization function | |
47 | //+------------------------------------------------------------------+ |
48 | int OnInit() |
49 | { |
50 | gPipsPoint = currencyUnitPerPips(Symbol()); |
51 | gSlippage = getSlippage(Symbol(), SlippagePips); |
52 |
53 | return (INIT_SUCCEEDED); |
54 | } |
55 |
56 | //+------------------------------------------------------------------+ |
57 | //| Expert deinitialization function | |
58 | //+------------------------------------------------------------------+ |
59 | void OnDeinit( const int reason) |
60 | { |
61 | } |
62 |
63 | //+------------------------------------------------------------------+ |
64 | //| Expert tick function | |
65 | //+------------------------------------------------------------------+ |
66 | void OnTick() |
67 | { |
68 | // ----------------------------------------------------------------- |
69 | // ロット数計算 |
70 | // ----------------------------------------------------------------- |
71 | if (UseVariableLotSizeFlg){ |
72 | double lotSize = calcLotSizeRiskPercent(AccountFreeMargin(), Symbol(), InitialSL_Pips, RiskPercent); |
73 | } else { |
74 | lotSize = FixLotSize; |
75 | } |
76 |
77 | if (lotSize < 0){ |
78 | return ; |
79 | } |
80 |
81 | // ----------------------------------------------------------------- |
82 | // 仕掛け |
83 | // ----------------------------------------------------------------- |
84 | // 成行注文 |
85 | int ticket = orderSendReliableRange(Symbol(), OP_BUY, lotSize, Ask, gSlippage, InitialSL_Pips, 0.0, Comments, MagicNumber, 0, gArrowColor[OP_BUY]); |
86 |
87 | // 本来はticketの値によって後続の処理を制御する必要があるが、簡単のため、ここでは無視 |
88 |
89 | } |