ここでは、FXで有名な取引手法であるトラリピのコードについて解説していきます。
買いのみトラリピEAになりますが、作ったあとの動作も分かり易い上、コードも短いので初めて EAを作る練習台として良いと思います。
作成した買いのみトラリピEAを実際に使用する時は、ロスカット価格を理解した上で取引するようにしてください。トラリピのロスカット価格はこちらから計算ができます。
買いのみトラリピEAのコード
// BuyOnlyTraripEA.mq4 (買いのみトラリピEA フランのなるほどMT4)
//★1.初期設定値の決定★★★★★★★★★★★★
#property strict
extern double lots = 0.01; // ロット数
extern double maxPrice = 100.0; // 取引最大値(各通貨単位)
extern int trapsu = 100; // トラップ数(個)
extern int profitPips = 100; // 利益幅(Pips)
extern int trapIntervalPips = 50; // トラップ幅(Pips)
extern int slippage = 3; // スリッページ(Pips)
extern int magic = 1001; // マジックナンバー
extern int orderRangePips = 100; // 指値注文範囲(Pips)
int slippagePips; // スリッページの単位をPipsからPointへ変換した変数
double PipsPerCunit; // 1Pipsあたり各国通貨単位に変換する係数
int i, j, ticket; // i:トラップ数計数用の変数, j:ポジション数計数用の変数, ticket:チケットNoの変数
//★2.初期処理★★★★★★★★★★★★★★★★
void OnInit() { // 初期処理
int coe = 1; // 2,4桁表示業者の場合の係数は「1」
if(_Digits == 3 || _Digits == 5) { // 3,5桁表示業者の場合の係数は「10」
coe = 10;
}
PipsPerCunit = _Point * coe; // 1Pipsあたりを各国通貨単位に変換する係数
slippagePips = slippage * coe; // スリッページの単位をPipsからPointへ変換
}
//★3.終了処理★★★★★★★★★★★★★★★★
void OnDeinit(const int reason) { //終了処理
ObjectsDeleteAll(); // オブジェクトテキストをEA削除時消すための処理
}
//★4.レート変動毎の処理★★★★★★★★★★★
void OnTick() { //レート変動毎の処理
for(i=0; i < trapsu; i++){
//●4.①注文範囲内かのチェック●●●●●●●●
double openPrice = maxPrice - trapIntervalPips * i * PipsPerCunit;
RefreshRates();
double currentPrice = Ask;
if( orderRangePips != 0 && (openPrice < currentPrice - orderRangePips * PipsPerCunit || openPrice > currentPrice + orderRangePips * PipsPerCunit) ){
continue; // 現在値が指値注文範囲より乖離した注文は出さない
}
//●4.②決済価格とマジックナンバーの設定●●●
//注文範囲内に入ったので処理を続行
double closePrice = openPrice + profitPips * PipsPerCunit; //イフダン注文の決済価格を設定
int magicNumber = magic + i;
//●4.③注文有無のチェック●●●●●●●●●●
ticket = -1; // 既に注文orポジション化されているかチェック。無しの時、ticket=-1。
for(j=0; j < OrdersTotal(); j++) {
if(OrderSelect(j, SELECT_BY_POS) == false) {
break;
}
if(magicNumber == OrderMagicNumber() && OrderSymbol() == _Symbol) {
ticket = OrderTicket();
break;
}
}
//●4.④注文無しの時は注文する●●●●●●●●
if(ticket <= 0) { // 注文がなければ(ticket=-1の時)リピートする
int tradeType; // 注文形式を決定する
if(openPrice <= Ask) {
tradeType = OP_BUYLIMIT; // 指値 <= 現在価格(Ask) の時、買い指値注文
} else {
tradeType = OP_BUYSTOP; // 指値 > 現在価格(Ask) の時、買い逆指値注文
}
openPrice = NormalizeDouble(openPrice, Digits); //指値価格の有効桁丸め
closePrice = NormalizeDouble(closePrice, Digits); //決済価格の有効桁丸め
ticket = OrderSend(Symbol(), tradeType, lots, openPrice, slippagePips, 0, closePrice, "B_T_EA:" + (string)magicNumber, magicNumber, 0, clrNONE); //イフダン注文
}
}
}
このコードをコンパイルして実行するのに、自動売買を許可しておく必要があります。
自動売買を許可する方法はこちら、コードをコンパイルして実行する方法はこちらを参考にしてください。(デモ口座でお試しください)
買いのみトラリピEAの概要
買いのみトラリピEAは、「パラメーターの入力」で設定した値をもとに一定の価格幅ごと段階的にイフダン注文をしていきます。
パラメータの内容 「BuyOnlyTraripEA.ex4」 (買いのみトラリピEA)
変数 | 初期値 | 設定値の説明 |
---|---|---|
ロット数 | 0.01 | 1ポジションの取引通貨量です。0.01ロットが最低取引ロット数で、1ロット 10万通貨の場合は 1000通貨、1ロット 1000通貨の場合は 10通貨が最低取引通貨量になります。 |
取引最大値 (各通貨単位) | 100.0 | 取引する通貨ペアの最大の価格です。設定値以上の取引はしません。 |
トラップ数 (個) | 100 | 最大ポジション数です。この設定値以上の取引はしません。 たとえば、トラップ数が 100、取引最大値が 100円、トラップ幅が 10Pips (対日本円で 0.1円)であれば、100.0~90.1円の範囲でイフダン注文が繰り返されることになります。 |
利益幅 (Pips) | 100 | 1ポジションあたりの利益幅です。対日本円の場合 100Pips=1.0円なので、 1000通貨単位で取引して 100Pipsの利益幅を設定した時、 1000通貨 × 1.0 円 = 1000円 が決済した時の 1ポジションあたりの利益額になります。 |
トラップ幅 (Pips) | 50 | 指値注文をする間隔です。対日本円の場合、10Pips=0.1円の幅になります。設定幅で段階的にイフダン注文が仕掛けられます。 |
スリッページ (Pips) | 3 | スリッページの設定です。 Pips単位で設定してください。 |
マジックナンバー | 1001 | 各ポジションに付与される識別番号です。例えば、マジックナンバーが 1001,トラップ数が 100の時、マジックナンバーは 1001~1100の範囲になります。この時、取引最大値が 100円、トラップ幅が 10Pips (対日本円で 0.1円) であれば、100.0円-1001、99.9円-1002、99.8円-1003、…、90.2円-1099、90.1円-1100のようにマジックナンバーが割り当てられます。 |
指値注文範囲 (Pips) | 100 | イフダン注文を発生させる範囲です。最初からトラップ数分のイフダン注文を仕掛けるのではなく指値注文範囲が 100Pipsの場合、現在の価格 (Ask値) の前後 100Pips (対日本円の時 1円) 幅に対して注文を発生させます。価格は変動しますので、その時の価格の前後 1円幅でまだイフダン注文がポジションになければイフダン注文を発生させていきます。 仮に取引を始めた時の USDJPYの価格が 100円、取引最大値が 101円、指値注文範囲が 100Pipsの場合、100~101円は逆指値のイフダン注文、99~100円は指値のイフダン注文をすることになります。 |
任意設定した変数とその仕様
プログラム中で設定した変数の一覧表です。
変数名 | データ型 | 説明 |
---|---|---|
coe | int | PipsをPointへ変換する係数。ここでは、取引通貨ペアが 小数点以下 2,4桁の時は、1Pips = 1Point、 小数点以下 3,5桁の時は、1Pips = 10Point になります。 Pipsと Pointの関係はこちらで確認してください。 |
PipsPerCunit | double | 1Pipsあたりを各通貨単位に変換する係数。 |
slippagePips | int | スリッページの単位を Pipsから Pointへ変換した値。 |
reason | int | 初期化解除の理由コードを表す変数です。EAをチャートから削除する時にオブジェクトテキストやオブジェクトラインなどを削除します。データ型は intですが「REASON_REMOVE」などは MQL4の定数でint型にも対応します。仕様と対応表はこちらで確認してください。 |
i | int | パラメーターで設定した「トラップ数(個)」の変数です。この変数は「openPrice」や「magicNumber」に比例します。 |
openPrice | double | 新規取引するときの指値 (or 逆指値) 価格を表す変数です。 |
currentPrice | double | 取引する通貨ペアの現在の買値を表す変数です。直前の RefreshRates関数により常に最新の買値が表示されます。 |
closePrice | double | 決済価格です。指値価格の「openPrice」からパラメーターで設定した「利益幅(Pips)」分を足して決定されます。 |
magicNumber | int | 各ポジションに付与されるこちらで設定した番号 (マジックナンバー) の変数です。各ポジションに番号を付与することで現在ポジションとして既に確立しているか識別します。重複した注文をしないためにマジックナンバーで識別させます。 |
ticket | int | 予約注文した時に自動付与される整数で 1以上のチケット番号を表す変数です。任意のポジションで注文がまだ確立されていない時、既にそのポジションが決済されてポジションが無い時は -1になるようにプログラムで設定されています。 |
j | int | この EAで現在取引中のポジションを 0から順番に並べた番号の変数です。(チケット番号、マジックナンバーとは違います) MT4で自動取得されます。ポジションが決済などで減少するとポジションが持つ番号が 0から順番に並んだ番号で再取得されます。ですので途中の番号が飛ぶことはありません。(0,1,3…のように 2が飛ぶことはないです) |
tradeType | int | 注文する時の方法を表す変数です。「指値買い注文」「逆指値買い注文」かを見ています。値と注文IDの対応はこちらの表を参考にしてください。 |
コードの解説
大きく次の 4つのパートに分けて解説します。
- 初期設定値の決定
- 初期処理
- 終了処理
- レート変動毎の処理
1.初期設定の決定
//★1.初期設定値の決定★★★★★★★★★★★★
#property strict
extern double lots = 0.01; // ロット数
extern double maxPrice = 100.0; // 取引最大値(各通貨単位)
extern int trapsu = 100; // トラップ数(個)
extern int profitPips = 100; // 利益幅(Pips)
extern int trapIntervalPips = 50; // トラップ幅(Pips)
extern int slippage = 3; // スリッページ(Pips)
extern int magic = 1001; // マジックナンバー
extern int orderRangePips = 100; // 指値注文範囲(Pips)
int slippagePips; // スリッページの単位をPipsからPointへ変換した変数
double PipsPerCunit; // 1Pipsあたり各国通貨単位に変換する係数
int i, j, ticket; // i:トラップ数計数用の変数, j:ポジション数計数用の変数, ticket:チケットNoの変数
トラリピをするためのロット数や取引範囲、利益の設定をするためのものです。
先頭行の「#property strict」はコンパイルを厳密にチェックするのに必要なので外さないでください。
行の先頭に「extern」が付いているものが「パラメーターの入力」で手動設定ができます。
下から3行は、プログラムで使用する変数の型を宣言しています。
2.初期処理
//★2.初期処理★★★★★★★★★★★★★★★★
void OnInit() { // 初期処理
int coe = 1; // 2,4桁表示業者の場合の係数は「1」
if(_Digits == 3 || _Digits == 5) { // 3,5桁表示業者の場合の係数は「10」
coe = 10;
}
PipsPerCunit = _Point * coe; // 1Pipsあたりを各国通貨単位に変換する係数
slippagePips = slippage * coe; // スリッページの単位をPipsからPointへ変換
}
OnInit関数は、プログラムを開始する時に 1度だけ実行されるプログラムです。
ここでは、取引する通貨ペアの表示桁数をみることでPipsとPointの関係を計算させています。これは、3 or 5桁と 2 or 4桁で表示される通貨ペアでは Pipsと Pointの関係が違ってくるのでどの通貨ペアを選択しても問題が起きないように Pipsと Pointの関係を補正することが目的のコードになります。
スリッページはパラメーターの入力では、単位が Pipsになっていますが、注文する時は単位が Pointになるので補正計算する必要があります。
3.終了処理
//★3.終了処理★★★★★★★★★★★★★★★★
void OnDeinit(const int reason) { //終了処理
ObjectsDeleteAll(); // オブジェクトテキストをEA削除時消すための処理
}
OnDeinit関数は、プログラムを終了する時に実行されるプログラムです。
ここでは、EAの実行でオブジェクト機能による描画やコメントが表示されていた場合、EAを削除するのと同時にチャートからそれらも削除されるようにしています。
今回の「買いのみトラリピEA」は、オブジェクト機能を使っていませんので必要ないですが、他の EA作成でもそのまま使えるように一つの型として入れています。
4.価格変動毎の処理
//★4.レート変動毎の処理★★★★★★★★★★★
void OnTick() { //レート変動毎の処理
for(i=0; i < trapsu; i++){
//●4.①注文範囲内かのチェック●●●●●●●●
double openPrice = maxPrice - trapIntervalPips * i * PipsPerCunit;
RefreshRates();
double currentPrice = Ask;
if(orderRangePips != 0 && (openPrice < currentPrice - orderRangePips * PipsPerCunit || openPrice > currentPrice + orderRangePips * PipsPerCunit) ){
continue; // 現在値が指値注文範囲より乖離した注文は出さない
}
//●4.②決済価格とマジックナンバーの設定●●●
//注文範囲内に入ったので処理を続行
double closePrice = openPrice + profitPips * PipsPerCunit; //イフダン注文の決済価格を設定
int magicNumber = magic + i;
//●4.③注文有無のチェック●●●●●●●●●●
ticket = -1; // 既に注文orポジション化されているかチェック。無しの時、ticket=-1。
for(j=0; j < OrdersTotal(); j++) {
if(OrderSelect(j, SELECT_BY_POS) == false) {
break;
}
if(magicNumber == OrderMagicNumber() && OrderSymbol() == _Symbol) {
ticket = OrderTicket();
break;
}
}
//●4.④注文無しの時は注文する●●●●●●●●
if(ticket <= 0) { // 注文がなければ(ticket=-1の時)リピートする
int tradeType; // 注文形式を決定する
if(openPrice <= Ask) {
tradeType = OP_BUYLIMIT; // 指値 <= 現在価格(Ask) の時、買い指値注文
} else {
tradeType = OP_BUYSTOP; // 指値 > 現在価格(Ask) の時、買い逆指値注文
}
openPrice = NormalizeDouble(openPrice, Digits); //指値価格の有効桁丸め
closePrice = NormalizeDouble(closePrice, Digits); //決済価格の有効桁丸め
ticket = OrderSend(Symbol(), tradeType, lots, openPrice, slippagePips, 0, closePrice, "B_T_EA:" + (string)magicNumber, magicNumber, 0, clrNONE); //イフダン注文
}
}
}
「買いのみトラリピEA」の中心になるのがこのパートです。
ここでは、イフダン注文をするため、現価格の注文範囲を確認します。注文範囲内であれば、注文を重複させないために同じ注文の有無を確認します。同じ注文が無ければ、イフダン注文をします。
OnTick関数は、これらの工程を取引する通貨ペアの変動毎 (Tick) にプログラムが実行されます。通常一日に数万回以上繰り返されています。
このOnTick関数の中の詳細について処理内容を 4つのパートに分けて解説していきます。
①注文範囲内かのチェック
②決済価格とマジックナンバーの設定
③注文有無のチェック
④注文ポジション無しの時は注文をする
①注文範囲内かのチェック
//●4.①注文範囲内かのチェック●●●●●●●●
double openPrice = maxPrice - trapIntervalPips * i * PipsPerCunit;
RefreshRates();
double currentPrice = Ask;
if( orderRangePips != 0 && (openPrice < currentPrice - orderRangePips * PipsPerCunit || openPrice > currentPrice + orderRangePips * PipsPerCunit) ){
continue; // 現在値が指値注文範囲より乖離した注文は出さない
}
ここでは注文する指値価格が現在の買い価格からパラメーターで設定した「指値注文範囲(Pips)」に入っているかどうかを見ます。例えば、
現在の買い価格が 100円で、指値注文範囲(Pips)を 100Pips (対日本円で 1円) に設定したとすると
100 ± 1円、つまり、99~101円の範囲に注文する指値価格が入っていれば次の処理へ移動することになります。範囲に入っていなければ、次の処理に移動はしません。
RefreshRates関数は、時間の経過と共に Ask値が変動するので最新の状態にするために使われています。
②決済価格とマジックナンバーの設定
//●4.②決済価格とマジックナンバーの設定●●●
//注文範囲内に入ったので処理を続行
double closePrice = openPrice + profitPips * PipsPerCunit; //イフダン注文の決済価格を設定
int magicNumber = magic + i;
①で指値価格 (openPrice) が決定されたので、これをもとに決済価格 (closePrice) を決定します。決済価格はパラメーターで設定した「利益幅(Pips)」から決定されます。仮に、指値価格が 100円で利益幅 (Pips) が 100Pips (対日本円で 1円) であれば、101円が決済価格になります。
マジックナンバーはパラメーターで設定した「マジックナンバー」が 1001であれば、そこから計数した変数iがプラスされます。このマジックナンバーと指値価格は比例関係にあります。
③注文有無のチェック
//●4.③注文有無のチェック●●●●●●●●●●
ticket = -1; // 既に注文orポジション化されているかチェック。無しの時、ticket=-1。
for(j=0; j < OrdersTotal(); j++) {
if(OrderSelect(j, SELECT_BY_POS) == false) {
break;
}
if(magicNumber == OrderMagicNumber() && OrderSymbol() == _Symbol) {
ticket = OrderTicket();
break;
}
}
現時点で予約注文中か、予約注文の指値注文が約定してポジション化されているかをチェックしています。どちらも該当しない (ポジション化されていない) 場合は、変数 ticketが -1を返します。
変数 j はOrderSelect関数で使われる、現在取引中のポジションとオーダーに付与されるインデックス番号です。注文した順番に 0、1、2、…というように付与されます。(チケット番号、マジックナンバーとは違います) ポジションが決済などで減少するとポジションが持つインデックス番号が 0から順番に並んだ番号で再取得されます。ですので途中の番号が飛ぶことはありません。(0,1,3…のように 2が飛ぶことはないです) ゆえに、変数 j の最大値は常に全ポジション数 (OrdersTotal関数) 未満の値になります。これを利用して現在のポジションとオーダー情報 (今回の場合は、マジックナンバーと取引通貨ペア名) をチェックしていきます。
④注文ポジション無しの時は注文をする
//●4.④注文無しの時は注文する●●●●●●●●
if(ticket <= 0) { // 注文がなければ(ticket=-1の時)リピートする
int tradeType; // 注文形式を決定する
if(openPrice <= Ask) {
tradeType = OP_BUYLIMIT; // 指値 <= 現在価格(Ask) の時、買い指値注文
} else {
tradeType = OP_BUYSTOP; // 指値 > 現在価格(Ask) の時、買い逆指値注文
}
openPrice = NormalizeDouble(openPrice, Digits); //指値価格の有効桁丸め
closePrice = NormalizeDouble(closePrice, Digits); //決済価格の有効桁丸め
ticket = OrderSend(Symbol(), tradeType, lots, openPrice, slippagePips, 0, closePrice, "B_T_EA:" + (string)magicNumber, magicNumber, 0, clrNONE); //イフダン注文
}
変数ticketが 1以上の時は、まだ決済されていない注文が有ることを意味します。ですので、注文をしません。この条件分岐で同じマジックナンバーが存在することは無いことになります。
変数ticketが -1の時は、イフダン注文をするための処理をします。
現時点の価格 (Ask) より指値価格 (openPrice) の方が低い場合は「買い指値のイフダン注文」、逆に高い場合は「買い逆指値のイフダン注文」をするように条件分岐をします。
注文する際は、指値 (逆指値) 価格と決済価格についてNormalizeDouble関数を使って通貨ペアの有効桁数になるように数字の丸め処理を行ないます。
これまでの値を使って、OrderSend関数でイフダン注文をします。注文が成立すると変数 ticketに 1以上の整数でチケット番号が返されることになります。
注文にはコメント欄に「B_T_EA:〇〇〇〇」と入るようになっています。「B_T_EA」はBuyOnlyTraripEAの略で、「〇〇〇〇」はマジックナンバーが入ります。コメント欄は自分が分かり易いように自由に設定してみてください。
まとめ
以上、「買いのみトラリピEA」のコード解説です。取引に売りも含めるなど、まだまだいろいろ改造ができるので勉強しながら是非試してみてください。
「リピ・トレ(EA)」はこの進化版です。是非こちらも試してみてください。
また、今回の「買いのみトラリピEA」を使う場合、多くのポジションを持つことがあるので「全注文を決済 又は キャンセル処理するEA」を合わせて使用すると便利に使えます。
ここで作成した「買いのみトラリピEA」をストラテジーテスターでテストをするのであれば次を参考にするとよいでしょう。