前回は とりあえず簡単な仕組みのEA を最後まで完成させました。↓
作ったMT5用EAのコード記述は、多くのブローカーが採用しているヘッジングシステム口座で運用する事を前提としています。(2024年2月現在、少なくとも国内MT5提供業者は全てヘッジングシステム採用です)
従って、ネッティングシステムを想定した記述は必要ないかな?とも思ったのですが、海外ブローカーの中には一定数ネッティングシステムを採用している所があるのと、現在のMQL5の文法が、ネッティングシステムとヘッジングシステムが混在する事を前提としていることもあり、一応少しは触れておいた方が良いな、と思いなおしました。(例えば、ネッティングシステムでしか使わない関数をヘッジングシステムで使うと、誤動作の元になります)
そんな訳で今回は、「前回作ったEAをネッティングシステム用に記述するならどうなるのか?」というのを、さらっと紹介したいなと思います。
※ネッティングシステムとヘッジングシステムについては↓
MQL5 EA講座 第57回「ネッティングとヘッジング」 をご覧ください。
ネッティングシステムにおけるシンプルEAの記述
ポジションの選択にはPositionSelect関数を使う
//現在のポジション情報を取得する。
bool openPosition = PositionSelect(_Symbol);
ネッティングシステムの場合、ポジション保有が1銘柄につき1つのみ、という前提があるので、for文を使ってポジション数をチェックしたり、ポジション番号を使ってポジションを指定する、という作業が必要ありません。
ポジションの選択にはPositionSelect関数を使います。()内の引数に銘柄名を記述すればポジション選択は完了です。
※公式リファレンスの説明によると↓
ポジションに制限がない場合(ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)各シンボルごとに同時に複数のポジションを保有することができます。この場合、PositionSelectは最小チケットのポジションを選択します。
MQL5公式リファレンスより
と、あります。ヘッジングシステム口座で、複数ポジションを保有するような戦略のMT5用EAを作るときは、PositionSelect関数を使うとうまく動作しないことを意味します。
前回紹介したヘッジングシステム版EAのサンプルコードのようにPositionSelectByTicket関数を使ったポジション選択をする必要があるでしょう。
ポジションタイプの特定にはPositionGetIntegerを使う
PositionSelect関数でポジション選択が完了すると、PositionGetInteger、PositionGetDouble 、PositionGetStringなどの関数を使って、ポジション情報を取得できるようになります。
ポジションタイプの特定にはPositionGetInteger関数の引数に定数値「POSITION_TYPE」を記述します。
long positionType = PositionGetInteger(POSITION_TYPE);
現在のポジションのロット数を格納するローカル変数を宣言する
続いて現在のポジションのロット数を格納するdouble型のローカル変数「nowVolume」を宣言します。
この変数は、ポジションを持っていないときは0、ポジションを持っている時はPositionGetDouble関数の引数に定数値「POSITION_VOLUME」を記述して取得した値が格納されるようになっています。
//ロット数を格納する変数を宣言
double nowVolume = 0;
if(openPosition == true) nowVolume = PositionGetDouble(POSITION_VOLUME);
これにより、ヘッジングシステムのように反対ポジションを決済する時に、OrderSend関数を使い、新規ポジションを注文する時にもう一度OrderSend関数を使う、といったような記述をする事なく、売買条件を満たしたときに、1回のOrderSend関数で反対売買が行えるようになります。
次は新規買い注文のコード記述を見ていきます。
新規買い注文のコード記述
// 新規買い注文を行う
if(close > sma[0] && BuyPosition== false && (positionType != POSITION_TYPE_BUY || openPosition == false))
{
request.action = TRADE_ACTION_DEAL;
request.type = ORDER_TYPE_BUY;
request.symbol = _Symbol;
request.volume = Volume + nowVolume;
request.price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
request.sl = 0;
request.tp = 0;
request.deviation = Deviation;
request.type_filling=FillPolicy();
bool sent = OrderSend(request,result);
前回の記事で作った、ヘッジングシステム想定のEA記述とは違う部分はif文の条件内容と、request.Volumeの部分になります。
if(close > sma[0] && BuyPosition== false && (positionType != POSITION_TYPE_BUY || openPosition == false))
close > sma[0] && BuyPosition== false という部分は前回作ったEAの記述と同じです。
和訳すると
(現在値が移動平均線より上で、直近に保有していたポジションが買いポジションじゃない時)
となります。
その後の、論理積&&を挟んで続く↓
(positionType != POSITION_TYPE_BUY || openPosition == false)
の記述が少々複雑ですが、これも和訳すると
(「保有しているポジションが買いポジションではない」 または 「ポジションを持っていない」時)
となります。
第33回「AND条件、OR条件の記述(論理積・論理和)」をご覧ください。
request.volume = Volume+ nowVolume ;
繰り返しになりますが、nowVolumeにはノーポジションの時は0、反対ポジションである売りポジションを保有している時は売りポジションのロット数が値として.Volumeに格納されています。従って、
ノーポジションの時はinput変数「Volume」に格納されているロット数+「0」で新規発注され、
反対ポジションである売りポジションを保有している時はinput変数「Volume」+売りポジションのロット数が格納されている「nowVolume」の値が.Volumeに格納されます。「nowVolume」のロット数は売りポジションの決済に使われ、input変数「Volume」のロット数が新規買い注文に使われるイメージです。
他の部分のメンバ変数や、最後のOrderSend関数で注文を出す流れは前回の記事の記述と同じです。
注文が無事約定したら、ストップロスとテイクプロフィットの設定記述に移ります。
約定後にSLとTPの設定をする(買い)
注文が約定したら、ストップロスとテイクプロフィットを設定していきます。↓
// ストップロスとテイクプロフィットを設定する
if(result.retcode == TRADE_RETCODE_PLACED || result.retcode == TRADE_RETCODE_DONE)
{
request.action = TRADE_ACTION_SLTP;
request.position = result.order;
PositionSelect(_Symbol);
double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);
if(SL > 0)
request.sl = positionOpenPrice - (SL * _Point);
if(TP > 0)
request.tp = positionOpenPrice + (TP * _Point);
if(request.sl > 0 || request.tp > 0)
sent = OrderSend(request,result);
BuyPosition = true;
SellPosition = false;
}
ストップロスとテイクプロフィットを設定記述についてはヘッジングシステム版とほとんど同じなのですが、約定価格を取得する記述と、OrderSend関数によるSLTP修正送信後の記述が少し異なっています。
PositionSelect(_Symbol);
double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);
()内の引数に銘柄名を記述し、PositionSelect関数でポジションを選択した上で、PositionGetDouble関数の引数にPOSITION_PRICE_OPENを記述して約定価格を取得します。
BuyPosition = true;
SellPosition = false;
前回のヘッジングシステム版の時は、SellPosition = falseだけでしたが、今回のネッティングシステム版では
BuyPosition = true;
SellPosition = false;
となっています。
ネッティングシステムにおいては常に1銘柄1ポジションなので、前回のヘッジングシステム版のようにfor文を使ったポジション数のチェックなどを行っていません(ヘッジングシステム版の時は、そこで買いポジションがあればBuyPosition = trueとなる記述をしていました)。
従って、ネッティングシステムではストップロスとテイクプロフィットの設定注文が終わった段階で、BuyPositionとSellPosition の両方の変数へ代入を行います。
これで、買い注文に関する記述は終了です。後は売り注文に関する記述を、細かい部分逆にしていくだけです。
新規売り注文のコード記述
// 新規売り注文を行う
if(close <sma[0] && SellPosition== false && (positionType != POSITION_TYPE_SELL || openPosition == false))
{
request.action = TRADE_ACTION_DEAL;
request.type = ORDER_TYPE_SELL;
request.symbol = _Symbol;
request.volume = Volume + nowVolume;
request.price = SymbolInfoDouble(_Symbol,SYMBOL_BID);
request.sl = 0;
request.tp = 0;
request.deviation = Deviation;
request.type_filling=FillPolicy();
bool sent = OrderSend(request,result);
if文の条件文↓
if(close <sma[0] && SellPosition== false && (positionType != POSITION_TYPE_SELL || openPosition == false))
を和訳すると、
「(現在値が移動平均線より下の位置で、直近保有ポジションが売りポジションではなかった)
かつ
(保有ポジションが売りポジションはない または ノーポジションである)」
となります。
request.volume = Volume + nowVolume;
の箇所の理屈については、新規買い注文のコード記述で解説した通りです。
注文が無事約定したら、ストップロスとテイクプロフィットの設定記述に移ります。
約定後にSLとTPの設定をする(売り)
注文が約定したら、ストップロスとテイクプロフィットを設定していきます。↓
// ストップロスとテイクプロフィットを設定する
if(result.retcode == TRADE_RETCODE_PLACED || result.retcode == TRADE_RETCODE_DONE)
{
request.action = TRADE_ACTION_SLTP;
PositionSelect(_Symbol);
double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);
if(SL > 0)
request.sl = positionOpenPrice + (SL * _Point);
if(TP > 0)
request.tp = positionOpenPrice - (TP * _Point);
if(request.sl > 0 || request.tp > 0)
sent = OrderSend(request,result);
BuyPosition = false;
SellPosition = true;
}
約定後にSLとTPの設定をする(買い) の所で説明したように、ネッティングシステム版ではストップロスとテイクプロフィットの設定注文が終わった段階で、BuyPositionとSellPosition の値代入記述を行います。↓
BuyPosition = false;
SellPosition = true;
買いの時とは逆に、BuyPosition が falseに、SellPosition は trueとなります。
これで売りの注文に関する記述も完成です。
完成したコード全体の記述内容
完成したコード全体の記述内容は以下の通りです↓
//+------------------------------------------------------------------+
//| test3EA.mq5 |
//| MQL5ssei |
//| https://mqlinvestmentlab.com/ |
//+------------------------------------------------------------------+
#property copyright "MQL5ssei"
#property link "https://mqlinvestmentlab.com/"
#property version "1.00"
//必要なパラメータ
input double Volume=0.1;//ロット数
input int SL=200;//ストップロス
input int TP=200;//テイクプロフィット
input int MAPeriod = 20;//移動平均期間
input int Deviation=50;//スリッページ
//直近の注文状況をチェックするグローバル変数
bool BuyPosition, SellPosition;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
ENUM_ACCOUNT_TRADE_MODE tradeMode=(ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE);
if(tradeMode != ACCOUNT_TRADE_MODE_DEMO) // デモ口座以外の場合
{
MessageBox("このEAはデモ口座でのみ稼働します","口座エラー");
return INIT_FAILED; // 処理終了
}
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//インスタンスの生成
MqlTradeRequest request;
MqlTradeResult result;
//構造体の初期化
ZeroMemory(request);
// ZeroMemory(result);
//フィルポリシーを設定する。
request.type_filling=FillPolicy();
// 単純移動平均の値取得
//単純移動平均の値を格納する配列宣言
double sma[];
//配列を時系列にセット
ArraySetAsSeries(sma,true);
//単純移動平均のハンドルを取得
int smaHandle=iMA(_Symbol,0,MAPeriod,MODE_SMA,0,PRICE_CLOSE);
///単純移動平均の値を配列にコピー
CopyBuffer(smaHandle,0,0,1,sma);
//終値情報を変数に格納
double close=iClose(NULL,0,1);
//現在のポジション情報を取得する。
bool openPosition = PositionSelect(_Symbol);
//現在のポジション情報を取得する。
long positionType = PositionGetInteger(POSITION_TYPE);
//ロット数を格納する変数を宣言
double nowVolume = 0;
if(openPosition == true) nowVolume = PositionGetDouble(POSITION_VOLUME);
// 新規買い注文を行う
if(close > sma[0] && BuyPosition== false && (positionType != POSITION_TYPE_BUY || openPosition == false))
{
request.action = TRADE_ACTION_DEAL;
request.type = ORDER_TYPE_BUY;
request.symbol = _Symbol;
request.volume = Volume + nowVolume;
request.price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
request.sl = 0;
request.tp = 0;
request.deviation = Deviation;
//request.type_filling=FillPolicy();
bool sent = OrderSend(request,result);
// ストップロスとテイクプロフィットを設定する
if(result.retcode == TRADE_RETCODE_PLACED || result.retcode == TRADE_RETCODE_DONE)
{
request.action = TRADE_ACTION_SLTP;
PositionSelect(_Symbol);
double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);
if(SL > 0)
request.sl = positionOpenPrice - (SL * _Point);
if(TP > 0)
request.tp = positionOpenPrice + (TP * _Point);
if(request.sl > 0 && request.tp > 0)
sent = OrderSend(request,result);
BuyPosition = true;
SellPosition = false;
}
}//if(close[0] > sma[0] && BuyPosition== false
// 新規売り注文を行う
else if(close <sma[0] && SellPosition== false && (positionType != POSITION_TYPE_SELL || openPosition == false))
{
request.action = TRADE_ACTION_DEAL;
request.type = ORDER_TYPE_SELL;
request.symbol = _Symbol;
request.volume = Volume + nowVolume;
request.price = SymbolInfoDouble(_Symbol,SYMBOL_BID);
request.sl = 0;
request.tp = 0;
request.deviation = Deviation;
//request.type_filling=FillPolicy();
bool sent = OrderSend(request,result);
// ストップロスとテイクプロフィットを設定する
if(result.retcode == TRADE_RETCODE_PLACED || result.retcode == TRADE_RETCODE_DONE)
{
request.action = TRADE_ACTION_SLTP;
PositionSelect(_Symbol);
double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);
if(SL > 0)
request.sl = positionOpenPrice + (SL * _Point);
if(TP > 0)
request.tp = positionOpenPrice - (TP * _Point);
if(request.sl > 0 || request.tp > 0)
sent = OrderSend(request,result);
BuyPosition = false;
SellPosition = true;
}//if(result.retcode == TRADE_RETCODE_PLACED || result.retcode == TRADE_RETCODE_DONE)
}// if(close <ma[0] && SellPosition== false
}//OnTick
//+------------------------------------------------------------------+
//フィルポリシーを返す関数
ENUM_ORDER_TYPE_FILLING FillPolicy()
{
long fillType = SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
if(fillType==SYMBOL_FILLING_IOC)return ORDER_FILLING_IOC;
else if(fillType==SYMBOL_FILLING_FOK)return ORDER_FILLING_FOK;
else return ORDER_FILLING_RETURN;
}
サンプルEAの挙動は以下のようになります↓
今回は以上になります。今回で中級編は最終回になります。
次回より、応用編に入ります。クラスを使って、EAを作る工程を簡略化する方法を学んでいきます。
最後までお読みいただきありがとうございました<m(__)m>
【超入門】MQL5 EA講座 第69回「簡単な仕組みのMT5用EAを作るーその2ー」【EAの作り方】←
→【超入門】MQL5 EA講座 第71回「トレード用のオリジナルクラスを作る」
-<PR>-
MQL5を使って自作したEAをシステムトレードに利用するには、取引プラットフォームとしてMT5を提供しているFX会社に口座を開設しなくてはいけません。
当サイトでは以下のFX会社での口座開設・EA運用をおススメしています。
おススメする理由の詳細につきましては、各FX会社について解説する記事を書いておりますので、下記のリンク記事を参考にしていただければと思います。
※外為ファイネストに関する記事は↓をご覧ください。
※アヴァトレードジャパンに関する記事は↓をご覧ください。
※フィリップ証券に関する記事は↓をご覧ください。
コメント