前回までのおさらい
前回はEA開発用テンプレートを用いて実際にEAを作る過程を解説しました。
※前回作ったMAクロスEAのコード全体記述については↓のリンクから確認ができます。
※EA開発用テンプレートの全体記述については↓のリンクから確認ができます。
※EA開発用テンプレートにインクルードファイルとして取り込んでいるファイルは以下のリンクからご確認いただけます。
- OriginalTrade.mqhファイル(注文関連のクラスや関数が盛り込まれているファイル)
- OriginalPrice.mqhファイル(価格情報取得・管理に関連する機能が盛り込まれているファイル)
- OriginalMoneyManagement.mqhファイル(資金管理関連の機能が盛り込まれているファイル)
- OriginalTrailingStop.mqhファイル(トレーリングストップやブレイクイーブンストップに関連する機能が盛り込まれているファイル)
- OriginalIndicators.mqhファイル(インジケータを扱う為の機能が盛り込まれているファイル)
- OriginalTimer.mqhファイル(トレードタイマーに関する機能が盛り込まれているファイル)
EA開発用テンプレートを利用したEA作りに慣れてもらうために、今回もEAを作っていきたいと思います。
今回作るのはボリンジャーバンドとRSIを利用した逆張り系のEAです。
相場の動きが極端に1方向に向かっているであろう点を見つけて、そろそろ相場が反転するのであろうという想定の下、相場の大きな流れとは逆方向にエントリーするシステムの作成を目指します。
まず価格帯がボリンジャーバンドの上バンド、あるいは下バンドを超えているのを確認します。その後その後、RSIで買われすぎや売られすぎの状態を確認します。
例えば、価格が下のボリンジャーバンドを下回り、RSIが30以下であれば買い注文を出します。価格が上のバンドを上回り、RSIが70以上であれば売り注文を出します。
ただ、これだけだと相場の流れにあまりにも逆行し過ぎているので、「相場がもうそろそろ反転する予兆」もエントリー条件に加味したいところです。そこでエントリー条件に2段階の確認プロセスを設け、逆張りの中でトレンド転換の予兆を捉えるようなEAを目指してEAの仕様を決定していきます。
今回作るEAの仕様
今回作るEAの仕様は以下の通りです↓
・保有ポジションは1つまで。
・売買判断にはRSI、及びボリンジャーバンドと価格との関係を用いる
・価格がボリンジャーバンドの下バンドの外側を推移しており、かつRSIの値が特定の値(input変数で変更可能にする)以下になった時を「第1段階の買いエントリーシグナル」とする。
・「第1段階の買いエントリーシグナル」を満たした状態で、価格がボリンジャーバンドの下バンド内側に回帰してきた時を「第2段階の買いエントリーシグナル」と見なして買いエントリーする(この時、もし売りポジションを持っていれば売りポジションは決済してクローズする)
・価格がボリンジャーバンド上バンドの外側を推移しており、かつRSIの値が特定の値(input変数で変更可能にする)以上になった時を「第1段階の売りエントリーシグナル」とする。
・「第1段階の売りエントリーシグナル」を満たした状態で、価格がボリンジャーバンドの上バンド内側に回帰してきた時を「第2段階の売りエントリーシグナル」と見なして売りエントリーする(この時、もし買いポジションを持っていれば買いポジションは決済してクローズする)
基本的には前回と同じく、すでにいろんな機能を実装しているEA開発用テンプレートに、エントリー条件記述を追加していくだけなのですが、今回は2点程作業を追加します。
1点目は、OriginalIndicators.mqhファイルへボリンジャーバンドとRSIを使う為の派生クラスを作成する事です。
・第110回「インジケータ取得クラスに派生クラスを追加する」
で手順を解説しているのですが、この時実際に解説して実装したのは移動平均線とストキャスティクスだけでした。
今回使うボリンジャーバンドやRSIはOriginalIndicators.mqhファイルに取り込んでいないので、復習も兼ねて今回追加していきます。
2点目は「今回作るEAの仕様」セクションで言及した、「第1段階の買いエントリーシグナル」「第1段階の売りエントリーシグナル」の状態を情報として格納するenum列挙型を作る事です。
今回はエントリーシグナルを2段階にするので、コード記述に落とし込むときに記述が複雑にならないようにする為の作業になります。
現時点ではどういうことなのかピンとこない人も多いと思います。後程詳しく説明しますので、今は「そんな作業をするんだ」ぐらいに思っておいてください。
という訳で、前回はやらなかった2つの作業を含めた、EA作成のロードマップを次のセクションで示します。
※enum列挙型については↓の記事をご参照ください
EA作成のロードマップ
今回作るEAは以下の手順を踏んで作成していきます↓
・ボリンジャーバンドとRSIを扱う為のインスタンスを宣言する。
・ボリンジャーバンド、RSIのパラメータ設定用のinput変数を宣言する。
・トレードシグナル用のEnum列挙型を作り、そのインスタンスを宣言する。
・OnInit関数にボリンジャーバンド及びRSIの設定に必要な記述をする。
1つ1つ順を追って見ていきましょう。
EA開発用テンプレートから新規ファイルを作成する
まずはEA開発用テンプレートから新規ファイルを作成します。これは前回やった事なので殊更説明を加える点はありませんが、一応簡単に説明します。
EA開発用テンプレートのソースコードをを当ブログのリンク先記事からコピーして、コンパイルし
MQL5 Source Fileを生成します。
今回も生成したMQL5 Source Fileをたたき台にしてこれからEAを作っていきます。テンプレートを上書きしないように
「ファイル」メニューから「名前を付けて保存」を選択し、新しく作るEAの名前を付けて別ファイルを作りましょう。
※今回ファイル名は「Counter-Trend System Example」としました。
※詳しく確認したい方は前回記事の「EA開発用テンプレートから新規ファイルを作成する」セクションをご覧ください。
ボリンジャーバンドの派生クラスを作成する
OriginalIndicators.mqhファイルに以下の記述を追加します。
// Bollinger Bands インジケータの派生クラス
class CDerivediBollinger : public CBaseIndicator
{
private:
double upper[], lower[]; // 上部バンドと下部バンドの値を格納する配列
public:
CDerivediBollinger(); // コンストラクタ
int Init(string parSymbol,ENUM_TIMEFRAMES parTimeframe,int parPeriod,int parShift,double parDeviation,ENUM_APPLIED_PRICE parPrice); // 初期化関数
double Upper(int parShift=0); // 上部バンドの値を取得する関数
double Lower(int parShift=0); // 下部バンドの値を取得する関数
};
派生クラス名は「CDerivediBollinger」としました。
CDerivediBollingerクラスは講座記事第109回で解説したCBaseIndicatorクラスが親クラスとなっています。
ボリンジャーバンドは上バンド、センターライン、下バンドという3本の表示ラインで構成されたマルチバッファインジケータです。
アクセスレベル=privateの領域に宣言した配列「upper」「 lower」 はそれぞれ上部バンドと下部バンドの値を格納する配列です。
・コンストラクタであるCDerivediBollinger関数
・上部バンドの値を取得するUpper関数
・下部バンドの値を取得するLower関数
という4つのメンバ関数を宣言しました。
コンストラクタ「CDerivediBollinger関数」の処理実装記述
CDerivediBollinger::CDerivediBollinger(void)
{
ArraySetAsSeries(upper,true); // upper配列を時系列として設定
ArraySetAsSeries(lower,true); // lower配列を時系列として設定
}
コンストラクタ「CDerivediBollinger関数」はArraySetAsSeries関数を使ってupper配列、lower配列を時系列セットする処理を担っています。
Init関数の処理実装記述
// 初期化関数
int CDerivediBollinger::Init(string parSymbol,ENUM_TIMEFRAMES parTimeframe,int parPeriod,int parShift,double parDeviation,ENUM_APPLIED_PRICE parPrice)
{
handle = iBands(parSymbol,parTimeframe,parPeriod,parShift,parDeviation,parPrice); // Bollinger Bandsインジケータのハンドルを取得
return(handle); // ハンドルを返す
}
Init関数はボリンジャーバンドのハンドル値を取得するメンバ関数です。
※親クラスであるCBaseIndicatorクラスにて仮想関数として定義されており、派生クラスのCDerivediBollingerクラスで具体的な処理を実装しています。
ハンドル値の取得にはiBands関数を使い、取得したハンドル値をメンバ変数「handle」に代入し、returnで返します。
※iBands関数についての詳細は↓の記事をご参照ください
※メンバ変数「handle」は親クラスのCBaseIndicatorクラスで作ったメンバです。
Upper関数の処理実装記述
double CDerivediBollinger::Upper(int parShift=0)
{
CopyBuffer(handle,1,0,MAX_COPY,upper); // 上部バンドの値をバッファからコピー
double value = NormalizeDouble(upper[parShift],_Digits); // 値を正規化
return(value); // 正規化された値を返す
}
Upper関数はボリンジャーバンド上部バンドの値を取得するメンバ関数です。
CopyBuffer関数を使って上部バンドの値を配列「upper」にコピーし、NormalizeDouble関数で正規化を行ったうえで値を返します。
Lower関数の処理実装記述
// 下部バンドの値を取得する関数
double CDerivediBollinger::Lower(int parShift=0)
{
CopyBuffer(handle,2,0,MAX_COPY,lower); // 下部バンドの値をバッファからコピー
double value = NormalizeDouble(lower[parShift],_Digits); // 値を正規化
return(value); // 正規化された値を返す
}
Lower関数はボリンジャーバンド下部バンドの値を取得するメンバ関数です。
CopyBuffer関数を使って下部バンドの値を配列「lower」にコピーし、NormalizeDouble関数で正規化を行ったうえで値を返します。
RSIの派生クラスを作成する
// RSI インジケータの派生クラス
class CDerivediRSI : public CBaseIndicator
{
public:
int Init(string parSymbol, ENUM_TIMEFRAMES parTimeframe, int parRSIPeriod, ENUM_APPLIED_PRICE parRSIPrice); // 初期化関数
};
// 初期化関数
int CDerivediRSI::Init(string parSymbol, ENUM_TIMEFRAMES parTimeframe, int parRSIPeriod, ENUM_APPLIED_PRICE parRSIPrice)
{
handle = iRSI(parSymbol,parTimeframe,parRSIPeriod,parRSIPrice); // RSIインジケータのハンドルを取得
return(handle); // ハンドルを返す
}
派生クラス名は「CDerivediRSI」としました。
CDerivediRSIクラスもCBaseIndicatorクラスが親クラスとなっています。RSIはボリンジャーバンドと違い、一本の表示ラインで構成されるシングルバッファインジケータです。
配列の時系列セットやRSIの値呼取得は親クラスであるCBaseIndicatorクラスのコンストラクタ「CBaseIndicator関数」やMain関数が行うので、CDerivediRSIクラスで新たに宣言するメンバ関数はRSIのハンドル値を取得するInit関数のみです。
※このあたりの理屈が良くわからない人は↓
今一度ご確認頂く事をお勧めします。
取得したハンドル値をメンバ変数「handle」に代入し、returnで返します。
※iRSI関数についての詳細は↓の記事をご参照ください
ボリンジャーバンドとRSIを扱う為のインスタンスを宣言する
OriginalIndicators.mqhファイルにCDerivediBollingerクラスとCDerivediRSIクラスを追加し終わったので、晴れてメインプログラム側の記述に取り掛かります。
作ったCDerivediBollingerクラスとCDerivediRSIクラスをデータ型として、インスタンスを2つ宣言します。
// インジケータ
#include <OriginalIndicators.mqh> // インジケータを扱うためのファイルをインクルード
CDerivediBollinger Bollinger;//ボリンジャーバンドを扱う為のインスタンス
CDerivediRSI RSI;//RSIを扱う為のインスタンス
ボリンジャーバンドを扱う為のインスタンス名を「Bollinger」、
ボリンジャーバンド、RSIのパラメータ設定用のinput変数を宣言する
続いてボリンジャーバンド、RSIのパラメータ設定用のinput変数を宣言します。今回も基本取引設定groupの下に記述を追加します。
input group "ボリンジャーバンドの設定"
input int BandsPeriod = 20; // ボリンジャーバンドの期間
input int BandsShift = 0; // ボリンジャーバンドのシフト
input double BandsDeviation = 2; // ボリンジャーバンドの偏差
input ENUM_APPLIED_PRICE BandsPrice = PRICE_CLOSE; // ボリンジャーバンドの価格適用
input group "RSIの設定"
input int RSIPeriod = 8; // RSIの期間
input int RSIUpperLevel=70; //RSIの上限レベル
input int RSILowerLevel=30; //RSIの下限レベル
input ENUM_APPLIED_PRICE RSIPrice = PRICE_CLOSE; // RSIの価格適用
ボリンジャーバンドについては、
・ボリンジャーバンドの期間
・ボリンジャーバンドのシフト値(チャートからどれくらいずらして表示させるか)
・ボリンジャーバンドで採用する標準偏差
・ボリンジャーバンドの計算に適用する価格
に関してのinput変数を宣言しました。
RSIについては
・RSIの期間
・RSIの上限レベル
・RSIの下限レベル
・RSIの計算に適用する価格
に関してのinput変数を宣言しました。
トレードシグナル用のEnum列挙型を作り、そのインスタンスを宣言する
続いてトレードシグナル用のenum列挙型を作り、そのインスタンスを宣言します。
記述箇所は、文法的にはグローバル領域であればどこでも良いのですが、グローバル変数「glBuyTicket」「 glSellTicket」を宣言した箇所の下におこないます。
// シグナルの列挙型
enum Signal
{
SIGNAL_BUY, // 買いシグナル
SIGNAL_SELL, // 売りシグナル
SIGNAL_NONE, // シグナルなし
};
Signal glSignal; // シグナル用のインスタンス
シグナル用のオリジナルenum列挙型名は「Signal」としました。
そして
買いシグナル用にSIGNAL_BUY、
売りシグナル用にSIGNAL_SELL、
売買シグナルを発していない時用にSIGNAL_NONE
という識別子を作っています。
そして「Signal」をデータ型とした変数「glSignal」も宣言しています。
「今回作るEAの仕様」セクションで説明した、「第1段階の買いエントリーシグナル」を満たしたときには「glSignal」にSIGNAL_BUYが代入されます。
逆に「第1段階の売りエントリーシグナル」を満たしたときはには「glSignal」にSIGNAL_SELLが代入されるようにメインプログラムで記述をしていきます。
OnInit関数にボリンジャーバンド及びRSIの設定に必要な記述をする
続いてOnInit関数にボリンジャーバンド及びRSIの設定に必要な記述をしていきます。
// ボリンジャーバンドの初期化
Bollinger.Init(_Symbol, _Period, BandsPeriod, BandsShift, BandsDeviation, BandsPrice);
// RSIの初期化
RSI.Init(_Symbol, _Period, RSIPeriod, RSIPrice);
CDerivediBollingerクラスのインスタンス「Bollinger」からInit関数を呼び出し、ボリンジャーバンドのハンドル値を取得します。
同様にCDerivediRSIクラスのインスタンス「RSI」からもInit関数を呼び出し、RSIのハンドル値を取得します。
※ハンドル値とはインジケータの情報にアクセスする為の鍵のようなものです。
それぞれのInit関数の引数には「ボリンジャーバンド、RSIのパラメータ設定用のinput変数を宣言する」セクションで宣言したinput変数を記述していきます。
これで、メイン処理を行うOnTick関数内でボリンジャーバンドとRSIを扱う準備が出来ました。
OnTick関数内に売買条件に関する記述を追加する
最後にOnTick関数内に売買条件に関する記述を追加していきます。
前回と同様に買いエントリーに関する箇所と、売りエントリーに関する箇所に記述を追加していくのですが、今回はエントリー条件が2段階形式になっているので、もう一つ「第1段階の売買シグナルに関する記述」を追加していきます。
OnTick関数内の追加記述1:第1段階の売買シグナルに関する記述
第1段階の売買シグナルに関する記述は
//新しいバーかつトレードタイマーが許可している時のみ注文処理を行う
if(newBar == true && timerOn == true)
{
↑のif文{}内、資金管理/ロット調整に関する記述の下あたりに追加します。
// ロット調整
double tradeSize;
//資金管理モードONなら設定したリスクに応じたロットを算出
if(UseMoneyManagement == true)
tradeSize = MoneyRiskManagement(_Symbol,FixedVolume,RiskPercent,StopLoss);
//固定ロットを指定
else
tradeSize = VerifyVolume(_Symbol,FixedVolume);
// トレードシグナルの確認
// ボリンジャーバンドの下部にあり、かつRSIが下限レベル以下であれば買いシグナル
if(Price.Close(barShift) < Bollinger.Lower(barShift) && RSI.Main(barShift) < RSILowerLevel)
glSignal = SIGNAL_BUY;
// ボリンジャーバンドの上部にあり、かつRSIが上限レベル以上であれば売りシグナル
else
if(Price.Close(barShift) > Bollinger.Upper(barShift) && RSI.Main(barShift) > RSIUpperLevel)
glSignal = SIGNAL_SELL;
第1段階の買いエントリーシグナルについて
「Price」インスタンスから呼び出しているClose関数はバーの終値情報を
「Bollinger」インスタンスから呼び出しているLower関数はボリンジャーバンドの下バンドラインの値を、
「RSI」インスタンスから呼び出しているMain関数はRSIの値情報をそれぞれ取得しています。
各関数の引数として記述されている変数「barShift」は、確定足モードをtrueにしている場合は「1」、falseにしている場合は「0」が入っています。
input変数「RSILowerLevel」はRSIの下限レベルをパラメータで設定した値が入っています
このif文を読み解くと
「終値がボリンジャーバンドの下部にあり、かつRSIが下限レベル以下であれば買いシグナル」となります。
if文の条件を満たしているのであれば、「トレードシグナル用のEnum列挙型を作り、そのインスタンスを宣言する」セクションで定義したenum列挙型の変数「glSignal」に識別子「SIGNAL_BUY」を代入します。
第1段階の売りエントリーシグナルについて
「Price」インスタンスから呼び出しているClose関数はバーの終値情報を、
「Bollinger」インスタンスから呼び出しているUpper関数はボリンジャーバンドの上バンドラインの値を、
「RSI」インスタンスから呼び出しているMain関数はRSIの値情報をそれぞれ取得しています。
各関数の引数として記述されている変数「barShift」は、確定足モードをtrueにしている場合は「1」、falseにしている場合は「0」が入っています。
input変数「RSIUpperLevel」はRSIの上限レベルをパラメータで設定した値が入っています。
このif文を読み解くと
「終値がボリンジャーバンドの上部にあり、かつRSIが上限レベル以上であれば売りシグナル」となります。
else if文の条件を満たしているのであれば、「トレードシグナル用のEnum列挙型を作り、そのインスタンスを宣言する」セクションで定義したenum列挙型の変数「glSignal」に識別子「SIGNAL_SELL」を代入します。
OnTick関数内の追加記述2:買いエントリー箇所
EA開発用テンプレートでは買い注文を出す際の記述は以下のようになっています。
if(Positions.GetBuyPosCount(MagicNumber) == 0)//買いポジションがなければ
{
//買い注文を出す
glBuyTicket = Trade.Buy(_Symbol,tradeSize);
この状態からEAの仕様に従った売買条件にするために、下記のように記述を追加します↓
// 買い注文について
// シグナルが買いである場合
if(glSignal == SIGNAL_BUY
&&
// 現在の価格がボリンジャーバンドの下部より上にある
Price.Close(barShift) > Bollinger.Lower(barShift)
&&
// 前のバーの価格がボリンジャーバンドの下部より下にある
Price.Close(barShift + 1) <= Bollinger.Lower(barShift + 1)
&&
Positions.GetBuyPosCount(MagicNumber) == 0
)// 買いポジションがなければ
glSignal == SIGNAL_BUY
の状態というのは「終値がボリンジャーバンドの下部にあり、かつRSIが下限レベル以下」という状態を過去のどこかで達成している事を意味します。
論理積&&で繋いだその下の記述
Price.Close(barShift) > Bollinger.Lower(barShift)は
「現在の価格がボリンジャーバンドの下部より上にある」事を意味します。
そして同様に論理積&&で繋いださらに下の記述
Price.Close(barShift + 1) <= Bollinger.Lower(barShift + 1)は
「1つ前のバーの価格がボリンジャーバンドの下部より下にある」事を意味します。
※「barShift + 1」という記述で1つ前のバーを表現しています。
これで、第1段階の買いエントリー条件で「相場の行き過ぎ感」を感知した上で、そろそろ反転の予兆が見えた事を2段階目に確認し、買いエントリーする、という戦略をコード記述に落とし込む事ができました。
今回も前回作ったEAと同様に買い発注する前に、もし売りポジションを保有していたらクローズ決済する、という仕様なので↓
// 売りポジションがあれば決済する
Trade.ClosePosition(glSellTicket);
// 売りポジション番号をリセット
glSellTicket = 0;
//買い注文を出す
glBuyTicket = Trade.Buy(_Symbol,tradeSize);
ClosePosition関数で売りポジションを決済~売りポジション番号をリセットしてから、Buy関数を使い買い注文を出します。
また、無事買い注文が約定し、SLTPの設定等まで行われた後には「glSignal」に代入されている買いシグナルのサインをリセットする為に、if(glBuyTicket > 0)のif文に対応する{}内の最下部に、以下の記述を入れておきます。
glSignal = SIGNAL_NONE; // シグナルをリセット
買いエントリーに追加する記述は以上になります。
OnTick関数内の追加記述3:売りエントリー箇所
続いて売りエントリーに関する箇所の追加です。
EA開発用テンプレートでは売り注文を出す際の記述は以下のようになっています。
// 売り注文について
if(Positions.GetSellPosCount(MagicNumber) == 0)// 売りポジションがなければ
{
//新規売り注文を出す
glSellTicket = Trade.Sell(_Symbol,tradeSize);
この状態からEAの仕様に従った売買条件にするために、下記のように記述を追加します↓
// 売り注文について
// シグナルが売りである場合
if(glSignal == SIGNAL_SELL
&&
// 現在の価格がボリンジャーバンドの上部より下にある
Price.Close(barShift) < Bollinger.Upper(barShift)
&&
// 前のバーの価格がボリンジャーバンドの上部より上にある
Price.Close(barShift + 1) >= Bollinger.Upper(barShift + 1)
&&
Positions.GetSellPosCount(MagicNumber) == 0)// 売りポジションがなければ
glSignal == SIGNAL_SELL
の状態というのは「終値がボリンジャーバンドの上部にあり、かつRSIが上限レベル以上」という状態を過去のどこかで達成している事を意味します。
論理積&&で繋いだその下の記述
Price.Close(barShift) < Bollinger.Upper(barShift)は
「現在の価格がボリンジャーバンドの上部バンドより下にある」事を意味します。
そして同様に論理積&&で繋いださらに下の記述
Price.Close(barShift + 1) >= Bollinger.Upper(barShift + 1)は
「1つ前のバーの価格がボリンジャーバンドの上部バンドより上にある」事を意味します。
これで、第1段階の買いエントリー条件で「相場の行き過ぎ感」を感知した上で、そろそろ反転の予兆が見えた事を2段階目に確認し、売りエントリーする、という戦略をコード記述に落とし込む事ができました。
前回作成のEAと同様に、売り注文を出す前に、もし買いポジションを保有していたらクローズ決済する仕様ですので↓
//買いポジションがあれば決済する
Trade.ClosePosition(glBuyTicket);
//買いポジション番号をリセット
glBuyTicket = 0;
//新規売り注文を出す
glSellTicket = Trade.Sell(_Symbol,tradeSize);
ClosePosition関数で買いポジションを決済~買いポジション番号をリセットしてから、Sell関数を使い売り注文を出します。
無事売り注文が約定し、SLTPの設定等まで行われた後には「glSignal」に代入されている売りシグナルのサインをリセットする為に、if(glSellTicket > 0)のif文に対応する{}内の最下部に、「glSignal」に格納されている値をリセットする記述を入れておきます。
glSignal = SIGNAL_NONE; // シグナルをリセット
売りエントリーに追加する記述は以上になります。
EA全体の記述も以上となります。
まとめ
今回も前回に引き続きEA開発用テンプレートを使った、EAの作成過程を解説しました。
今回は逆張りシステム系のEA作成を目標としていたのですが、エントリー条件が変わってもやる作業は殆ど変わりません。
・インジケータ(今回はボリンジャーバンドとRSI)用のインスタンスを宣言
・OnTick関数内に売買条件に関する記述と、反対ポジション決済用のClosePosition関数の追加
上記の作業は、前回もおこなったものです。
今回は、ボリンジャーバンドとRSIを扱う為の派生クラス化、という作業をおこないましたが、これも一度インクルードファイルに作ってしまえば再度作業する必要はありません。
また、トレードシグナル用のenum列挙型を作って、エントリー条件を2段階にする記述も今回紹介しましたが、これはエントリー条件を記述する上で便利な方法なので、是非今後も覚えておいていただければと思います。※詳細は当記事の「トレードシグナル用のEnum列挙型を作り、そのインスタンスを宣言する」セクションをご覧ください。
今回作ったEAの全体記述は以下の通りです。
//+------------------------------------------------------------------+
//| Counter-Trend System Example |
//| MQL5ssei |
//| https://mqlinvestmentlab.com/ |
//+------------------------------------------------------------------+
#property copyright "MQL5ssei"
#property link "https://mqlinvestmentlab.com/"
// 注文関連
#include <OriginalTrade.mqh> // 注文関連機能を提供するファイルをインクルード
OriginalCTrade Trade; // 取引のための主要インスタンス
OriginalCPositions Positions; // ポジション管理のためのインスタンス
// 価格情報
#include <OriginalPrice.mqh> // 価格データ取得のためのファイルをインクルード
OriginalCBars Price; // 価格情報を管理するインスタンス
// 資金管理
#include <OriginalMoneyManagement.mqh> // 資金管理機能を提供するファイルをインクルード
// トレーリングストップ
#include <OriginalTrailingStop.mqh> // トレーリングストップ機能を提供するファイルをインクルード
OriginalCTrailing Trailing; // トレーリングストップを管理するインスタンス
// インジケータ
#include <OriginalIndicators.mqh> // インジケータを扱うためのファイルをインクルード
CDerivediBollinger Bollinger;//ボリンジャーバンドを扱う為のインスタンス
CDerivediRSI RSI;//RSIを扱う為のインスタンス
// タイマー
#include <OriginalTimer.mqh> // タイマー機能を提供するファイルをインクルード
CTradeSession Session; // トレーディングセッションを管理するインスタンス
OriginalCNewBar NewBar; // 新しいバーが生成されたかを検出するインスタンス
//+------------------------------------------------------------------+
//| Input変数 |
//+------------------------------------------------------------------+
input group "基本取引設定" // 基本取引設定のグループ化
input ulong Slippage = 3; // スリッページ設定
input ulong MagicNumber = 123; // EA識別番号
input bool TradeOnNewBar = true; // 新しいバーでの取引を行うかどうか
input group "ボリンジャーバンドの設定"
input int BandsPeriod = 20; // ボリンジャーバンドの期間
input int BandsShift = 0; // ボリンジャーバンドのシフト
input double BandsDeviation = 2; // ボリンジャーバンドの偏差
input ENUM_APPLIED_PRICE BandsPrice = PRICE_CLOSE; // ボリンジャーバンドの価格適用
input group "RSIの設定"
input int RSIPeriod = 8; // RSIの期間
input int RSIUpperLevel=70; //RSIの上限レベル
input int RSILowerLevel=30; //RSIの下限レベル
input ENUM_APPLIED_PRICE RSIPrice = PRICE_CLOSE; // RSIの価格適用
input group "資金管理" // 資金管理設定のグループ化
input bool UseMoneyManagement = false; // マネーマネジメントを使用するかどうか
input double RiskPercent = 2; // リスク許容度(%)
input double FixedVolume = 0.1; // 固定取引量
input group "ストップロスとテイクプロフィット" // ストップロスとテイクプロフィット設定のグループ化
input int StopLoss = 0; // ストップロスの設定値
input int TakeProfit = 0; // テイクプロフィットの設定値
input group "トレーリングストップ" // トレーリングストップ設定のグループ化
input bool UseTrailingStop = false; // トレーリングストップを使用するかどうか
input int TrailingStop = 500; // トレーリングストップの距離
input int MinimumProfit = 200; // トレーリングを開始する最小利益
input int Step = 20; // トレーリングのステップ/
input group "ブレイクイーブン" // ブレイクイーブン設定のグループ化
input bool UseBreakEven = false; // ブレイクイーブンを使用するかどうか
input int BreakEvenProfit = 300; // ブレイクイーブンに設定する利益
input int LockProfit = 0; // 利益を確保するためのロック量
input group "トレードタイマー" // トレードタイマー設定のグループ化
input bool UseTimer = false; // トレードタイマーを使用するかどうか
input int StartHour = 0; // トレード開始時間(時)
input int StartMinute = 0; // トレード開始時間(分)
input int EndHour = 0; // トレード終了時間(時)
input int EndMinute = 0; // トレード終了時間(分)
input bool UseLocalTime = false; //ローカル時間を使うかどうか
//+------------------------------------------------------------------+
//| グローバル変数 |
//+------------------------------------------------------------------+
ulong glBuyTicket, glSellTicket;//ポジション番号を格納する変数
//+------------------------------------------------------------------+
// シグナルの列挙型 |
//+------------------------------------------------------------------+
enum Signal
{
SIGNAL_BUY, // 買いシグナル
SIGNAL_SELL, // 売りシグナル
SIGNAL_NONE, // シグナルなし
};
Signal glSignal; // グローバルシグナル変数
//+------------------------------------------------------------------+
//| OnInit関数 |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int OnInit()
{
// フィルポリシーを取得する
ENUM_ORDER_TYPE_FILLING filltype = FillPolicy();
// EAのマジックナンバーを設定する
Trade.SetMagicNumber(MagicNumber);
// 注文実行時の許容スリッページを設定する
Trade.SetDeviation(Slippage);
// 取得したフィルポリシーを設定する
Trade.SetFillType(filltype);
// ボリンジャーバンドの初期化
Bollinger.Init(_Symbol, _Period, BandsPeriod, BandsShift, BandsDeviation, BandsPrice);
// RSIの初期化
RSI.Init(_Symbol, _Period, RSIPeriod, RSIPrice);
// 初期化が完了したことを示す(0 = 成功)
return(0);
}
//+------------------------------------------------------------------+
//| OnTick関数 |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void OnTick()
{
// 新しいバーがあるかどうかの初期状態をtrueと仮定し、バーシフトを0に設定
bool newBar = true;
int barShift = 0;
// 新しいバーでの取引を行う場合の処理
if(TradeOnNewBar == true)
{
// 現在のシンボルと期間で新しいバーが始まったかどうかをチェック
newBar = NewBar.CheckNewBar(_Symbol,_Period);
// 新しいバーが確認された場合、バーシフトを1に設定
barShift = 1;
}
// トレードタイマーの設定
bool timerOn = true;
// トレードタイマーを使用する場合の処理
if(UseTimer == true)
{
// 指定された時間内で取引が許可されているかどうかをチェック
timerOn = Session.DailySessionTimer(StartHour, StartMinute, EndHour, EndMinute, UseLocalTime);
}
//価格情報をアップデートする
Price.Update(_Symbol,_Period);
// 注文関連の記述
// 新しいバーかつトレードタイマーが許可している時のみ注文処理を行う
if(newBar == true && timerOn == true)
{
// ロット調整
double tradeSize;
//資金管理モードONなら設定したリスクに応じたロットを算出
if(UseMoneyManagement == true)
tradeSize = MoneyRiskManagement(_Symbol,FixedVolume,RiskPercent,StopLoss);
//固定ロットを指定
else
tradeSize = VerifyVolume(_Symbol,FixedVolume);
// トレードシグナルの確認
// ボリンジャーバンドの下部にあり、かつRSIが下限レベル以下であれば買いシグナル
if(Price.Close(barShift) < Bollinger.Lower(barShift) && RSI.Main(barShift) < RSILowerLevel)
glSignal = SIGNAL_BUY;
// ボリンジャーバンドの上部にあり、かつRSIが上限レベル以上であれば売りシグナル
else
if(Price.Close(barShift) > Bollinger.Upper(barShift) && RSI.Main(barShift) > RSIUpperLevel)
glSignal = SIGNAL_SELL;
// 買い注文について
// シグナルが買いである場合
if(glSignal == SIGNAL_BUY
&&
// 現在の価格がボリンジャーバンドの下部より上にある
Price.Close(barShift) > Bollinger.Lower(barShift)
&&
// 前のバーの価格がボリンジャーバンドの下部より下にある
Price.Close(barShift + 1) <= Bollinger.Lower(barShift + 1)
&&
Positions.GetBuyPosCount(MagicNumber) == 0
)// 買いポジションがなければ
{
// 売りポジションがあれば決済する
Trade.ClosePosition(glSellTicket);
// 売りポジション番号をリセット
glSellTicket = 0;
//買い注文を出す
glBuyTicket = Trade.Buy(_Symbol,tradeSize);
if(glBuyTicket > 0)
{
// 開いたポジションの価格を取得
double openPrice = PositionOpenPrice(glBuyTicket);
double buyStop = CalBuySL(_Symbol, StopLoss, openPrice); // ストップロス計算
if(buyStop > 0)
buyStop =ModifyLowerStopLevel(_Symbol, buyStop); // ストップロスの設定が有効かチェック
double buyProfit = CalBuyTP(_Symbol, TakeProfit, openPrice); // テイクプロフィット計算
if(buyProfit > 0)
buyProfit =ModifyUpperStopLevel(_Symbol, buyProfit); // テイクプロフィットの設定が有効かチェック
// ストップロスとテイクプロフィットを設定
if(buyStop > 0 || buyProfit > 0)
Trade.SetSLTP(glBuyTicket, buyStop, buyProfit);
glSignal = SIGNAL_NONE; // シグナルをリセット
}
}//買い注文記述の終わり
// 売り注文について
// シグナルが売りである場合
if(glSignal == SIGNAL_SELL
&&
// 現在の価格がボリンジャーバンドの上部より下にある
Price.Close(barShift) < Bollinger.Upper(barShift)
&&
// 前のバーの価格がボリンジャーバンドの上部より上にある
Price.Close(barShift + 1) >= Bollinger.Upper(barShift + 1)
&&
Positions.GetSellPosCount(MagicNumber) == 0)// 売りポジションがなければ
{
//買いポジションがあれば決済する
Trade.ClosePosition(glBuyTicket);
//買いポジション番号をリセット
glBuyTicket = 0;
//新規売り注文を出す
glSellTicket = Trade.Sell(_Symbol,tradeSize);
if(glSellTicket > 0)
{
// 開いたポジションの価格を取得
double openPrice = PositionOpenPrice(glSellTicket);
double sellStop = CalSellSL(_Symbol,StopLoss,openPrice);// ストップロス計算
if(sellStop > 0)
sellStop = ModifyUpperStopLevel(_Symbol,sellStop); // ストップロスの設定が有効かチェック
double sellProfit = CalSellTP(_Symbol,TakeProfit,openPrice);// テイクプロフィット計算
if(sellProfit > 0)
sellProfit = ModifyLowerStopLevel(_Symbol,sellProfit); // テイクプロフィットの設定が有効かチェック
// ストップロスとテイクプロフィットを設定
if(sellStop > 0 || sellProfit > 0)
Trade.SetSLTP(glSellTicket,sellStop,sellProfit);
glBuyTicket = 0;//買いチケットリセット
glSignal = SIGNAL_NONE; // シグナルをリセット
}
}//売り注文の記述終わり
}// 注文関連の記述終わり
// ポジション番号を取得
ulong tickets[]; // チケット番号を格納するための配列
Positions.GetTotalPosNum(MagicNumber, tickets); // 全ポジションのポジション番号を取得
int numTickets = ArraySize(tickets); // 取得したポジションの数を格納
// トレーリングストップ処理
if(UseTrailingStop == true && numTickets > 0) // トレーリングストップが有効かつオープンポジションが存在する場合
{
for(int i = 0; i < numTickets; i++) // オープンポジションの数だけループ
{
// 各ポジションに対してトレーリングストップ処理を適用
Trailing.TrailingStop(tickets[i], TrailingStop, MinimumProfit, Step);
}
}//トレーリングストップ記述の終わり
// ブレイクイーブン処理
if(UseBreakEven == true && numTickets > 0) // ブレイクイーブンが有効かつオープンポジションが存在する場合
{
for(int i = 0; i < numTickets; i++) // オープンポジションの数だけループ
{
// 各ポジションに対してブレイクイーブン処理を適用
Trailing.BreakEven(tickets[i], BreakEvenProfit, LockProfit);
}
} // ブレイクイーブン処理の終わり
}//全体記述の終わり
//+------------------------------------------------------------------+
今回は以上となります。
最後までお読みいただきありがとうございました。