はじめに
ここでは、GUIでストップ狩りを回避するラインを実装します。
GitHubにソースを公開しました。細かい修正は、こちらをご覧下さい。
GitHub : KEG_Qiita_EA
GitHub : KEG_Qiita_EA 差分
前回までの記事を理解しているのが前提です。
【MQL4 : MT4】GUI で ストップ狩りを回避するライン を実装した EA を作る。②
作成手順としては、以下の通りです。
- ComboBoxにライン作成Listを追加
- 2本のライン作成処理
GUIを拡張(横スクロールできる予定)- ライン情報( pips, 損益,
RR)を表示 - 仕掛けに対して、ラインで決済できる処理
- 細かい修正etc...
今回は、5. 仕掛けに対して、ラインで決済できる処理 を実装します。
※ネーミングセンスはありません。なるだけ分かりやすくしているつもりです!
いろいろと初心者な為、至らない部分もありますが、宜しくお願いします。
フォルダ構造のアドバイスなどを頂けると助かります。
- Experts/
- Sample/
- Common/
- CMD.mqh
- Common.mqh
- Plugin/
- ExitCurrency.mqh
- LineOrder.mqh ←今回のメインファイルです。
- guiwindow.mq4
- AppWindow.mqh
- Event.mqh
- Common/
- Sample/
実行結果
ラインを作り、Buyボタンを押すと、同じ場所に決済用のラインが作られます。
今回は、移動して確認します。
現在のプライスと比較して、条件があったら決済します。
仕掛けに対して、ラインで決済できる処理
LineOrder.mqh
#include "../Common/Common.mqh"
class CLineOrder
{
public:
bool check();
bool GetLine();
bool create( string str_name, double d_param );
bool reCreate( string str_name, int i_type );
bool exitCheck();
bool exit( int i_ticket );
};
bool CLineOrder::check() // --- 1
{
if( ObjectFind( ChartID(), "LineOrder0" ) != -1 )
b_check0 = true;
else
b_check0 = false;
if( ObjectFind( ChartID(), "LineOrder1" ) != -1 )
b_check1 = true;
else
b_check1 = false;
return true;
}
~
中略
~
bool CLineOrder::reCreate( string str_name, int i_type ) // --- 2
{
double d_line0 = ObjectGet( str_name, OBJPROP_PRICE1 );
ObjectDelete( ChartID(), str_name );
string str_type;
if( i_type == 0 )
{
if( d_line0 > Close[0] )
str_type = "LongProfit";
else
str_type = "LongLoss";
}else{
if( d_line0 < Close[0] )
str_type = "SellProfit";
else
str_type = "SellLoss";
}
if( OrderSelect( OrdersTotal() - 1, SELECT_BY_POS, MODE_TRADES ) )
{
string str_exName = "KEG_" + Symbol() + "_" + (string)OrderTicket() + "_" + str_type;
if( !ObjectCreate( ChartID(), str_exName, OBJ_HLINE, 0, 0, d_line0 ) ) return false;
}else{
return false;
}
return true;
}
bool CLineOrder::exitCheck() // --- 3
{
string objGet[];
if( ObjectsTotal() >= 1 )
{
for( int i = 0; i < ObjectsTotal(); i++ )
{
if( StringSplit( ObjectName( i ), '_', objGet ) != 0 && objGet[0] == "KEG" )
{
double objPrice = NormalizeDouble( ObjectGet( ObjectName( i ), OBJPROP_PRICE1 ), Digits() );
if( ( objGet[3] == "LongLoss" && objPrice >= Close[ 0 ] ) || ( objGet[3] == "LongProfit" && objPrice <= Close[ 0 ] ) ||
( objGet[3] == "SellLoss" && objPrice <= Close[ 0 ] ) || ( objGet[3] == "SellProfit" && objPrice >= Close[ 0 ] ) )
{
if( !exit( (int)objGet[2] ) ) return false;
ObjectsDeleteAll( ChartID(), objGet[0] + "_" + objGet[1] + "_" + objGet[2] );
}
}
}
}
return true;
}
bool CLineOrder::exit( int i_ticket ) // --- 4
{
for( int i = OrdersTotal() - 1; i >= 0; i-- )
{
if( !OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) ) continue;
if( OrderSymbol() != Symbol() ) continue;
if( OrderTicket() != i_ticket ) continue;
if( !OrderClose( OrderTicket(), OrderLots(), OrderClosePrice(), 0 ) ) return false;
}
Sleep( 500 );
return true;
}
LineOrder.mqh の ソースコード詳細
1. check関数
bool CLineOrder::check()
{
if( ObjectFind( ChartID(), "LineOrder0" ) != -1 )
b_check0 = true;
else
b_check0 = false;
if( ObjectFind( ChartID(), "LineOrder1" ) != -1 )
b_check1 = true;
else
b_check1 = false;
return true;
}
Pipsなどを取得するラインが存在するかのチェック関数です。
if( ObjectFind( ChartID(), "LineOrder0" ) != -1 )
ラインの名前は、LineOrder0 と固定しているので、名前で検索します。
戻り値は、見つかったら、チャートID
見つからなかったら、負数 ( -1 )
b_check0 = true;
bool型で、ラインの有無を確認します。
2. reCreate関数
bool CLineOrder::reCreate( string str_name, int i_type )
{
double d_line0 = ObjectGet( str_name, OBJPROP_PRICE1 );
ObjectDelete( ChartID(), str_name );
string str_type;
if( i_type == 0 )
{
if( d_line0 > Close[0] )
str_type = "LongProfit";
else
str_type = "LongLoss";
}else{
if( d_line0 < Close[0] )
str_type = "SellProfit";
else
str_type = "SellLoss";
}
if( OrderSelect( OrdersTotal() - 1, SELECT_BY_POS, MODE_TRADES ) )
{
string str_exName = "KEG_" + Symbol() + "_" + (string)OrderTicket() + "_" + str_type;
if( !ObjectCreate( ChartID(), str_exName, OBJ_HLINE, 0, 0, d_line0 ) ) return false;
}else{
return false;
}
return true;
}
ラインの有無をチェックし、Buyボタンか、Sellボタンを押したら、
決済用のラインを作ります
if( i_type == 0 )
0 = Buy
且つ
if( d_line0 > Close[0] )
現在の価格よりも、LineOrder0 が 上にあったら、
LongProfitと入れときます。
if( OrderSelect( OrdersTotal() - 1, SELECT_BY_POS, MODE_TRADES ) )
OrderSelectで最新の注文の情報を取得します。
string str_exName = "KEG_" + Symbol() + "_" + (string)OrderTicket() + "_" + str_type;
決済ラインの名前を、KEG_USDJPY_59486548_LongProfitにします。
アンダーバーで、区切っていて、決済の判断に使います。
※KEG_という名前に意味はありません。
if( !ObjectCreate( ChartID(), str_exName, OBJ_HLINE, 0, 0, d_line0 ) ) return false;
先ほどの名前と、プライスで ラインを作ります。
3. exitCheck関数
bool CLineOrder::exitCheck() // --- 3
{
string objGet[];
if( ObjectsTotal() >= 1 )
{
for( int i = 0; i < ObjectsTotal(); i++ )
{
if( StringSplit( ObjectName( i ), '_', objGet ) != 0 && objGet[0] == "KEG" )
{
double objPrice = NormalizeDouble( ObjectGet( ObjectName( i ), OBJPROP_PRICE1 ), Digits() );
if( ( objGet[3] == "LongLoss" && objPrice >= Close[ 0 ] ) || ( objGet[3] == "LongProfit" && objPrice <= Close[ 0 ] ) ||
( objGet[3] == "SellLoss" && objPrice <= Close[ 0 ] ) || ( objGet[3] == "SellProfit" && objPrice >= Close[ 0 ] ) )
{
if( !exit( (int)objGet[2] ) ) return false;
ObjectsDeleteAll( ChartID(), objGet[0] + "_" + objGet[1] + "_" + objGet[2] );
}
}
}
}
return true;
}
いよいよ決済処理です。
全体的な流れとしては、
- オブジェクトを全検索
- オブジェクトの名前を、アンダーバーで分割
- KEGという名前があるなら、そのラインの価格を取得
- 例:買いのストップロスが現在の価格より上にいったら
- 決済処理の関数に、価格を渡す
- 決済が完了したら、ライン削除
for( int i = 0; i < ObjectsTotal(); i++ )
オブジェクトの数だけ回します。
if( StringSplit( ObjectName( i ), '_', objGet ) != 0 && objGet[0] == "KEG" )
StringSplitで、 '_'で分割し、objGetの配列に入れます。
例:KEG_USDJPY_59486548_LongProfit
objGet[0] = KEG;
objGet[1] = USDJPY; ←通貨ペア
objGet[2] = 59486548; ←チケットナンバー
objGet[3] = LongProfit; ←ラインのタイプ
そして、objGet[0]に KEG が入っていたら、以下に進みます。
double objPrice = NormalizeDouble( ObjectGet( ObjectName( i ), OBJPROP_PRICE1 ), Digits() );
ObjectNameで、ラインの名前を返してくれるので、
ObjectGetでラインの価格を取得してます。
if( ( objGet[3] == "LongLoss" && objPrice >= Close[ 0 ] ) || ...
objGet[3] が LongLoss 且つ ラインのプライスが、現在プライスよりも、大きかったら以下の決済処理に。
if( !exit( (int)objGet[2] ) ) return false;
exit関数にチケットナンバーを渡して、検索数を削減します。
もし、決済に失敗したら、return false; で戻され、エラーが表示されます。
ObjectsDeleteAll( ChartID(), objGet[0] + "_" + objGet[1] + "_" + objGet[2] );
決済に成功したら、接頭詞で、オブジェクトを削除します。
4. exit関数
bool CLineOrder::exit( int i_ticket )
{
for( int i = OrdersTotal() - 1; i >= 0; i-- )
{
if( !OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) ) continue;
if( OrderSymbol() != Symbol() ) continue;
if( OrderTicket() != i_ticket ) continue;
if( !OrderClose( OrderTicket(), OrderLots(), OrderClosePrice(), 0 ) ) return false;
}
Sleep( 500 );
return true;
}
exitCheck関数から、チケットナンバーを受け取ったので、チケットナンバーが同じ注文を決済します。
if( OrderSymbol() != Symbol() ) continue;
第一の門
通貨ペアが同じかどうか。
if( OrderTicket() != i_ticket ) continue;
第二の門
Select中のチケットナンバーと決済するチケットナンバーが同じかどうか。
if( !OrderClose( OrderTicket(), OrderLots(), OrderClosePrice(), 0 ) ) return false;
ここまで通貨した注文は、決済するよ。。。
Sleep( 500 );
エラー防止のSleep 500msec
Event.mqh
~
略
~
bool CEvent::Order( int i_type, double d_lots ) // --- 5
{
LineOrder.check();
if( OrderSend( Symbol(), i_type, d_lots, Close[0], 0, 0, 0, "GUISample", 999, 0, clrRed ) == -1 )
{
CMD.Error( "Order" );
}else{
Alert( "OrderSend Success!!\n Lots = " + (string)d_lots + "\n type = " + (string)i_type );
if( b_check0 )
LineOrder.reCreate( "LineOrder0", i_type );
if( b_check1 )
LineOrder.reCreate( "LineOrder1", i_type );
}
return true;
}
Event.mqh の ソースコード詳細
5. Order関数
bool CEvent::Order( int i_type, double d_lots )
{
LineOrder.check();
if( OrderSend( Symbol(), i_type, d_lots, Close[0], 0, 0, 0, "GUISample", 999, 0, clrRed ) == -1 )
{
CMD.Error( "Order" );
}else{
Alert( "OrderSend Success!!\n Lots = " + (string)d_lots + "\n type = " + (string)i_type );
if( b_check0 )
LineOrder.reCreate( "LineOrder0", i_type );
if( b_check1 )
LineOrder.reCreate( "LineOrder1", i_type );
}
return true;
}
前回までは、ただ注文を出すだけでしたが、ライン決済に対応させました。
LineOrder.check();
Buy,Sellボタンを押下したら、ラインが存在するのかをチェックします。
if( b_check0 )
Line0が表示されてるかをチェックします。
LineOrder.reCreate( "LineOrder0", i_type );
表示されていたら、ラインの名前と、オーダータイプを渡して、決済用のラインを作ってもらいます。
Common.mqh
#include "../AppWindow.mqh"
#include "CMD.mqh"
CPanelDialog AppWindow;
CCMD CMD;
bool b_lineCreate0;
bool b_lineCreate1;
bool b_check0; // --- 6
bool b_check1;
Common.mqh の ソースコード詳細
6. ラインの有無チェック
bool b_check0;
bool b_check1;
ぶっちゃけ、これ無駄じゃないかって思ってます。
次回までに良い方法が思いつけば変えます。
guiwindow.mqh
~
略
~
void OnTimer()
{
LineOrder.GetLine();
LineOrder.exitCheck(); // --- 7
}
guiwindow.mqh の ソースコード詳細
7. OnTimer関数
void OnTimer()
{
LineOrder.GetLine();
LineOrder.exitCheck();
}
OnTimer関数で、Pips用のラインと決済用のラインを呼び出してチェックしてます。
このままでは、無駄に重くなるので、ラインが存在するときだけチェックするように変更する予定です。
さいごに
今回は、5. 仕掛けに対して、ラインで決済できる処理 を実装します
次回は、6. 細かい修正 を実装します。
GitHubにソースを公開しました。細かい修正は、こちらをご覧下さい。
2年振りぐらいなので、いろいろと覚えてなかった。。。
GitHub : KEG_Qiita_EA
GitHub : KEG_Qiita_EA 差分
メインの部分はできたので、次回はデザイン、コードの簡略化、整理などをします。
出来るだけ分かりやすく、簡略化したですね。
YoutubeでLive配信しながら作ってます。
https://www.youtube.com/channel/UCcTw_iVgpLfrep9f94KxwLg?sub_confirmation=1
チャンネル登録お願いします
Twitterでは毎日呟いています。
https://twitter.com/IceSeed_bz
フォローお願いします
お疲れ様。
コメント