こんにちは、たかはしみのるです。
前回に引き続き、裁量支援ツールの作成について書いていきたいと思います。
前回の記事を読んでいない方は、ぜひそちらから読んでください。
この図の右2つです。
エントリーダイアログのラジオボタンの排他処理
前回、エントリーダイアログのラジオボタンの排他処理は本体プログラムで作成すると書きましたが、エントリーダイアログの中で実装することにします。
この処理は、ラジオボタン3つのうち、どれか1つがクリックされた場合、残りの2つのラジオボタンのチェックを外すことで実現できます。
EntryDialogクラスのヘッダーファイルに以下の記述を追加します。
EVENT_MAP_BEGIN( CEntryDialog )
ON_EVENT(ON_CHANGE,m_rbtnStream,OnClickRBtnStream )
ON_EVENT(ON_CHANGE,m_rbtnPending,OnClickRBtnPending )
ON_EVENT(ON_CHANGE,m_rbtnStopLimit,OnClickRBtnStopLimit )
EVENT_MAP_END(CAppDialog )
void CEntryDialog::OnClickRBtnStream(){
m_rbtnPending.State(false);
m_rbtnStopLimit.State(false);
}
void CEntryDialog::OnClickRBtnPending(){
m_rbtnStream.State(false);
m_rbtnStopLimit.State(false);
}
void CEntryDialog::OnClickRBtnStopLimit(){
m_rbtnStream.State(false);
m_rbtnPending.State(false);
}
これでラジオボタンが押されたときに残りのラジオボタンのチェックが外れます。
決済用ダイアログ
前回の内容を踏まえ、決済用ダイアログはサクッと作っていきます。
決済用のダイアログはこんな感じで設計しました。
これに基づいてコーディングしてきます。
特に注意点はないですが、部品にCheckBoxを用いています。
#include <Controls\Dialog.mqh>
#include <Controls\Edit.mqh>
#include <Controls\Label.mqh>
#include <Controls\Button.mqh>
#include <Controls\CheckBox.mqh>
class CExitDialog : public CAppDialog{
public:
CExitDialog();
~CExitDialog();
//create
virtual bool Create(const long chart,const string name,const int subwin);
protected:
//Label
CLabel m_labelExit;
CLabel m_labelTotal;
CLabel m_labelTotalPips;
CLabel m_labelPips1;
CLabel m_labelPips2;
CLabel m_labelTrailPips;
CCheckBox m_checkTrail;
CCheckBox m_checkPips;
CEdit m_editTrailPips;
CButton m_btnExitPair;
CButton m_btnExitAll;
CButton m_btnDeletePips;
bool CreateLabelExit();
bool CreateLabelTotal();
bool CreateLabelTotalPips();
bool CreateLabelPips1();
bool CreateLabelPips2();
bool CreateLabelTrailPips();
bool CreateCheckTrail();
bool CreateCheckPips();
bool CreateEditTrailPips();
bool CreateBtnExitPair();
bool CreateBtnExitAll();
bool CreateBtnDeletePips();
};
CExitDialog::CExitDialog(){}
CExitDialog::~CExitDialog(){}
bool CExitDialog::Create( const long chart, const string name, const int subwin){
int x1 = 315;
int y1 = 50;
int x2 = 580;
int y2 = 280;
if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2)) return false;
if(!CreateLabelExit()) return false;
if(!CreateLabelTotal()) return false;
if(!CreateLabelTotalPips()) return false;
if(!CreateLabelPips1()) return false;
if(!CreateLabelTrailPips()) return false;
if(!CreateEditTrailPips()) return false;
if(!CreateLabelPips2()) return false;
if(!CreateCheckTrail()) return false;
if(!CreateCheckPips()) return false;
if(!CreateBtnExitPair()) return false;
if(!CreateBtnExitAll()) return false;
if(!CreateBtnDeletePips()) return false;
int i;
string objName;
string prefix=Name();
int total=ControlsTotal();
for(i=0;i<total;i++){
CWnd*obj=Control(i);
objName=obj.Name();
if(objName==prefix+"Client"){
CWndClient *wndclient=(CWndClient*) obj;
wndclient.ColorBackground(clrLavender);
ChartRedraw();
}
if(objName==prefix+"Caption"){
CEdit *edit=(CEdit*) obj;
edit.ColorBackground(clrLightSteelBlue);
ChartRedraw();
}
}
total = ObjectsTotal();
for(i=0; i < total; i++){
objName = ObjectName(i);
if(objName==prefix+"Chk_PipsLabel"){
ObjectSetString(m_chart_id, objName, OBJPROP_FONT, "MS Pゴシック");
ObjectSetInteger(m_chart_id, objName, OBJPROP_BGCOLOR, clrLavender);
ObjectSetInteger(m_chart_id, objName, OBJPROP_BORDER_COLOR, clrLavender);
ChartRedraw();
}
if(objName==prefix+"Chk_TrailLabel"){
ObjectSetString(m_chart_id, objName, OBJPROP_FONT, "MS Pゴシック");
ObjectSetInteger(m_chart_id, objName, OBJPROP_BGCOLOR, clrLavender);
ObjectSetInteger(m_chart_id, objName, OBJPROP_BORDER_COLOR, clrLavender);
ChartRedraw();
}
}
return true;
}
bool CExitDialog::CreateLabelExit(){
int x1 = 5;
int y1 = 5;
int x2 = 60;
int y2 = 20;
if(!m_labelExit.Create(m_chart_id, m_name+"Label_Exit", m_subwin, x1, y1, x2, y2)) return false;
if(!m_labelExit.Text("■決済")) return false;
if(!m_labelExit.Font("MS Pゴシック")) return false;
if(!Add(m_labelExit)) return false;
return true;
}
bool CExitDialog::CreateLabelTotal(){
int x1 = 80;
int y1 = 27;
int x2 = 125;
int y2 = 51;
if(!m_labelTotal.Create(m_chart_id, m_name+"Label_Total", m_subwin, x1, y1, x2, y2)) return false;
if(!m_labelTotal.Text("Total: ")) return false;
if(!m_labelTotal.Font("Arial")) return false;
if(!m_labelTotal.FontSize(14)) return false;
if(!Add(m_labelTotal)) return false;
return true;
}
bool CExitDialog::CreateLabelTotalPips(){
int x1 = 145;
int y1 = 27;
int x2 = 195;
int y2 = 51;
if(!m_labelTotalPips.Create(m_chart_id, m_name+"Label_TotalPips", m_subwin, x1, y1, x2, y2)) return false;
if(!m_labelTotalPips.Font("Arial")) return false;
if(!m_labelTotalPips.FontSize(14)) return false;
if(!Add(m_labelTotalPips)) return false;
return true;
}
bool CExitDialog::CreateLabelPips1(){
int x1 = 195;
int y1 = 27;
int x2 = 225;
int y2 = 51;
if(!m_labelPips1.Create(m_chart_id, m_name+"Label_Pips1", m_subwin, x1, y1, x2, y2)) return false;
if(!m_labelPips1.Text("pips")) return false;
if(!m_labelPips1.Font("Arial")) return false;
if(!m_labelPips1.FontSize(14)) return false;
if(!Add(m_labelPips1)) return false;
return true;
}
bool CExitDialog::CreateLabelTrailPips(){
int x1 = 45;
int y1 = 83;
int x2 = 145;
int y2 = 98;
if(!m_labelTrailPips.Create(m_chart_id, m_name+"Label_TrailPips", m_subwin, x1, y1, x2, y2)) return false;
if(!m_labelTrailPips.Text("トレーリング幅")) return false;
if(!m_labelTrailPips.Font("MS Pゴシック")) return false;
if(!Add(m_labelTrailPips)) return false;
return true;
}
bool CExitDialog::CreateLabelPips2(){
int x1 = 205;
int y1 = 83;
int x2 = 245;
int y2 = 98;
if(!m_labelPips2.Create(m_chart_id, m_name+"Label_Pips2", m_subwin, x1, y1, x2, y2)) return false;
if(!m_labelPips2.Text("pips")) return false;
if(!m_labelPips2.Font("Arial")) return false;
if(!Add(m_labelPips2)) return false;
return true;
}
bool CExitDialog::CreateEditTrailPips(){
int x1 = 150;
int y1 = 83;
int x2 = 200;
int y2 = 103;
if(!m_editTrailPips.Create(m_chart_id, m_name+"Edit_TrailPips", m_subwin, x1, y1, x2,y2)) return false;
if(!Add(m_editTrailPips)) return false;
if(!m_editTrailPips.Font("Arial")) return false;
if(!m_editTrailPips.FontSize(9)) return false;
return true;
}
bool CExitDialog::CreateCheckTrail(){
int x1 = 15;
int y1 = 61;
int x2 = 240;
int y2 = 81;
if(!m_checkTrail.Create(m_chart_id, m_name+"Chk_Trail", m_subwin, x1, y1, x2, y2)) return false;
//if(!m_checkTrail.Text("Enable Trailing Stop")) return false;
if(!m_checkTrail.Text("トレーリングストップ有効化")) return false;
if(!Add(m_checkTrail)) return false;
return true;
}
bool CExitDialog::CreateCheckPips(){
int x1 = 15;
int y1 = 158;
int x2 = 140;
int y2 = 178;
if(!m_checkPips.Create(m_chart_id, m_name+"Chk_Pips", m_subwin, x1, y1, x2, y2)) return false;
//if(!m_checkPips.Text("Display Pips")) return false;
if(!m_checkPips.Text("Pips表示")) return false;
if(!Add(m_checkPips)) return false;
return true;
}
bool CExitDialog::CreateBtnExitPair(){
int x1 = 20;
int y1 = 113;
int x2 = 120;
int y2 = 143;
if(!m_btnExitPair.Create(m_chart_id, m_name+"Btn_ExitPair", m_subwin, x1, y1, x2, y2)) return false;
if(!m_btnExitPair.Text("現通貨決済")) return false;
if(!m_btnExitPair.Font("MS Pゴシック")) return false;
if(!m_btnExitPair.ColorBackground(clrTomato)) return false;
if(!Add(m_btnExitPair)) return false;
return true;
}
bool CExitDialog::CreateBtnExitAll(){
int x1 = 140;
int y1 = 113;
int x2 = 240;
int y2 = 143;
if(!m_btnExitAll.Create(m_chart_id, m_name+"Btn_ExitAll", m_subwin, x1, y1, x2, y2)) return false;
if(!m_btnExitAll.Text("全通貨決済")) return false;
if(!m_btnExitAll.Font("MS Pゴシック")) return false;
if(!m_btnExitAll.ColorBackground(clrTomato)) return false;
if(!Add(m_btnExitAll)) return false;
return true;
}
bool CExitDialog::CreateBtnDeletePips(){
int x1 = 140;
int y1 = 153;
int x2 = 240;
int y2 = 183;
if(!m_btnDeletePips.Create(m_chart_id, m_name+"Btn_DeletePips", m_subwin, x1, y1, x2, y2)) return false;
if(!m_btnDeletePips.Text("Pips消去")) return false;
if(!m_btnDeletePips.Font("MS Pゴシック")) return false;
if(!m_btnDeletePips.ColorBackground(clrKhaki)) return false;
if(!Add(m_btnDeletePips)) return false;
return true;
}
表示してみたものがこちら。
設計通りになっています。
予約注文管理用ダイアログ
今回作成しているツールには、MT5のStopLimit注文を疑似的に再現する機能を組み込みます。ただMT4ではStopLimit注文を行うことができないため、あらかじめ決めた値段になるまでプログラム中に注文予約という形で保持しておき、条件を達成したら指値注文を入れる形で実装します。
このため、プログラム中に保持した予約注文を管理するダイアログが必要になります。
設計はシンプルです。
中央の大きな四角はListViewです。
配置場所さえ決まれば、あまり難しいことはありません。こちらもサクッと作っていきます。
で、出来上がったのがこちらです。
#include <Controls\Dialog.mqh>
#include <Controls\Label.mqh>
#include <Controls\Button.mqh>
#include <Controls\ListView.mqh>
class CReservedOrderDialog : public CAppDialog{
public:
CReservedOrderDialog();
~CReservedOrderDialog();
//create
virtual bool Create(const long chart,const string name,const int subwin);
protected:
CLabel m_labelManageReservedOrder;
CLabel m_labelColumn;
CListView m_listviewList;
CButton m_btnCancelSelected;
CButton m_btnCancelAll;
bool CreateLabelReservedDialog();
bool CreateLabelColumn();
bool CreateList();
bool CreateBtnCancel();
bool CreateBtnCancelAll();
};
CReservedOrderDialog::CReservedOrderDialog(){};
CReservedOrderDialog::~CReservedOrderDialog(){};
bool CReservedOrderDialog::Create(const long chart,const string name,const int subwin){
int x1 = 50;
int y1 = 340;
int x2 = 310;
int y2 = 520;
if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2)) return false;
if(!CreateLabelReservedDialog()) return false;
if(!CreateLabelColumn()) return false;
if(!CreateList()) return false;
if(!CreateBtnCancel()) return false;
if(!CreateBtnCancelAll()) return false;
int i;
string objName;
string prefix=Name();
int total=ControlsTotal();
for(i=0;i<total;i++){
CWnd*obj=Control(i);
objName=obj.Name();
if(objName==prefix+"Client"){
CWndClient *wndclient=(CWndClient*) obj;
wndclient.ColorBackground(clrLavender);
ChartRedraw();
}
if(objName==prefix+"Caption"){
CEdit *edit=(CEdit*) obj;
edit.ColorBackground(clrLightSteelBlue);
ChartRedraw();
}
}
return true;
}
bool CReservedOrderDialog::CreateLabelReservedDialog(){
int x1 = 5;
int y1 = 5;
int x2 = 110;
int y2 = 20;
if(!m_labelManageReservedOrder.Create(m_chart_id, m_name+"Label_Reserved", m_subwin, x1, y1, x2, y2)) return false;
if(!m_labelManageReservedOrder.Text("■予約注文管理(StopLimit)")) return false;
if(!m_labelManageReservedOrder.Font("MS Pゴシック")) return false;
if(!Add(m_labelManageReservedOrder)) return false;
return true;
}
bool CReservedOrderDialog::CreateLabelColumn(){
int x1 = 15;
int y1 = 24;
int x2 = 240;
int y2 = 39;
if(!m_labelColumn.Create(m_chart_id, m_name+"Label_Column", m_subwin, x1, y1, x2, y2)) return false;
if(!m_labelColumn.Text("ロット, 注文, 到達価格, エントリー価格")) return false;
if(!m_labelColumn.Font("MS Pゴシック")) return false;
if(!m_labelColumn.FontSize(8)) return false;
if(!Add(m_labelColumn)) return false;
return true;
}
bool CReservedOrderDialog::CreateList(){
int x1 = 15;
int y1 = 40;
int x2 = 240;
int y2 = 100;
if(!m_listviewList.Create(m_chart_id, m_name+"ReservedList", m_subwin, x1, y1, x2, y2)) return false;
if(!Add(m_listviewList)) return false;
return true;
}
bool CReservedOrderDialog::AddItem(string str){
if(!m_listviewList.AddItem(str)) return false;
return true;
}
bool CReservedOrderDialog::ItemDelete(const int index){
return m_listviewList.ItemDelete(index);
}
bool CReservedOrderDialog::ItemsClear(void){
if(!m_listviewList.ItemsClear()) return false;
return true;
}
bool CReservedOrderDialog::CreateBtnCancel(){
int x1 = 20;
int y1 = 110;
int x2 = 120;
int y2 = 140;
if(!m_btnCancelSelected.Create(m_chart_id, m_name+"CancelSelected", m_subwin, x1, y1, x2,y2)) return false;
if(!Add(m_btnCancelSelected)) return false;
if(!m_btnCancelSelected.Text("選択取消")) return false;
if(!m_btnCancelSelected.FontSize(10)) return false;
if(!m_btnCancelSelected.Font("MS Pゴシック")) return false;
if(!m_btnCancelSelected.ColorBackground(clrKhaki)) return false;
return true;
}
bool CReservedOrderDialog::CreateBtnCancelAll(){
int x1 = 140;
int y1 = 110;
int x2 = 240;
int y2 = 140;
if(!m_btnCancelAll.Create(m_chart_id, m_name+"CancelAll", m_subwin, x1, y1, x2,y2)) return false;
if(!Add(m_btnCancelAll)) return false;
if(!m_btnCancelAll.Text("一括取消")) return false;
if(!m_btnCancelAll.FontSize(10)) return false;
if(!m_btnCancelAll.Font("MS Pゴシック")) return false;
if(!m_btnCancelAll.ColorBackground(clrTomato)) return false;
return true;
}
出来上がったものを表示します。
こちらも設計した通りになっています。
ダイアログ作成は今回でほぼ終了です。(セッターやゲッターを追加する必要はありますが。。)
残っている作業は、成行注文、指値注文、一括決済などの具体的な機能の追加になります。みなさんの役に立ちそうな内容があれば、「プログラミング」カテゴリーで記事を書こうと思います。
特になければ、「自作ツール」カテゴリーで出来上がったものを共有したいと思っていますので、引き続きお付き合いください。
コメント
大変貴重な情報
ありがとうございます。
今までやりたかったことがすべてここにあり、
大変参考になっております。
ただ、私はC言語はわかりますが、MQL4のC++的な(そう感じてます)
コーディングの解読には一苦労しています。
OnInit()やOnTick()などからの呼び出し方法がわかると幸いです。
あるいは、MQL4の新規作成から何を選んで作成すればよいかわかると幸いです。
勝手なお願いで大変恐縮でございますが、
お時間がある時に、ご回答いただけると幸いです。
よろしくお願いいたします。
ひろすえ様
コメントありがとうございます。
ここで作成したダイアログは”.mqh”というファイルに保存することになりますので、
「インクルード(*.mqh)」を選択することになります。
プログラム中で呼び出す場合は、
1. mqhファイルをインクルードする
2. 作成したクラスの変数を定義し初期化する
というステップで呼び出すことができるようになります。
私は過去、作成したダイアログのmqhファイルを消してしまったことがあり、
EAやインジケーターフォルダにmqhファイルを保存していますので、
インクルード方法が若干異なるかもしれませんが、実例を書きます。
(他のMT4にソースファイルをコピーし忘れないようにしています)
1. mqhファイルをインクルードする
“EntryDialog.mqh”というmqhファイルを作成してEAと同じディレクトリに保存した場合、EAのプログラムの冒頭部分に
#include “EntryDialog.mqh”
と記述することでmqhファイルをインクルードすることができます。
これでプログラム中で作成したクラスを使うことができるようになります。
2. 作成したクラスの変数を定義し初期化する
“CEntryDialog”クラスの変数”EntryWindow”を作成する場合、私は
CEntryDialog *EntryWindow;
で定義しています。(グローバル変数にしています)
OnInit()の中で
EntryWindow = new CEntryDialog;
と記述することで、使えるようになります。
もし不明な点があれば、またコメントいただけるとうれしいです。
いつも有難うございます。
私は、FXを始めたばかりのものですが、中々勝てません。
トレンドの出ている押し目や、戻り売りの場所で入るのですが、
なぜか私がエントリーすると、逆へ動いてしまいます。…
本題に入ります。
高橋様の「MT4裁量支援ツール作成」を拝見し、MT4のEAの1つとして導入させていただこうかと思っております。
M_SupportTool.ex4は正しく動きます。
各ダイアログのソースを公開されているので、同じ機能のEAを練習のために作ろうと思うのですが、
1. 各ダイアログを表示/非表示するボタンは、MQL4にあるObjectCreate(OBJ_BUTTON)でしょうか?
2. 表示された各ダイアログうちどれかの[x]ボタンをクリックするとEA自体が閉じてしまい 再表示出来ません。
以上の問題を解決するにはどの様にすれば良いのでしょうか?
私は、C++をいじったことが無いので どの様に解決できるのか思案に暮れております。
よろしければご教示いただければ幸いです。
Y Ima様
コメントありがとうございます。励みになります。
私も押し目買いや戻り売りを狙いますが、反転せず含み損になってしまうことはよくありますよね。
最近は長期にはもちろん順張りで、短期足でも反転を確認してからエントリーするように心がけています。(例えば、RSIだと30を下回ったから買うのではなく、30を上回ったことを確認する)頭はくれてやる、という気持ちです(笑)
さて、ご質問いただいた裁量支援ツールの件、改めてソースコードを確認しました。
1. 「表示/非表示ボタン」について
これはCAppDialogクラスのメンバ変数でCBmpButtonクラスです。m_button_minmaxという名前です。CAppDialogクラスを継承して作成した独自クラスには、勝手に継承されると思います。詳細が気になる場合は、
Include – Controls – Dialog.mqh を参照されるといいかもしれません。
2. 「×」ボタンについて
これは私も困った記憶があります。
今回は、OnChartEventの中で押されたボタンを判別し、「×」ボタンや「非表示」ボタンであれば、Dialogのイベントを呼ばずにDialogのvisibleステータスをfalseにすることで逃げています。
もし何か不明な点があれば気軽に質問ください。