目次
とりあえずプログラムを見てみる
個人的な見解にはなるんですが、
プログラミングは教材とかで関数など一つ一つ見て覚えていくより
サンプルコードを見ながら
- どのように書かれてるのか
- どこに書かれているのか
- どんな使い方をしているのか
などを把握していきながらの勉強のほうが身につきやすいと考えてます。
なので今回はサンプルコードを見ながら一つ一つ分解して解説していこうと思います。
以下に移動平均線のEAのサンプルコードを貼っておきます。
長いのでクリックしたら見えるようにしています。
一回一回ここに戻ってこなくてもいいように解説する際は該当するコードとともに行います。
(homeで目次まで戻ることができます。)
今回使用するサンプルコードはメタエディタにデフォルトで入っていたものを使用しています。
//+------------------------------------------------------------------+
//| Moving Average.mq4 |
//| Copyright 2005-2014, MetaQuotes Software Corp. |
//| http://www.mql4.com |
//+------------------------------------------------------------------+
#property copyright "2005-2014, MetaQuotes Software Corp."
#property link "http://www.mql4.com"
#property description "Moving Average sample expert advisor"
#define MAGICMA 20131111
//--- Inputs
input double Lots =0.1;
input double MaximumRisk =0.02;
input double DecreaseFactor=3;
input int MovingPeriod =12;
input int MovingShift =6;
//+------------------------------------------------------------------+
//| Calculate open positions |
//+------------------------------------------------------------------+
int CalculateCurrentOrders(string symbol)
{
int buys=0,sells=0;
//---
for(int i=0;i0) return(buys);
else return(-sells);
}
//+------------------------------------------------------------------+
//| Calculate optimal lot size |
//+------------------------------------------------------------------+
double LotsOptimized()
{
double lot=Lots;
int orders=HistoryTotal(); // history orders total
int losses=0; // number of losses orders without a break
//--- select lot size
lot=NormalizeDouble(AccountFreeMargin()*MaximumRisk/1000.0,1);
//--- calcuulate number of losses orders without a break
if(DecreaseFactor>0)
{
for(int i=orders-1;i>=0;i--)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false)
{
Print("Error in history!");
break;
}
if(OrderSymbol()!=Symbol() || OrderType()>OP_SELL)
continue;
//---
if(OrderProfit()>0) break;
if(OrderProfit()<0) losses++;
}
if(losses>1)
lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1);
}
//--- return lot size
if(lot<0.1) lot=0.1;
return(lot);
}
//+------------------------------------------------------------------+
//| Check for open order conditions |
//+------------------------------------------------------------------+
void CheckForOpen()
{
double ma;
int res;
//--- go trading only for first tiks of new bar
if(Volume[0]>1) return;
//--- get Moving Average
ma=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);
//--- sell conditions
if(Open[1]>ma && Close[1]ma)
{
res=OrderSend(Symbol(),OP_BUY,LotsOptimized(),Ask,3,0,0,"",MAGICMA,0,Blue);
return;
}
//---
}
//+------------------------------------------------------------------+
//| Check for close order conditions |
//+------------------------------------------------------------------+
void CheckForClose()
{
double ma;
//--- go trading only for first tiks of new bar
if(Volume[0]>1) return;
//--- get Moving Average
ma=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);
//---
for(int i=0;ima && Close[1]ma)
{
if(!OrderClose(OrderTicket(),OrderLots(),Ask,3,White))
Print("OrderClose error ",GetLastError());
}
break;
}
}
//---
}
//+------------------------------------------------------------------+
//| OnTick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- check for history and trading
if(Bars<100 || IsTradeAllowed()==false)
return;
//--- calculate open orders by current symbol
if(CalculateCurrentOrders(Symbol())==0) CheckForOpen();
else CheckForClose();
//---
}
//+------------------------------------------------------------------+
宣言や定義など
コメントアウト //と/**/
複数行コメントは /* で始まり */ で終わります。
単一行コメントは // で始まり改行で終わります。
単一行コメントは、複数行コメント内でも使用する事が出来ます。
プログラミングでは、処理の内容などのメモ書きを結構します。
しかし、それらがプログラムの動作に影響があってはだめなので
このコメントアウト(//と/**/)を使い動作に関係ないですよーってしてあげないとだめです。
//+------------------------------------------------------------------+
//| Moving Average.mq4 |
//| Copyright 2005-2014, MetaQuotes Software Corp. |
//| http://www.mql4.com |
//+------------------------------------------------------------------+
上記はコメントアウトされた記述です。
//はその行のそれ以降の文字をコメントアウトしてます。
/*+------------------------------------------------------------------+
| Moving Average.mq4 |
| Copyright 2005-2014, MetaQuotes Software Corp. |
| http://www.mql4.com |
+------------------------------------------------------------------+*/
また、先ほどは単一行のコメントアウトを使っていたが
上記のように複数行のコメントアウトでも書くことができます。
どちらを使っても構わないと思います。
プログラムプロパティ #property
「#property」は、 プログラミングの付属情報のようなもので、EAに関する情報を表示するために使います。
プログラムの動作には関係ありません。
例えば、作成者名、作成者のサイトURL、プログラムのバージョン情報など。
今回は 作成者名、作成者のサイトURL、プログラムが何なのかが書かれています。
情報は、MT4画面左側の「ナビゲーター」からEA名をダブルクリックすればそのEAのサブウィンドウが開きますが、そのバージョン情報に表示されます。
#property copyright "2005-2014, MetaQuotes Software Corp."
#property link "http://www.mql4.com"
#property description "Moving Average sample expert advisor"
マクロ代入 #define
「define」 ですが、これま単に定義するだけの命令文 になります。
#define MAGICMA 20131111
今回はMAGICMAを後ろの数字 20131111に置き換えるとういことになります。
マジックナンバーは、
ポジション管理に直結する必要不可欠な要素なのでEAを作る際は必ず必要です。
同じ口座アカウントで複数のEA/ロジックを同時稼働するときに
ポジション毎にマジックナンバーを割り当てて管理することが可能になります。
このマジックナンバーが管理されていないと、まったく関係のないEAのポジションを勝手に決済してしまったりするのでしっかり管理しましょう。
変数 int,doubleなど
次はinputの話かと思いますが、
その前に変数についてかるーーく書いておきます。
まず、 変数とは何か?ご自分でこの問に答えてみてください。
あなたはおいくつですか?
5年後、あなたはいくつになっていますか?
年齢は、時間の経過とともに変化します。つまり、あなたの年齢が変数なのです。
すなわち、変数の特徴は何かしらの変化とともに変化することです。
例えば上記で示した例のように時間の変化により年齢が変わるみたいな感じです。
変数にはそれぞれ一定のタイプがあります。
- 整数--int
- 実数(小数も可)--double
- 文字列--string
- 真または偽--bool
基本的にこの四つは頭に入れておきましょう。
それぞれ使い方など軽く説明します。
変数を宣言する場合以下のような形で宣言します。
[ 変数タイプ] [ 識別子] = [ 変数 値];
識別子とは定数や変数、関数などの実体に対して与える名称の事です。
最初の文字に数字を使う事はできず長さの上限は63文字です。
使用出来る文字は、
・0~9の数字
・A~Z,a~zの英字
・アンダーライン( _ )
となってます。
int age = 20;
double height = 1.66;
string name = "い。";
bool trend = false;
上記に例を示しました。
「int」は整数のみ、「double」は少数も大丈夫。
「string」は文字列なので日本語も使えます。必ず「 ”(ダブルクォーテーション)」で挟んでください。
「bool」は「true」「false」の二つしか受け取れません。
MQL4 言語は大文字小文字を区別します。
複数の変数を同じ名前で大文字小文字を変えて宣言する場合、異なる変数となります。
「Age」「age」は別物です。
外部パラメーター input
テクニカルインジケーターのパラメーターや、取引ロット数、マジックナンバーなどは、
EAの使用者が自由に設定や変更したい数値です。
このような場合、「input」の後にこれらの数値を変数として宣言すれば、外部パラメーターとして利用できます。
外部パラメーターの数値は、
そのEAのサブウィンドウからユーザーが自由に設定や変更ができます。
input double Lots =0.1;
input double MaximumRisk =0.02;
input double DecreaseFactor=3;
input int MovingPeriod =12;
input int MovingShift =6;
今回のコードを上記に示しました。
五つのパラメーターをユーザーが変更可能ということになります。
例えば一番上の「Lots」ですが、0.1とあるがユーザーが変更すれば1にも0.01にも変えれます。
簡単に言うと、先ほど説明した変数はその時に決まりユーザーは変更できませんが
「input」を使うとユーザーがこのEAを使う前に変更したりできるようになる。ってことです。
コードに書かれている数値(0.1や0.02など)は初期値になります。
エキスパート設定で上の画面のようにでてくるのでここで変更できるようになります。
メイン部分の基礎
それでは大事なメイン部分を見ていきましょう。
基本的な部分は別ページにて細かい説明を行い、このページは使用している関数について主に解説します。。
その都度リンクを貼ります。
関数作成
関数とは、一定の処理を実行する流れをまとめたものです。
詳しくはこちらの記事にて(現在作成中)
if文、for文
if文とは、条件が満たされた場合に、一定の処理を実行させるために使用します。
for文とは、一定の処理を繰り返して実行させるために使用します。
EA作成において確実に使う知識です。しっかり身につけましょう。
詳しくは こちらの記事にて(現在作成中)
配列
配列とは、簡単に言うと複数の値を1つの変数にまとめて使用するためのものです。
詳しくは こちらの記事にて(現在作成中)
オープンポジションを計算する
オープンポジション(所有しているポジション)の計算をしています。
CalculateCurrentOrders()という関数を作って
OrderSelect()で所有ポジションがあるか調べ、OrderSymbol()で通貨ペアとマジックナンバーが一致しているか調べて、買いならbuysの値を増やし、売りならsellsの値を増やしてマイナスをかけています。
//+------------------------------------------------------------------+
//| Calculate open positions |
//+------------------------------------------------------------------+
int CalculateCurrentOrders(string symbol)
{
int buys=0,sells=0;
//---
for(int i=0;i<OrdersTotal();i++)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICMA)
{
if(OrderType()==OP_BUY) buys++;
if(OrderType()==OP_SELL) sells++;
}
}
//--- return orders volume
if(buys>0) return(buys);
else return(-sells);
}
注文総数 OrdersTotal()
エントリー中の注文と保留中注文の総数を返します。
例えば、エントリー中のポジション数が1、保留中の指値数が1
あったとすると戻り値は2になります。
ポジションの情報を取得 OrderSelect()
OrderSelect関数では3つの引数を指定して、該当のオーダーを選択します。
オーダーの選択に成功した場合はtrue、失敗した場合はfalseが返り値となります。
OrderSelect関数でオーダーを選択すると、OrderMagicNumberやOrderOpenPriceなどの関数を使用してオーダーの詳細情報を取得できるようになります。
基本のコードは以下になります。
bool OrderSelect(
int index, // index or order ticket
int select, // flag
int pool=MODE_TRADES // mode
);
- index
注文インデックスを指定。
select = SELECT_BY_TICKETの時はチケット番号を指定。 - select
SELECT_BY_POS : 注文プールのインデックスをindexに指定。SELECT_BY_TICKET : チケット番号をindexに指定。 - pool
注文プールの選択。初期値はMODE_TRADES
select = SELECT_BY_POSの時に使用します。
MODE_TRADES : 取引プールから注文を選択します(エントリー中の注文、保留中の注文)。
MODE_HISTORY : 履歴プールから注文を選択します(決済済み注文・キャンセルした注文)。
indexの番号は1からではなく0からです。
これは配列などと同じ感じです。 プログラミングでは数の始まりが基本的に0からです。
通貨ペア名 OrderSymbol()
現在選択中の注文の通貨ペア名を返します
事前にOrderSelect()で対象とする注文を選択する必要があります。
マジックナンバー OrderMagicNumber()
現在選択中の注文のマジックナンバーを返します 。
こちらも事前にOrderSelect()で対象とする注文を選択する必要があります。
OrderSelect()の後に使う関数一覧
OrderTicket(); //選択した注文のチケット番号
OrderOpenTime(); //選択した注文の注文時間
OrderOpenPrice(); //選択した注文の注文価格
OrderType(); //選択した注文の注文タイプ
OrderLots(); //選択した注文のロット数
OrderSymbol(); //選択した注文の通貨ペア
OrderStopLoss(); //選択した注文のストップロス価格
OrderTakeProfit(); //選択した注文のリミット価格
OrderCloseTime(); //選択した注文の決済時間
OrderClosePrice(); //選択した注文の決済価格
OrderCommission(); //選択した注文の手数料
OrderExpiration(); //選択した注文の保留有効期限
OrderSwap(); //選択した注文のスワップ
OrderProfit(); //選択した注文の損益
OrderComment(); //選択した注文のコメント
OrderMagicNumber(); //選択した注文のマジックナンバー
ロットサイズの計算
このコードではロットサイズの計算をしています。
HistoryTotal()でクローズした注文数の値を取り出してます。
AccountFreeMargin()で余剰証拠金額を取得します。
それに最初に設定したMaximumRiskをかけて1000で割ったものの小数点第1位までを求めています。
lossesで負けたトレードの数をカウントして負けるとロット数を減らすようにしています。
0.1より小さいときは0.1にしています。
//+------------------------------------------------------------------+
//| Calculate optimal lot size |
//+------------------------------------------------------------------+
double LotsOptimized()
{
double lot=Lots;
int orders=HistoryTotal(); // history orders total
int losses=0; // number of losses orders without a break
//--- select lot size
lot=NormalizeDouble(AccountFreeMargin()*MaximumRisk/1000.0,1);
//--- calcuulate number of losses orders without a break
if(DecreaseFactor>0)
{
for(int i=orders-1;i>=0;i--)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false)
{
Print("Error in history!");
break;
}
if(OrderSymbol()!=Symbol() || OrderType()>OP_SELL)
continue;
//---
if(OrderProfit()>0) break;
if(OrderProfit()<0) losses++;
}
if(losses>1)
lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1);
}
//--- return lot size
if(lot<0.1) lot=0.1;
return(lot);
}
クローズした注文数 HistoryTotal()
OrdersHistoryTotal()の昔の関数です。
ターミナルにロードされたアカウント履歴の決済済みの注文の数を返します。
小数点第何位まで求める NormalizeDouble()
指定された精度で丸められた浮動小数点数を返します。
基本のコードは以下です。
double NormalizeDouble(
double value, // 値
int digits // 丸める小数点以下の桁数
);
今回はdigitsが1なので 小数点第1位までです。
余剰証拠金 AccountFreeMargin()
現在アカウントの余剰証拠金を返します。
ポジションを持つときの状態を確認
CheckForOpen()という関数を作ってポジションを持つときの状態を確認します。
Volume[0]>1というのは新しい足にティック数がある場合を指します。この時はreturnで戻っているので新しい足にティック数がある場合はトレードしない。
したがって、新しい足ができた直後トレードするようになっています。
iMA()関数は移動平均線の関数でそれが
Open[1](一つ前の足の始値)より小さくかつClose[1](一つ前の足の終値)より大きい、つまり移動平均線を価格が下に抜けた場合に売り注文をだします。
その逆なら買い注文を出します。
注文はOrderSend()関数を使います。
//+------------------------------------------------------------------+
//| Check for open order conditions |
//+------------------------------------------------------------------+
void CheckForOpen()
{
double ma;
int res;
//--- go trading only for first tiks of new bar
if(Volume[0]>1) return;
//--- get Moving Average
ma=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);
//--- sell conditions
if(Open[1]>ma && Close[1]<ma)
{
res=OrderSend(Symbol(),OP_SELL,LotsOptimized(),Bid,3,0,0,"",MAGICMA,0,Red);
return;
}
//--- buy conditions
if(Open[1]<ma && Close[1]>ma)
{
res=OrderSend(Symbol(),OP_BUY,LotsOptimized(),Ask,3,0,0,"",MAGICMA,0,Blue);
return;
}
//---
}
iMA()
基本のコードは以下です。
double iMA(
string symbol, // 通貨ペア
int timeframe, // 時間軸
int ma_period, // MAの平均期間
int ma_shift, // MAシフト
int ma_method, // MAの平均化メソッド
int applied_price,// 適用価格
int shift // シフト
);
- string symbol
移動平均線の値を計算する通貨ペア名を指定します。
特定の通貨ペア名を指定せず、EAを適用したチャートの通貨ペアの場合は、「NULL」とします。 - int timeframe
移動平均線の値を計算する時間軸を指定。種 類 定 数 内 容 PERIOD_CURRENT 0 現在の時間足 PERIOD_M1 1 1分足 PERIOD_M5 5 5分足 PERIOD_M15 15 15分足 PERIOD_M30 30 30分足 PERIOD_H1 60 1時間足 PERIOD_H4 240 4時間足 PERIOD_D1 1440 日 足 PERIOD_W1 10080 週 足 PERIOD_MN1 43200 月 足 - int ma_period
移動平均線の値を計算する期間を指定。 - int ma_shift
移動平均線の表示を右方向にシフト(ずらす)するバーの個数を指定。 - int ma_method
移動平均線の種類を指定。種 類 定 数 内 容 MODE_SMA 0 単純移動平均線 MODE_EMA 1 指数移動平均線 MODE_SMMA 2 平滑移動平均線 MODE_LWMA 3 線形加重移動平均線 - int applied_price
移動平均線の値の計算に使用する価格データを指定します。種 類 定 数 内 容 PRICE_CLOSE 0 終 値 PRICE_OPEN 1 始 値 PRICE_HIGH 2 高 値 PRICE_LOW 3 安 値 PRICE_MEDIAN 4 中央値 (高値+安値)÷2 PRICE_TYPICAL 5 代表値 (高値+安値+終値)÷3 PRICE_WEIGHTED 6 加重終値 (高値+安値+終値+終値)÷4 - int shift
移動平均線の値を取得したいバーの位置を指定。
移動平均線の値を取得したいバーが現在のバーであれば「0」とします。
オーダーをする OrderSend()
オーダー関数は、以下のような定義になっています。
int OrderSend(
string symbol, // 通貨ペア名
int cmd, // 注文タイプ
double volume, // ロット数
double price, // 注文価格
int slippage, // スリップページ
double stoploss, // ストップロス価格
double takeprofit, // リミット価格
string comment = NULL, // コメント
int magic = 0, // マジックナンバー(識別用)
datetime expiration = 0, // 有効期限
color arrow_color = clrNONE // 色
);
- string symbol
注文を出す通貨ペアを指定します。
特定の通貨ペア名を指定せず、EAを適用したチャートの通貨ペアの場合は、「Symbol()」とします。 - int cmd
注文方法を指定します。種 類 定 数 内 容 OP_BUY 0 成行買い OP_SELL 1 成行売り OP_BUYLIMIT 2 指値買い OP_SELLLIMIT 3 指値売り OP_BUYSTOP 4 逆指値買い OP_SELLSTOP 5 逆指値売り - double volume
ロットサイズを指定。 - double price
注文価格を指定。 - int slippage
許容スリッページ数を指定。 - double stoploss
損切り価格を指定。損切り価格を指定しない場合は0。 - double takeprofit
利益確定価格を指定。利益確定価格を指定しない場合は0。 - string comment
注文に対するコメントを指定。コメントを指定しない場合は、「NULL」とします。 - int magic
マジックナンバー(=個々のEAを識別する整数値)を指定。 - datetime expiration
注文の有効期限を指定。
有効期限の指定は、待機注文に対してのみ有効です。
有効期限を指定しない場合は0。 - color arrow_color
オブジェクトの表示色を指定。
オブジェクトを表示させたくない場合は省略するか「clr_NONE」とします。
ポジションを決済するときの状態を確認
CheckForClose()という関数を作ってポジションを決済するときの状態を確認しています。
買い注文のポジションを持っている場合は、移動平均線が1つ前の足の始値より小さくかつ終値より大きい場合、つまり移動平均線を価格が下に抜けた場合に決済されます。
//+------------------------------------------------------------------+
//| Check for close order conditions |
//+------------------------------------------------------------------+
void CheckForClose()
{
double ma;
//--- go trading only for first tiks of new bar
if(Volume[0]>1) return;
//--- get Moving Average
ma=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);
//---
for(int i=0;i<OrdersTotal();i++)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
if(OrderMagicNumber()!=MAGICMA || OrderSymbol()!=Symbol()) continue;
//--- check order type
if(OrderType()==OP_BUY)
{
if(Open[1]>ma && Close[1]<ma)
{
if(!OrderClose(OrderTicket(),OrderLots(),Bid,3,White))
Print("OrderClose error ",GetLastError());
}
break;
}
if(OrderType()==OP_SELL)
{
if(Open[1]<ma && Close[1]>ma)
{
if(!OrderClose(OrderTicket(),OrderLots(),Ask,3,White))
Print("OrderClose error ",GetLastError());
}
break;
}
}
//---
}
OrderClose()
基本のコードは以下です。
bool OrderClose(
int ticket, // チケット番号
double lots, // ロット
double price, // 決済価格
int slippage, // スリップページ
color arrow_color // 決済カラー
);
これでポジションの決済を行います。
- int ticket
決済する注文のチケット番号を指定。 - double lots
決済するロットサイズを指定。 - double price
決済価格を指定。 - int slippage
許容スリッページ数を指定。 - color arrow_color
オブジェクトの表示色を指定します。
オブジェクトを表示させたくない場合は省略するか「clr_NONE」とします。
メイン処理
OnTick()でティックが更新されるたびに読み込みます。
Bars<100で足の数が100未満、IsTradeAllowed()==falseでトレードができない場合は注文を出しません。
CalculateCurrentOrders(Symbol())==0)はポジションがない状態を指します。その時はCheckForOpen()の関数でポジションを取るかどうかの判定に移ります。
それ以外の時はポジションがある状態ですのでCheckForClose()関数で決済するかどうかの判定に移ります。
//+------------------------------------------------------------------+
//| OnTick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- check for history and trading
if(Bars<100 || IsTradeAllowed()==false)
return;
//--- calculate open orders by current symbol
if(CalculateCurrentOrders(Symbol())==0) CheckForOpen();
else CheckForClose();
//---
}
//+------------------------------------------------------------------+
メイン処理 OnTick()
ここには、EAの中身そのもの、例えば、エントリーやエグジットの条件、新規注文や決済注文の命令などの各種処理を記述します。
OnTickは、チャートにレートが配信されるタイミングで実行します。
基本的に、プログラムは、上から順に動作していくので、イベントを起こしたい順番に上から記述していきます。
まとめ
このプログラムは
移動平均線が価格を上抜けした場合にロング、逆ならショート、
決済条件はポジションを取ったときの条件の逆というシンプルでした。
分からない単語、用語はすぐに調べる
始めの内は知らない単語だらけです。
自分も初見でこれを書いたので何もわからない状態でしたが、
調べまとめることでかなり理解できたと思います。
よくわからない単語をそのままにしておくと変な勘違いなど起きやすいです。
しっかり調べましょう。
最後にEAを作成したら必ずバックテストを行いましょう。できた!と言ってすぐ実践すると大きい損失になるかもしれませんよ。
以下にバックテストのやり方の記事を置いときます。
とりあえずバックテストはやっておいて損は無いのでしておきましょうね、、
EAのバックテスト(MT4)