皆さんこんにちは!
今回は、MT4内に標準で搭載されている「Moving Average」に関してプログラミング視点で解説していきます!
これを理解すれば、オリジナルのEAを作成することができたりバックテストをとることも出来るので是非覚えてください!
※MT4の導入方法はこちらで解説しています!
今回、サンプルコードにはコメントを書き込んでいますが、この処理が何を意味するのかを理解することで学びが深まります。
私自身も勉強している時は、分からない関数出てきたら調べてコメントアウトしていました。
このようにサンプルコードを分解することは1番勉強になると感じております。
1度は持っているコードを分解してみてください!!
※おすすめのサイト
・https://yukifx.web.fc2.com/index.html
■コードの解説
- #define MAGICMA 20111111
- //— Inputs
- input double Lots =0.1;
- input double MaximumRisk =0.02;
- input double DecreaseFactor=3;
- input int MovingPeriod =12;
- input int MovingShift =6;
MAGICMA⇒マジックナンバーといってEAを区別するための個別の数字(同じものは使用できないが、何でもいい)
Inputs以下に関しては、グローバル関数で定義された変数※もう少し後で使用さるのでその際に解説
〇保有ポジションを計算する関数
- 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); //売り注文数を返す
- }
まず、for文で現在足からすべてのオーダーまでループさせます。
for文内if分で保有ポジションがない場合、break関数でループを抜ける。
取引中の通貨ペアが現在の通貨ペア且つ、マジックナンバーが一致した場合、買い/売りの注文数をカウントします。
〇複利で注文を行っていく処理
- double LotsOptimized()
- {
- double lot=Lots;
- int orders=OrdersHistoryTotal(); // history orders total
- int losses=0; // number of losses orders without a break
- //— select lot size
- lot=NormalizeDouble(AccountFreeMargin()*MaximumRisk/1000.0,1); //現在の余剰証拠金を取得して、それに指定してたリスクをかけて1000で割った値を少数点以下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++; //損益がマイナスだったらプラスカウント
- }
- //損失回数が2かい以上であればロットを減らす
- if(losses>1)
- lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1);
- }
- //— return lot size
- if(lot<0.1) lot=0.1; //ロットの最小値は0.1に設定
- return(lot);
- }
現在の余剰証拠金に対して[MaximumRisk](グローバル関数内で定義※今回は0.02)掛けて1000で割った値でLotを管理している。
処理内で損失が増えてくると(このコード内では2回以上)ロットを減らす処理が入っている。
ロットを減らしていくが、最小ロットは0.1になるよう設定されている。
〇注文処理
- 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); //Lots:ロット数、Bid:売り値、3:スリッページ、ストップロス、テイクプロフィット
- return;
- }
- //— buy conditions
- if(Open[1]<ma && Close[1]>ma)
- {
- res=OrderSend(Symbol(),OP_BUY,LotsOptimized(),Ask,3,0,0,””,MAGICMA,0,Blue); //Lots:ロット数、Ask:買い値、3:スリッページ、ストップロス、テイクプロフィット
- return;
- }
- //—
- }
if(Volume[0]>1) return;
ここではVolumeが1より大きくなった場合には処理を返すということをしています
⇒ロウソク足が確定してからという意味でもあり、連続で条件を満たさないようにしています。
下記では、変数を定義しています。
double ma ⇒MAの詳細
int res ⇒オーダーの詳細
MAの計算式
(通貨ペア, 時間軸 , 期間 , 表示移動, 種類 ,適応価格 ,バーの位置)
iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0)
⇒期間 = 12,移動表示 = 6, 種類 = SMA,適応価格 = 終値,バーの位置 = 0と定義されている。
※[MovingPeriod](グローバル関数内で定義),[MovingShift](グローバル関数内で定義)
注文処理を行う条件式を言語化すると、
【売り条件】
・一本前ロウソク足終値がMAを下に抜けた
上記が満たされると、OrderSend関数で処理が行われる。
OrderSend(symbol,cmd,volume,price,slippage,stoploss,takeprofit,comment,magic,expiration,arrow_color)
( 通貨ペア,注文タイプ,ロット数,注文価格,許容スリッページ,ストップロス,リミット価格,コメント,マジックナンバー,有効期限,矢印の色)
OrderSend(Symbol(),OP_SELL,LotsOptimized(),Bid,3,0,0,””,MAGICMA,0,Red)
⇒ロット数 = LotsOptimized関数(複利)でのロット,注文価格 = Bid(現在の売値) ,許容スリッページ = 3,ストップロス = 0(なし),リミット価格 = 0(なし),コメント = “”(なし),マジックナンバー = MAGICMA(定義した数字),有効期限 = 0(なし),矢印の色 = Red(赤色))
上記処理ではストップロスやリミット価格を定義していないが、この部分を定義してあげることで各処理を設定できる。
※買い条件はこれの逆のため割愛します。
〇決済処理
- 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;
- }
- }
- //—
- }
for文内で注文と保留中注文の総数までループし、注文があるかを確認します。
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
⇒保有ポジションが無ければfor文をbreakで抜けるという意味。
if(OrderMagicNumber()!=MAGICMA || OrderSymbol()!=Symbol()) continue;
⇒マジックナンバーが一致しない又は、通貨ペアが一致しない場合は処理を続行するという意味。
if(OrderType()==OP_SELL)
⇒売り注文があった場合は下の処理を行っています。
決済処理を行う条件式を言語化すると、
【売りポジションを持っている場合】
・一本前ロウソク足終値がMAを上に抜けている
上記が満たされると、OrderClose関数で処理が行われる。
(チケット番号,決済ロット数,価格,スリッページ,矢印の色)
OrderClose(OrderTicket(),OrderLots(),Ask,3,White)
⇒チケット番号 = OrderTicket()(OrderSelect()関数で設定された値),決済ロット数 = OrderLots()(OrderSelect()関数で設定された値),価格 = Ask(現在の買い価格),スリッページ = 3,矢印の色 = White(色)
※買いポジションを持っている場合の決済処理も逆になるため割愛します。
注意点として、売りポジションを持っている時は決済処理でAsk(買い)を使い、買いポジションを持っている時はBid(売り)を選択する。
〇実行処理
- 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();
- //—
- }
各処理で定義した自作関数(注文処理や決済処理等)を動かすための処理を上記では行っており、
Tickが更新するごとに条件に当てはまるのかを確認している。
■サンプルコード全文
- //+——————————————————————+
- //| 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 20111111
- //— 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;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); //売り注文数を返す
- }
- //+——————————————————————+
- //| Calculate optimal lot size |
- //+——————————————————————+
- //複利でまわす処理
- double LotsOptimized()
- {
- double lot=Lots;
- int orders=OrdersHistoryTotal(); // history orders total
- int losses=0; // number of losses orders without a break
- //— select lot size
- lot=NormalizeDouble(AccountFreeMargin()*MaximumRisk/1000.0,1); //現在の余剰証拠金を取得して、それに指定してたリスクをかけて1000で割った値を少数点以下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++; //損益がマイナスだったらプラスカウント
- }
- //損失回数が2かい以上であればロットを減らす
- if(losses>1)
- lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1);
- }
- //— return lot size
- if(lot<0.1) lot=0.1; //ロットの最小値は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_SELL,LotsOptimized(),Bid,3,0,0,””,MAGICMA,0,Red); //Lots:ロット数、Bid:売り値、3:スリッページ、ストップロス、テイクプロフィット
- return;
- }
- //— buy conditions
- if(Open[1]<ma && Close[1]>ma)
- {
- res=OrderSend(Symbol(),OP_BUY,LotsOptimized(),Ask,3,0,0,””,MAGICMA,0,Blue); //Lots:ロット数、Ask:買い値、3:スリッページ、ストップロス、テイクプロフィット
- 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;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;
- }
- }
- //—
- }
- //+——————————————————————+
- //| 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();
- //—
- }