たまにはEA作成講座でも・・・その1
といってもテクニカルにはからきし疎いので、ヾ(--;)
ポジションを持ったときやクローズしたときにメールを送るEAを作りたいと思います。
メール送信機能のないEAといっしょに使うといいかも。
ではでは、MetaTraderの前に集合ーっ!
まず、MT4を起動しメニューの「ツール」-「MetaQuotes language Editor」を起動します。

MeteEditorが起動したらメニューの「File」-「New」を選択します。

Expert Advisor Wizardが起動するのでExpert Advisorを選んで次へをクリックします。

Name(EA名)にSendMailと入力し完了をクリックします。

するとEAのテンプレートができましたよね?
//+------------------------------------------------------------------+
//| SendMail.mq4 |
//| Copyright ゥ 2011, MetaQuotes Software Corp. |
//| http://www.metaquotes.net |
//+------------------------------------------------------------------+
#property copyright "Copyright ゥ 2011, MetaQuotes Software Corp."
#property link "http://www.metaquotes.net"
//+------------------------------------------------------------------+
//| expert initialization function |
//+------------------------------------------------------------------+
int init()
{
//----
//----
return(0);
}
//+------------------------------------------------------------------+
//| expert deinitialization function |
//+------------------------------------------------------------------+
int deinit()
{
//----
//----
return(0);
}
//+------------------------------------------------------------------+
//| expert start function |
//+------------------------------------------------------------------+
int start()
{
//----
//----
return(0);
}
//+------------------------------------------------------------------+
//~行末まではコメントでプログラムには影響しません。
#propertyの行もプログラムの動作とは関係ないので無視して良いです。
以下の3つの関数が出来ています。
init()・・・EAの初期化タイミングで一度だけMT4から呼ばれます。
deinit()・・・EAの終了タイミングで一度だけMT4から呼ばれます。
start()・・・ティック毎(価格変動時)に毎回MT4から呼ばれます。
※「呼ばれます」と書きましたが、EAはMT4から呼ばれない限り、自ら能動的には動作することができません。
さて、これからプログラムを記述するわけですが、まず方針を決めます。
目的のメールを送信するためにはポジションが変化したかどうかを判断する必要があります。
方法は色々あると思いますが、今回はinit()関数で現在ポジションを変数に保存しておき、
start()関数で毎回ポジションに変化があったかどうかで判断しようと思います。
init()関数の前に現在ポジションのチケット番号を記憶する領域を宣言します。
#define NUM_POSITION_SIZE 32
int aiTicket[NUM_POSITION_SIZE];
#defineは記号常数で「プログラム中でNUM_POSITION_SIZEと書いたら32に置き換えてね」と言う意味です。
intは変数の型で整数という意味、aiTicketは配列変数の名前、[NUM_POSITION_SIZE]で、
この配列変数には32個の整数が記憶できるという意味になります。
32というのは私が勝手に決めた数で、このくらいで十分かなと思っただけです。
init()関数に現在ポジションを収集するコードを書き加えます。
int init()
{
int i, cnt;
int total = OrdersTotal();
for(i = 0; i < NUM_POSITION_SIZE; i++)
aiTicket[i] = 0;
for(i = 0, cnt = total - 1; i < NUM_POSITION_SIZE && cnt >= 0 ; cnt--){
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
if(OrderType() == OP_BUY || OrderType() == OP_SELL){
aiTicket[i] = OrderTicket();
i++;
}
}
return(0);
}
int i, cnt;はinit()関数の中で使う変数の宣言です。
int total = OrdersTotal();では変数totalに現在の全オーダー数を取得しています。
この様に変数は宣言と同時に値を割り当てることもできます。OrdersTotal()はMQL4で提供されている関数です。
どの様な関数があるかはこちらが参考になると思います。
※上記がリンク切れの場合メタトレーダー4日本語リファレンス「メタシス・シーカー【MetaSys-Seeker.net】」
上部の「MQL言語リファレンス」を辿ってください。
for(i = 0; i < NUM_POSITION_SIZE; i++)は繰り返し処理で、変数iの値を0~NUM_POSITION_SIZE-1まで
1ずつ増やしながら次の行aiTicket[i] = 0;を実行します。
つまりaiTicket[0]~aiTicket[31]に0がセットされます。
※配列の添え字は0から始るので、配列のサイズ-1が添え字の上限になります。
チケット番号は0以外なのでaiTicket配列の要素が0なら、そこは空き要素と判断できます。
for(i = 0, cnt = total - 1; i < NUM_POSITION_SIZE && cnt >= 0 ; cnt--){ですが、
上のfor文よりちょっとだけ複雑です。
for文はセミコロンで3つの部分に分けられますが、最初のパートが変数の初期化で、
この場合はiに0を代入し、cntにtotal - 1を代入しています。カンマで区切っていくらでも
書くことが出来ます。初期化が不要なら何も書かずに;だけでも良いという柔軟な仕様です。
極端な話for(;;)でも文法的には通ります。このままでは脱出できませんが・・・。
次のパートは繰り返し処理の継続条件でここに書いた条件が満たされている間、
{~}までが実行されます。さっきのfor文では{}が付いていませんが、
繰り返し実行する文が1行だけなら{}を付けなくてもいいです。
最後のパートが次の繰り返し処理に行く前に行う計算です。
で、話を戻してループの継続条件ですがi < NUM_POSITION_SIZE && cnt >= 0は、
iの値がNUM_POSITION_SIZEより小さく、なおかつcntの値が0以上なら継続という意味になります。
iはaiTicket配列の添え字に使っていて、iが配列のサイズを超えることが出来ないので
保険として書いています。
また、cntはオーダー数分繰り返すので、次のcnt--と合わせてtotal - 1~0までが取り得る値なります。
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);で「これからcnt番目のオーダーを参照しますよ」と
MT4に伝えています。
これからポジションを持っているオーダーを収集したいので
if(OrderType() == OP_BUY || OrderType() == OP_SELL){で判断します。
というのは、オーダーの中には指値待機オーダーや逆指値待機オーダーも含まれているため、
買ポジかまたは売ポジだけを抽出したいからです。
aiTicket[i] = OrderTicket();でi番目の配列要素(0から始まります)にチケット番号を
保存しています。
チケット番号の保存場所をi++;で次に進めます。
あーしんど。意外と大変なので完成ソースでお茶を濁して
残りの解説は次回(需要あるのか?)に続く・・・(爆
//+------------------------------------------------------------------+
//| SendMail.mq4 |
//| yuki |
//| http://paopao46.blog34.fc2.com/ |
//+------------------------------------------------------------------+
#property copyright "yuki"
#property link "http://paopao46.blog34.fc2.com/"
#define NUM_POSITION_SIZE 32
//---- input parameters
extern bool OrderOpenMail=true;
extern string OrderOpenMailSubject="Order Open Mail";
extern bool OrderCloseMail=true;
extern string OrderCloseMailSubject="Order Close Mail";
int aiTicket[NUM_POSITION_SIZE];
//+------------------------------------------------------------------+
//| expert initialization function |
//+------------------------------------------------------------------+
int init()
{
int i, cnt;
int total = OrdersTotal();
for(i = 0; i < NUM_POSITION_SIZE; i++)
aiTicket[i] = 0;
for(i = 0, cnt = total - 1; i < NUM_POSITION_SIZE && cnt >= 0 ; cnt--){
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
if(OrderType() == OP_BUY || OrderType() == OP_SELL){
aiTicket[i] = OrderTicket();
i++;
}
}
return(0);
}
//+------------------------------------------------------------------+
//| expert deinitialization function |
//+------------------------------------------------------------------+
int deinit()
{
return(0);
}
//+------------------------------------------------------------------+
void SendOrderMail(int ticket, bool bOpen)
{
int mode;
string subject;
if(bOpen){
mode = MODE_TRADES;
subject =OrderOpenMailSubject;
}else{
mode = MODE_HISTORY;
subject =OrderCloseMailSubject;
}
if(OrderSelect(ticket, SELECT_BY_TICKET, mode)){
string body = "Time:";
datetime dt;
if(bOpen)
dt = OrderOpenTime();
else
dt = OrderCloseTime();
dt = dt + (TimeLocal() - TimeCurrent());
body = body + TimeToStr(dt,TIME_DATE|TIME_SECONDS) + "\r\n";
body = body + "Type:";
switch(OrderType()){
case OP_BUY:
case OP_BUYLIMIT:
case OP_BUYSTOP:
body = body + "buy\r\n";
break;
case OP_SELL:
case OP_SELLLIMIT:
case OP_SELLSTOP:
body = body + "sell\r\n";
break;
}
body = body + "Size:" + DoubleToStr(OrderLots(), 2) + "\r\n";
body = body + "Symbol:" + OrderSymbol() + "\r\n";
body = body + "Price:" + DoubleToStr(OrderClosePrice(), MarketInfo(OrderSymbol(),MODE_DIGITS)) + "\r\n";
if(!bOpen){
body = body + "Swap:" + DoubleToStr(OrderSwap(), MarketInfo(OrderSymbol(),MODE_DIGITS)) + "\r\n";
body = body + "Profit:" + DoubleToStr(OrderProfit(), MarketInfo(OrderSymbol(),MODE_DIGITS)) + "\r\n";
}
SendMail(subject, body);
Print("Mail Sent ", body);
}
}
//+------------------------------------------------------------------+
//| expert start function |
//+------------------------------------------------------------------+
int start()
{
static datetime dtLastTime = 0;
static string sLastCommandNo = "";
//check by 2 seconds
if(TimeLocal() - dtLastTime < 2)
return(0);
//save current time
dtLastTime = TimeLocal();
int i, cnt, ticket;
//check close
int total = OrdersTotal();
bool LiveMap[NUM_POSITION_SIZE];
for(i = 0; i < NUM_POSITION_SIZE; i++)
LiveMap[i] = false;
for(cnt = total - 1; cnt >= 0 ; cnt--){
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
ticket = OrderTicket();
if(OrderType() == OP_BUY || OrderType() == OP_SELL){
for(i = 0; i < NUM_POSITION_SIZE; i++){
if(aiTicket[i] == ticket){
LiveMap[i] = true;
break;
}
}
}
}
for(i = 0; i < NUM_POSITION_SIZE; i++){
if(!LiveMap[i] && aiTicket[i] != 0){
//closed order
SendOrderMail(aiTicket[i], false);
aiTicket[i] = 0;
}
}
//check open
bool bFound;
total = OrdersTotal();
for(cnt = total - 1; cnt >= 0 ; cnt--){
OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
ticket = OrderTicket();
if(OrderType() == OP_BUY || OrderType() == OP_SELL){
bFound = false;
for(i = 0; i < NUM_POSITION_SIZE; i++){
if(aiTicket[i] == ticket){
bFound = true;
break;
}
}
if(!bFound){
//new order found
SendOrderMail(ticket, true);
for(i = 0; i < NUM_POSITION_SIZE; i++){
if(aiTicket[i] == 0){
aiTicket[i] = ticket;
break;
}
}
}
}
}
return(0);
}
//+------------------------------------------------------------------+
上のソースをMeteEditorにコピペして保存するとMT4で使える様になります(多分)。
MT4メニューの「ツール」-「オプション」-「E-メール」の設定もお忘れ無く!
たまにはEA作成講座でも・・・その2に続く。

| h o m e |