このシリーズ記事ではEA(自動売買プログラム)を初めて開発する人に向けて、プログラミングの基礎から実践的な取引戦略をEAに実装する方法まで丁寧に解説しています。
記事を途中から読んでも問題なく理解できるようになっていますが、以下のリストの順番通りに読み進めていくことで、より理解が深まるように構成されています。
[itemlink post_id=”2236″]
プログラミング学習はどうしても一人だとつまづいてしまう時がきます。調べればわかることも少なくないですが、最初のうちは調べ方もわからないことが多いため、あまり効率的ではありません。
効率的かつ挫折せずにプログラミングを学習したい方はスクールを検討してみるのも一つの手です。
中には無料で通えるスクールや、就職保証をしてくれるスクールなどもあるので、きっとあなたの目的に応じて最適のスクールが見つかります!以下の記事で評判がよく特におすすめのスクールをいくつかピックアップしているので、スクール選びで後悔したくない方は御覧ください!
これまでEAにトレードを行わせるために必要な関数について解説してきました。今回はそれらを実際にどうやってあなたの取引戦略に活用していくかについて説明します。以下のシリーズ記事を順番に読んでいくと、より理解できるようになっています。
FX市場において取引を行うということは、常に自分の資産がリスクに晒されていることを意味します。
もちろん、相応の見返りを得られる可能性もありますが、それでは単なるギャンブルと変わりません。
FXがギャンブルと大きく異なる点はリスク管理をしっかり行うことで、長期的にみて堅実な利益を得ることができるという点です。ある調査によればプロのトレーダーは年間で平均7%の利益を得ているそうです。厳しいことを言いますが、この値が小さいと思った方はFXに向いていないかもしれません。
たしかに短期でそれ以上に勝つことは意外と簡単に達成できるかもしれません。しかしそれは偶然の産物であり、再現性に乏しいため、これに味を占めた方は長期的には資産を減らしていくことが容易に想像できます。
リスク管理をしっかり行うというのは、自分の資産に合ったトレード手法、ルールを事前に決め、それを徹底して継続することを意味します。そこに人間の感情は邪魔な要素になってくるため、EAにあなたの戦略を自動化させるのです。
それでは次のステップで、これまで解説した関数を使ってあなたのトレード戦略をEAに実装する手順を解説していきます。
考えたトレード戦略が利益を生み出すかは何回もテストを行って検証する必要があります。以下ではこれまでの関数を活用してトレード戦略を検証・実装する手順を紹介します。
次にCandleClose()関数を使って指定した本数のローソク足が完成した後にトレードをクロー
ズするように設定してみましょう。順番に5本、10本、20本後でクローズするようにテストし、
どのときに一番利益が出るか確かめてみてください。このときにUseStopLoss、UseTakeProfit
UseBreakEvenはfalseにしておきましょう。
いかがでしたか?
マーケットは常に変動するものなので、いつでも確実に利益をだす戦略というのはありえません。それでも長期的にみれば高い割合で利益を得られる戦略を見つけることは可能です。
そのためには様々な戦略を様々な通貨ペア・時間軸上で検証する必要があります。ぜひパラメーターを細かく調整しながら、あなたにとってベストなEAを開発してみてください。
[itemlink post_id=”2236″]
この記事は【EAの教科書シリーズ】の一部です。全記事は以下から確認できます。
MT4をまだダウンロードしていない方はこちらのページでダウンロード方法を案内しているので、事前にMT4を用意しておいてください。
今回作る関数はEAに行わせたい取引戦略を記述し、注文を出すタイミングを見極めるための関数です。今回作る関数でトレードタイミングを見極め、タイミングがきたら以前作成したTrade関数を使って実際にトレードを行わせています。Trade関数は細かい設定をつけて注文を出せる関数ですが、詳しくは以下の記事で解説していますので、興味のある方はご覧ください。
今回作る関数の名前はEntrySignal()とします。
今回はシンプルに短期の移動平均線が長期の移動平均線を上回ったタイミングでロングエントリー、下回ればショートエントリーする戦略をプログラム化していきたいと思います。従って、短期と長期それぞれの直前の移動平均と、二つ前の移動平均を出したうえで、それらを比較する必要があります。
例えば二つ前の短期移動平均が長期移動平均を下回っており、一つ前の短期移動平均が長期移動平均を上回っていればロングでエントリーするということになります。ちなみにこのことをゴールデンクロスと呼びます。
従って、上述した移動平均をそれぞれ保持するためのローカル変数を準備します。
それぞれの移動平均を算出するためにグローバル変数として宣言したLongMAPeriodなどが使われていることを確認してください。
void EntrySignal() //1
{
double ShortMACurrent=iMA(Symbol(),PERIOD_CURRENT,ShortMAPeriod,0,MODE_SMA,PRICE_CLOSE,1); //2
double LongMACurrent=iMA(Symbol(),PERIOD_CURRENT,LongMAPeriod,0,MODE_SMA,PRICE_CLOSE,1);
double ShortMAPrevious=iMA(Symbol(),PERIOD_CURRENT,ShortMAPeriod,0,MODE_SMA,PRICE_CLOSE,2);
double LongMAPrevious=iMA(Symbol(),PERIOD_CURRENT,ShortMAPeriod,0,MODE_SMA,PRICE_CLOSE,2);
if(TradeLong) //3
{
if(ShortMAPrevious<LongMAPrevious && ShortMACurrent>LongMACurrent) //4
{
Trade(0); //5
}
}
if(TradeShort)
{
if(ShortMAPrevious>LongMAPrevious && ShortMACurrent<LongMACurrent)
{
Trade(1);
}
}
return;
}
その後は逆の計算を売り注文のほうで行わせています。
extern int TakeProfit=50;
extern int StopLoss=25;
extern double LotSize=0.01;
extern bool UseBreakEven=true;
extern int MoveToBreakEven=40;
extern int PipsProfitLock=20;
double pips;
extern bool UseTrailingStop=true;
extern int TrailTrigger=50;
extern int TrailAmount=50;
extern bool UseStopLoss=true;
extern bool UseTakeProfit=true;
extern bool UseRiskReward=true;
extern bool UsePositionSizing=true;
extern int RiskPercent=1;
extern int RewardRatio=2;
extern bool UseCandleClose=true;
extern int CloseAfterCandles=1;
//今回追加した変数
extern int ShortMAPeriod=50;
extern int LongMAPeriod=100;
extern bool TradeLong=true;
extern bool TradeShort=true;
ここまでのグローバル変数は以下のようになっています。
この関数はOnTick()関数内で使用し、既存の注文がない場合に動作するようにします。
void OnTick()
{
//---
if(IsNewCandle()){
if(TotalOpenOrders()<1){
EntrySignal();
}
if(TotalOpenOrders()>0){
if(UseBreakEven){
BreakEven();
}
if(UseTrailingStop){
TrailingStop();
}
if(UseCandleClose){
CandleClose();
}
}
}
}
注文が一つもないときにはティック毎にEntrySignal()が動作し、チャートをチェックしてくれるので、エントリータイミングが来た瞬間に注文されることになります。
[itemlink post_id=”2236″]
プログラミング学習はどうしても一人だとつまづいてしまう時がきます。調べればわかることも少なくないですが、最初のうちは調べ方もわからないことが多いため、あまり効率的ではありません。
効率的かつ挫折せずにプログラミングを学習したい方はスクールを検討してみるのも一つの手です。
中には無料で通えるスクールや、就職保証をしてくれるスクールなどもあるので、きっとあなたの目的に応じて最適のスクールが見つかります!以下の記事で評判がよく特におすすめのスクールをいくつかピックアップしているので、スクール選びで後悔したくない方は御覧ください!
この記事は【EAの教科書シリーズ】の一部です。全記事は以下から確認できます。
MT4をまだダウンロードしていない方はこちらのページでダウンロード方法を案内しているので、事前にMT4を用意しておいてください。
トレードの終え方は人によって異なります。一定の損切りポイントや利確ポイントで取引を終了するトレーダーもいれば、チャート上のローソク足のルールに従って終了するトレーダーもいます。
今回この関数を作る目的は今後解説する予定の取引戦略にローソク足を基準とした取引手法が含まれているためです。
関数名はCandleClose()とします。
この関数はトレード中の注文が一つでもある場合にティック毎に呼び出されるようにします。ですので、以前作成したBreakEven()関数とTrailingStop()関数が配置されている場所と同じ括弧内に置かれることになります。
以下が今回導入する関数のコードになります。
void CandleClose() //1
{
int period=Period(); //2
int period2=0; //3
switch(period) //4
{
case 1:period2=60; break;
case 5:period2=300; break;
case 15:period2=900; break;
case 30:period2=1800; break;
case 60:period2=3600; break;
case 240:period2=14400; break;
case 1440:period2=86400; break;
case 10080:period2=604800; break;
case 43200:period2=2592000; break;
}
for(int i=OrdersTotal();i>0;i--) //5
{
if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //6
{
if(TimeCurrent()-OrderOpenTime()>period2*CloseAfterCandles) //7
{
CloseAllOrders(); //8
}
}
}
return;
}
以前までの記事の内容も含めたここまでのグローバルエリアは以下のようになっています。
extern int TakeProfit=50;
extern int StopLoss=25;
extern double LotSize=0.01;
extern bool UseBreakEven=true;
extern int MoveToBreakEven=40;
extern int PipsProfitLock=20;
double pips;
extern bool UseTrailingStop=true;
extern int TrailTrigger=50;
extern int TrailAmount=50;
extern bool UseStopLoss=true;
extern bool UseTakeProfit=true;
extern bool UseRiskReward=true;
extern bool UsePositionSizing=true;
extern int RiskPercent=1;
extern int RewardRatio=2;
//今回追加したグローバル変数
extern bool UseCandleClose=true;
extern int CloseAfterCandles=1;
この関数を使う場合はグローバル変数UseCandleCloseをtrueにしておく必要があります。
またCloseAfterCandlesに何本ローソク足が出来上がったら決済したいかを指定する数値を設定しておきます。
またこの関数を使う際には以前作成したUseStopLossとUseTakeProfitをfalseにしてください。そうしないと決済するためのプログラムが複数出来上がってしまいます。
前述したとおり、この関数はOnTick()の中で注文が一つ以上ある場合に実行します。
void OnTick()
{
//---
if(IsNewCandle()){
if(TotalOpenOrders()<1){
NewOrder();
}
if(TotalOpenOrders()>0){
if(UseBreakEven){
BreakEven();
}
if(UseTrailingStop){
TrailingStop();
}
if(UseCandleClose){
CandleClose();
}
}
}
}
ここまでのコード全体は以下のようになっています。
//+------------------------------------------------------------------+
//| test.mq4 |
//| Kanrinin |
//| https://codelabsjp.net |
//+------------------------------------------------------------------+
#property copyright "Kanrinin"
#property link "https://codelabsjp.net"
#property version "1.00"
#property strict
extern int TakeProfit=50;
extern int StopLoss=25;
extern double LotSize=0.01;
extern bool UseBreakEven=true;
extern int MoveToBreakEven=40;
extern int PipsProfitLock=20;
double pips;
extern bool UseTrailingStop=true;
extern int TrailTrigger=50;
extern int TrailAmount=50;
extern bool UseStopLoss=true;
extern bool UseTakeProfit=true;
extern bool UseRiskReward=true;
extern bool UsePositionSizing=true;
extern int RiskPercent=1;
extern int RewardRatio=2;
extern bool UseCandleClose=true;
extern int CloseAfterCandles=1;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
PipsFunction();
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//---
if(IsNewCandle()){
if(TotalOpenOrders()<1){
NewOrder();
}
if(TotalOpenOrders()>0){
if(UseBreakEven){
BreakEven();
}
if(UseTrailingStop){
TrailingStop();
}
if(UseCandleClose){
CandleClose();
}
}
}
}
//+------------------------------------------------------------------+
void NewOrder()
{
int Result=OrderSend(Symbol(),OP_BUY,LotSize,Ask,3,NormalizeDouble(Ask-StopLoss*pips,4),NormalizeDouble(Ask+TakeProfit*pips,4),NULL,0123,0,clrNONE);
return;
}
bool IsNewCandle()
{
static int BarsOnChart=0;
if(Bars==BarsOnChart){
BarsOnChart = Bars;
return(false);
}
BarsOnChart = Bars;
return(true);
}
int TotalOpenOrders()
{
int Orders=0;
int Total=OrdersTotal();
for(int i=Total; i>0; i--)
{
bool res=OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES);
if(OrderType()==OP_BUY || OrderType()==OP_SELL)
{
Orders++;
}
}
return(Orders);
}
void CloseAllOrders() //1
{
int Total=OrdersTotal(); //2
for(int i=Total;i>0;i--) //3
{
if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //4
{
if(OrderType()==OP_SELL) //5
{
bool res1=OrderClose(OrderTicket(),OrderLots(),Ask,3,clrNONE); //6
}
if(OrderType()==OP_BUY) //7
{
bool res2=OrderClose(OrderTicket(),OrderLots(),Bid,3,clrNONE); //8
}
if(OrderType()==OP_BUYLIMIT || OrderType()==OP_BUYSTOP || OrderType()==OP_SELLLIMIT || OrderType()==OP_SELLSTOP) //9
{
bool res3=OrderDelete(OrderTicket(),clrNONE); //10
}
}
}
return;
}
void PipsFunction() //1
{
double ticksize=MarketInfo(Symbol(),MODE_TICKSIZE); //2
if(ticksize == 0.00001) //3
{
pips = ticksize*10; //4
}
else
{
pips = ticksize; //5
}
return;
}
void BreakEven() //1
{
for(int i=OrdersTotal();i>0;i--) //2
{
if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //3
{
if(OrderType()==OP_BUY) //4
{
if(Bid-OrderOpenPrice()>MoveToBreakEven*pips) //5
{
if(OrderOpenPrice()>OrderStopLoss()) //6
{
bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()+PipsProfitLock*pips,OrderTakeProfit(),0,clrNONE); //7
Alert("Yes");
}
}
}
if(OrderType()==OP_SELL)
{
if(OrderOpenPrice()-Ask>MoveToBreakEven*pips)
{
if(OrderOpenPrice()<OrderStopLoss())
{
bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()-PipsProfitLock*pips,OrderTakeProfit(),0,clrNONE);
}
}
}
}
}
}
void TrailingStop() //1
{
for(int i=OrdersTotal();i>0;i--) //2
{
if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //3
{
if(OrderType()==OP_BUY)//4
{
if(Bid-OrderOpenPrice()>TrailTrigger*pips) //5
{
if(OrderStopLoss()<Bid-TrailAmount*pips) //6
{
bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),Bid-TrailAmount*pips,OrderTakeProfit(),0,clrNONE); //7
}
}
}
if(OrderType()==OP_SELL)
{
if(OrderOpenPrice()-Bid>TrailTrigger*pips)
{
if(OrderStopLoss()>Bid+TrailAmount*pips)
{
bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),Bid+TrailAmount*pips,OrderTakeProfit(),0,clrNONE);
}
}
}
}
}
return;
}
void Trade(int Direction) //1
{
double SL; //2
double TP; //3
double Equity=AccountEquity(); //4
double RiskedAmount=Equity*RiskPercent*0.01; //5
double Lots=0; //6
//買い注文の場合
if(Direction==0) //7
{
if(UseStopLoss) //8
{
SL=Bid-StopLoss*pips;
}
else
{
SL=0;
}
if(UseTakeProfit) //9
{
if(UseRiskReward && UseStopLoss) //10
{
TP=StopLoss*pips*RewardRatio+Bid;
}
else
{
TP=Bid+TakeProfit*pips;
}
}
else
{
TP=0;
}
if(UsePositionSizing && UseStopLoss) //11
{
Lots=(RiskedAmount/StopLoss)/10;
}
else
{
Lots=LotSize;
}
int res=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,NormalizeDouble(SL,4),NormalizeDouble(TP,4),NULL,0,0,clrNONE); //12
}
//売り注文の場合
if(Direction==1)
{
if(UseStopLoss) //8
{
SL=Ask+StopLoss*pips;
}
else
{
SL=0;
}
if(UseTakeProfit) //9
{
if(UseRiskReward && UseStopLoss) //10
{
TP=Ask-StopLoss*pips*RewardRatio;
}
else
{
TP=Ask-TakeProfit*pips;
}
}
else
{
TP=0;
}
if(UsePositionSizing && UseStopLoss) //11
{
Lots=(RiskedAmount/StopLoss)/10;
}
else
{
Lots=LotSize;
}
int res=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,NormalizeDouble(SL,4),NormalizeDouble(TP,4),NULL,0,0,clrNONE); //12
}
return;
}
void CandleClose() //1
{
int period=Period(); //2
int period2=0; //3
switch(period) //4
{
case 1:period2=60; break;
case 5:period2=300; break;
case 15:period2=900; break;
case 30:period2=1800; break;
case 60:period2=3600; break;
case 240:period2=14400; break;
case 1440:period2=86400; break;
case 10080:period2=604800; break;
case 43200:period2=2592000; break;
}
for(int i=OrdersTotal();i>0;i--) //5
{
if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //6
{
if(TimeCurrent()-OrderOpenTime()>period2*CloseAfterCandles) //7
{
CloseAllOrders(); //8
}
}
}
return;
}
[itemlink post_id=”2273″]
この記事は【EAの教科書シリーズ】の一部です。全記事は以下から確認できます。
これまでに別記事でEAにトレードを行わせるために必要な様々な関数を紹介してきました。本記事ではその中でも最も重要といっても過言でない実際にトレードを行わせるための関数を紹介していきます。
MT4をまだダウンロードしていない方はこちらのページでダウンロード方法を案内しているので、事前にMT4を用意しておいてください。
今回作成していく関数の名前はシンプルにTradeとします。戻り値はなく、引数にはint型のDirectionをとり、買い注文か売り注文かを指定するために使います。今回は0は買い注文、1は売り注文を表すことにします。
また今回作成するTradeには実践的なオプションがあり、ストップロスを設定するか、利確ポイントを設定するか、決済注文をリスクリワードレシオ(得られる収益と取らなければいけないリスクの割合)で指定するか、注文量を口座にある証拠金に対する割合で指定するかなどを設定できるようにします。
void Trade(int Direction) //1
{
double SL; //2
double TP; //3
double Equity=AccountEquity(); //4
double RiskedAmount=Equity*RiskPercent*0.01; //5
double Lots=0; //6
//買い注文の場合
if(Direction==0) //7
{
if(UseStopLoss) //8
{
SL=Bid-StopLoss*pips;
}
else
{
SL=0;
}
if(UseTakeProfit) //9
{
if(UseRiskReward && UseStopLoss) //10
{
TP=StopLoss*pips*RewardRatio+Bid;
}
else
{
TP=Bid+TakeProfit*pips;
}
}
else
{
TP=0;
}
if(UsePositionSizing && UseStopLoss) //11
{
Lots=(RiskedAmount/StopLoss)/10;
}
else
{
Lots=LotSize;
}
int res=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,NormalizeDouble(SL,4),NormalizeDouble(TP,4),NULL,0,0,clrNONE); //12
}
//売り注文の場合
if(Direction==1)
{
if(UseStopLoss) //8
{
SL=Ask+StopLoss*pips;
}
else
{
SL=0;
}
if(UseTakeProfit) //9
{
if(UseRiskReward && UseStopLoss) //10
{
TP=Ask-StopLoss*pips*RewardRatio;
}
else
{
TP=Ask-TakeProfit*pips;
}
}
else
{
TP=0;
}
if(UsePositionSizing && UseStopLoss) //11
{
Lots=(RiskedAmount/StopLoss)/10;
}
else
{
Lots=LotSize;
}
int res=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,NormalizeDouble(SL,4),NormalizeDouble(TP,4),NULL,0,0,clrNONE); //12
}
return;
}
上から順番に何をおこなっているのか解説していきます。
グローバルエリアは以下のようになっています。
extern int TakeProfit=50;
extern int StopLoss=25;
extern double LotSize=0.01;
extern bool UseBreakEven=true;
extern int MoveToBreakEven=40;
extern int PipsProfitLock=20;
double pips;
extern bool UseTrailingStop=true;
extern int TrailTrigger=50;
extern int TrailAmount=50;
extern bool UseStopLoss=true;
extern bool UseTakeProfit=true;
extern bool UseRiskReward=true;
extern bool UsePositionSizing=true;
extern int RiskPercent=1;
extern int RewardRatio=2;
この関数はエントリータイミングが来たタイミングで発注するための関数ですので、エントリータイミングを見極めるための関数と組み合わせて使用します。
エントリータイミングを見極める関数については以下をご覧ください。
[itemlink post_id=”2273″]
この記事は【EAの教科書シリーズ】の一部です。全記事は以下から確認できます。
MT4をまだダウンロードしていない方はこちらのページでダウンロード方法を案内しているので、事前にMT4を用意しておいてください。
はじめにトレーリングストップについてカンタンに説明したいと思います、
トレーリングストップとは、利益を大きく、損失を小さくするために編み出された取引戦略の一つで、マーケットの価格変動に合わせて損切りポイントを変更する仕組みのことを言います。トレール(trail)には追従といった意味があり、価格の変動に追従して損切りポイントを変更することからこの名前が付きました。
たとえば101円でドル円を買いエントリーし、1円下の100円を損切りポイントとしました。その後、ドル円の価格はあなたの思惑通り上昇し、102円に達しました。常に1円下に損切りポイントを設定したいので、次は損切りポイントを100円から101円に変更しました。ここで暴落したとしても100円で決済されるので、損失を出さずに済みます。これがトレーリングストップの考え方です。
同じように103円に達したら102円が損切りポイントになるので、仮に103円まで達したあとで暴落が始まったとしても102円で決済され、1円分の利益を確保することができます。
売り注文の場合はこれの逆だと考えてください。
関数名はTrailingStop()とします。戻り値はありません。
冒頭でも説明したように、トレーリングストップを自動で行うための関数です。例えば買いエントリーの場合であれば、現在の売値(Bid)の50pips下に損切りポイントを起きたいとします。売値が50pips上がった時点で、損切りポイントも合わせて50pips上にずらされます。
売りエントリーであれば、買値が50pips下落したら損切りポイントも50pips下にずらされます。
これらは全てグローバル変数として宣言します。またexternで宣言することによって、ストラテジーテスター実行時に値を自由に設定できるので、EAの最適化を行う際に便利です。
それではTrailingStop()関数のコードを確認していきましょう。
void TrailingStop() //1
{
for(int i=OrdersTotal();i>0;i--) //2
{
if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //3
{
if(OrderType()==OP_BUY)//4
{
if(Bid-OrderOpenPrice()>TrailTrigger*pips) //5
{
if(OrderStopLoss()<Bid-TrailAmount*pips) //6
{
bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),Bid-TrailAmount*pips,OrderTakeProfit(),0,clrNONE); //7
}
}
}
if(OrderType()==OP_SELL)
{
if(OrderOpenPrice()-Bid>TrailTrigger*pips)
{
if(OrderStopLoss()>Bid+TrailAmount*pips)
{
bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),Bid+TrailAmount*pips,OrderTakeProfit(),0,clrNONE);
}
}
}
}
}
return;
}
買い注文の場合の処理が終わったら売り注文のチェックを行っています。処理はほぼ同じですが、損切りポイントの計算方法が買い注文の場合と逆になっていることに注意してください。
現在までのグローバル変数は以下のようになっています。
extern int TakeProfit=50;
extern int StopLoss=25;
extern double LotSize=0.01;
extern bool UseBreakEven=true;
extern int MoveToBreakEven=40;
extern int PipsProfitLock=20;
double pips;
extern bool UseTrailingStop=true;
extern int TrailTrigger=50;
extern int TrailAmount=50;
別記事で紹介した関数用の変数なども含まれているので、興味がある方は確認してみてください。FX自動売買(EA)カテゴリから別記事を参照できます。
void OnTick()
{
//---
if(IsNewCandle()){
if(TotalOpenOrders()<1){
NewOrder();
}
if(TotalOpenOrders()>0){
if(UseBreakEven){
BreakEven();
}
if(UseTrailingStop){
TrailingStop();
}
}
}
}
この関数もBreakEven()関数と同じく、OnTick()関数の中でトレードが一つでも存在し、UseTrailingStop変数がtrueの場合に実行するように設定しています。
BreakEven()関数に関しては以下の記事を御覧ください。
最後にここまでのコード全体を載せておきます。
//+------------------------------------------------------------------+
//| test.mq4 |
//| Kanrinin |
//| https://codelabsjp.net |
//+------------------------------------------------------------------+
#property copyright "Kanrinin"
#property link "https://codelabsjp.net"
#property version "1.00"
#property strict
extern int TakeProfit=50;
extern int StopLoss=25;
extern double LotSize=0.01;
extern bool UseBreakEven=true;
extern int MoveToBreakEven=40;
extern int PipsProfitLock=20;
double pips;
extern bool UseTrailingStop=true;
extern int TrailTrigger=50;
extern int TrailAmount=50;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
PipsFunction();
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//---
if(IsNewCandle()){
if(TotalOpenOrders()<1){
NewOrder();
}
if(TotalOpenOrders()>0){
if(UseBreakEven){
BreakEven();
}
if(UseTrailingStop){
TrailingStop();
}
}
}
}
//+------------------------------------------------------------------+
void NewOrder()
{
int Result=OrderSend(Symbol(),OP_BUY,LotSize,Ask,3,NormalizeDouble(Ask-StopLoss*pips,4),NormalizeDouble(Ask+TakeProfit*pips,4),NULL,0123,0,clrNONE);
return;
}
bool IsNewCandle()
{
static int BarsOnChart=0;
if(Bars==BarsOnChart){
BarsOnChart = Bars;
return(false);
}
BarsOnChart = Bars;
return(true);
}
int TotalOpenOrders()
{
int Orders=0;
int Total=OrdersTotal();
for(int i=Total; i>0; i--)
{
bool res=OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES);
if(OrderType()==OP_BUY || OrderType()==OP_SELL)
{
Orders++;
}
}
return(Orders);
}
void CloseAllOrders() //1
{
int Total=OrdersTotal(); //2
for(int i=Total;i>0;i--) //3
{
if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //4
{
if(OrderType()==OP_SELL) //5
{
bool res1=OrderClose(OrderTicket(),OrderLots(),Ask,3,clrNONE); //6
}
if(OrderType()==OP_BUY) //7
{
bool res2=OrderClose(OrderTicket(),OrderLots(),Bid,3,clrNONE); //8
}
if(OrderType()==OP_BUYLIMIT || OrderType()==OP_BUYSTOP || OrderType()==OP_SELLLIMIT || OrderType()==OP_SELLSTOP) //9
{
bool res3=OrderDelete(OrderTicket(),clrNONE); //10
}
}
}
return;
}
void PipsFunction() //1
{
double ticksize=MarketInfo(Symbol(),MODE_TICKSIZE); //2
if(ticksize == 0.00001) //3
{
pips = ticksize*10; //4
}
else
{
pips = ticksize; //5
}
return;
}
void BreakEven() //1
{
for(int i=OrdersTotal();i>0;i--) //2
{
if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //3
{
if(OrderType()==OP_BUY) //4
{
if(Bid-OrderOpenPrice()>MoveToBreakEven*pips) //5
{
if(OrderOpenPrice()>OrderStopLoss()) //6
{
bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()+PipsProfitLock*pips,OrderTakeProfit(),0,clrNONE); //7
Alert("Yes");
}
}
}
if(OrderType()==OP_SELL)
{
if(OrderOpenPrice()-Ask>MoveToBreakEven*pips)
{
if(OrderOpenPrice()<OrderStopLoss())
{
bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()-PipsProfitLock*pips,OrderTakeProfit(),0,clrNONE);
}
}
}
}
}
}
void TrailingStop() //1
{
for(int i=OrdersTotal();i>0;i--) //2
{
if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //3
{
if(OrderType()==OP_BUY)//4
{
if(Bid-OrderOpenPrice()>TrailTrigger*pips) //5
{
if(OrderStopLoss()<Bid-TrailAmount*pips) //6
{
bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),Bid-TrailAmount*pips,OrderTakeProfit(),0,clrNONE); //7
}
}
}
if(OrderType()==OP_SELL)
{
if(OrderOpenPrice()-Bid>TrailTrigger*pips)
{
if(OrderStopLoss()>Bid+TrailAmount*pips)
{
bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),Bid+TrailAmount*pips,OrderTakeProfit(),0,clrNONE);
}
}
}
}
}
return;
}
別記事で紹介した関数なども含まれているので、上記コードだけで理解するのが難しい方はFX自動売買(EA)カテゴリに含まれている記事を日付順に読んでいくことをおすすめします。
[itemlink post_id=”2350″]
プログラミング学習はどうしても一人だとつまづいてしまう時がきます。調べればわかることも少なくないですが、最初のうちは調べ方もわからないことが多いため、あまり効率的ではありません。
効率的かつ挫折せずにプログラミングを学習したい方はスクールを検討してみるのも一つの手です。
中には無料で通えるスクールや、就職保証をしてくれるスクールなどもあるので、きっとあなたの目的に応じて最適のスクールが見つかります!以下の記事で評判がよく特におすすめのスクールをいくつかピックアップしているので、スクール選びで後悔したくない方は御覧ください!
この記事は【EAの教科書シリーズ】の一部です。全記事は以下から確認できます。
MT4をまだダウンロードしていない方はこちらのページでダウンロード方法を案内しているので、事前にMT4を用意しておいてください。
今回作っていく関数の名前はBreakEven()とします。Break evenは収支がトントンになるといったような意味があります。この関数は実行中のトレードが一定の利益を出したら、あらかじめ指定した価格をストップロスに設定し、確実に利益を確保・または損失を出さないようにするために使います。
この関数は実行中のトレードがあるときにのみ、毎ティック実行されます。ですのでOnTick()関数の中でif文で実行中のトレードがあるかを確認し、ある場合にはBreakEven()関数を実行するようにします。
また今回はこの関数自体を使うかどうかを設定できるようにするためのbool値をグローバルエリアにextern(EA実行時に値を変更できる)で宣言しておきます。デフォルト値はtrueに設定します。
グローバルエリアには他にextern int型のMoveToBreakEven、extern int型のPipsProfitLockがあります。
MoveToBreakEvenはいくらのpip数分の利益が出ていれば損切りポイントを変更するかを指定します。
PipsProfitLockはいくらの利益を最低限確保したいかを指定するために使います。0である場合はトントンを意味し、20である場合は確実に20pipsの利益は確保できることを意味します。
つまり、MoveToBreakEvenが40でPipsProfitLockが20である場合、40pips以上の利益が出ている状態であれば、20pipsの利益は確保できるように損切りポイントを変更するということになります。即座に決済注文が出されるわけではないので、さらに含み益が伸びる可能性もありますし、好ましくない方向に市場価格が動いても確実に20pipsの利益は確保できます。
それでは実際にBreakEven()関数のコードを紹介していきます。
void BreakEven() //1
{
for(int i=OrdersTotal();i>0;i--) //2
{
if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //3
{
if(OrderType()==OP_BUY) //4
{
if(Bid-OrderOpenPrice()>MoveToBreakEven*pips) //5
{
if(OrderOpenPrice()>OrderStopLoss()) //6
{
bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()+PipsProfitLock*pips,OrderTakeProfit(),0,clrNONE); //7
Alert("Yes");
}
}
}
if(OrderType()==OP_SELL)
{
if(OrderOpenPrice()-Bid>MoveToBreakEven*pips)
{
if(OrderOpenPrice()<OrderStopLoss())
{
bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()-PipsProfitLock*pips,OrderTakeProfit(),0,clrNONE);
}
}
}
}
}
}
それではコメントにふった番号に沿って説明していきます。
pips変数については以前の記事で解説しているので、詳しく知りたい方は以下の記事を御覧ください。
extern int TakeProfit=50;
extern int StopLoss=25;
extern double LotSize=0.01;
extern bool UseBreakEven=true;
extern int MoveToBreakEven=40;
extern int PipsProfitLock=20;
double pips;
上のコードは宣言している全てのグローバル変数になります。pips変数には何も値が代入されていませんが、事前に定義したPipsFunction()によって適切な値がプログラム実行時に代入されるので、問題ありません。
以前の記事ではStopLossやTakeProfitには0.0050や0.0025のような値が入っていましたが、これもpips変数を使って適切な値に変換できるので、整数値に変更しています。
また以前作成したNewOrder()関数内でもNomalizeDouble()関数を使ってすべてを小数点以下4桁に変換しています。NewOrder()関数については以下の記事を御覧ください。
最後にコード全体を載せておきます。
//+------------------------------------------------------------------+
//| test.mq4 |
//| Kanrinin |
//| https://codelabsjp.net |
//+------------------------------------------------------------------+
#property copyright "Kanrinin"
#property link "https://codelabsjp.net"
#property version "1.00"
#property strict
extern int TakeProfit=50;
extern int StopLoss=25;
extern double LotSize=0.01;
extern bool UseBreakEven=true;
extern int MoveToBreakEven=40;
extern int PipsProfitLock=20;
double pips;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
PipsFunction();
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//---
if(IsNewCandle()){
if(TotalOpenOrders()<1){
NewOrder();
}
if(TotalOpenOrders()>0){
if(UseBreakEven){ //BreakEvenを使うかのチェック
BreakEven();
}
}
}
}
//+------------------------------------------------------------------+
void NewOrder()
{
int Result=OrderSend(Symbol(),OP_BUY,LotSize,Ask,3,NormalizeDouble(Ask-StopLoss*pips,4),NormalizeDouble(Ask+TakeProfit*pips,4),NULL,0123,0,clrNONE);
return;
}
bool IsNewCandle()
{
static int BarsOnChart=0;
if(Bars==BarsOnChart){
BarsOnChart = Bars;
return(false);
}
BarsOnChart = Bars;
return(true);
}
int TotalOpenOrders()
{
int Orders=0;
int Total=OrdersTotal();
for(int i=Total; i>0; i--)
{
bool res=OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES);
if(OrderType()==OP_BUY || OrderType()==OP_SELL)
{
Orders++;
}
}
return(Orders);
}
void CloseAllOrders() //1
{
int Total=OrdersTotal(); //2
for(int i=Total;i>0;i--) //3
{
if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //4
{
if(OrderType()==OP_SELL) //5
{
bool res1=OrderClose(OrderTicket(),OrderLots(),Ask,3,clrNONE); //6
}
if(OrderType()==OP_BUY) //7
{
bool res2=OrderClose(OrderTicket(),OrderLots(),Bid,3,clrNONE); //8
}
if(OrderType()==OP_BUYLIMIT || OrderType()==OP_BUYSTOP || OrderType()==OP_SELLLIMIT || OrderType()==OP_SELLSTOP) //9
{
bool res3=OrderDelete(OrderTicket(),clrNONE); //10
}
}
}
return;
}
void PipsFunction() //1
{
double ticksize=MarketInfo(Symbol(),MODE_TICKSIZE); //2
if(ticksize == 0.00001) //3
{
pips = ticksize*10; //4
}
else
{
pips = ticksize; //5
}
return;
}
void BreakEven() //1
{
for(int i=OrdersTotal();i>0;i--) //2
{
if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //3
{
if(OrderType()==OP_BUY) //4
{
if(Bid-OrderOpenPrice()>MoveToBreakEven*pips) //5
{
if(OrderOpenPrice()>OrderStopLoss()) //6
{
bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()+PipsProfitLock*pips,OrderTakeProfit(),0,clrNONE); //7
Alert("Yes");
}
}
}
if(OrderType()==OP_SELL)
{
if(OrderOpenPrice()-Ask>MoveToBreakEven*pips)
{
if(OrderOpenPrice()<OrderStopLoss())
{
bool res1=OrderModify(OrderTicket(),OrderOpenPrice(),OrderOpenPrice()-PipsProfitLock*pips,OrderTakeProfit(),0,clrNONE);
}
}
}
}
}
}
[itemlink post_id=”2361″]
プログラミング学習はどうしても一人だとつまづいてしまう時がきます。調べればわかることも少なくないですが、最初のうちは調べ方もわからないことが多いため、あまり効率的ではありません。
効率的かつ挫折せずにプログラミングを学習したい方はスクールを検討してみるのも一つの手です。
中には無料で通えるスクールや、就職保証をしてくれるスクールなどもあるので、きっとあなたの目的に応じて最適のスクールが見つかります!以下の記事で評判がよく特におすすめのスクールをいくつかピックアップしているので、スクール選びで後悔したくない方は御覧ください!
MT4をまだダウンロードしていない方はこちらのページでダウンロード方法を案内しているので、事前にMT4を用意しておいてください。
通貨ペアの中には価格を表示するのに4桁の数値を使っているところや5桁の数値を使っているところなど、ペアによって価格の表記方法が異なります。つまり同じ価格でも1.5000のように表示されていたり、1.50000のように表示されている場合があるのです。もしpipsを表すために、pips=0.0001などという風に固定していると、別の通貨ペアによっては1pipsを表すつもりが10pipsになってしまったりする場合もあります。
取引通貨ペアによって異なるそれらの表示形式に影響されずに、pips情報を取得するには、小数点以下が4桁の場合には0.0001を取得し、5桁の場合には0.00001を取得するようにできる関数が必要になってきます。今回はその役割を担うPipsFunction()という関数を作成していきます。
今回は先におおまかな関数の仕組みをざっくりと説明します。
実装にあたって、double型のグローバル変数pipsを宣言します。pipsには通貨ペアごとに1pipsをあらわす値を代入しますが、この値が毎回異なるので、それを今回の関数の中で適切な値が代入されるようにしていきます。
重要な点として、この変数にint型の変数をかけることによって、求めたいpipsに変換することができるということです。例えばこれを使って損切りや利確のための幅をpipsで指定することができるようになります。
あとから値を自由に変更できるexternのint型の変数StopLossに50を代入しておけば、50pipsで損切りさせることができるようになります。ただしこのままではただ整数の50を表す変数なので、この変数と変数pipsをかけることで、実際に50pipsを表す値を生成することができます。
この関数はOnInit()関数内で利用し、プログラム起動時に一度のみ実行されるようにします。この関数の最終的な目的はグローバルエリアに宣言したpipsという変数に適切な値を代入し、StopLossやTakeProfitなどがちゃんとpipsを表せるようにすることです。
以下がPipsFunction()のコードになります。
void PipsFunction() //1
{
double ticksize=MarketInfo(Symbol(),MODE_TICKSIZE); //2
if(ticksize == 0.00001) //3
{
pips = ticksize*10; //4
}
else
{
pips = ticksize; //5
}
return;
}
順番に解説していきます
extern double TakeProfit=0.0050;
extern double StopLoss=0.0025;
extern double LotSize=0.01;
double pips = 0; //ここでグローバル変数としてpipsを宣言している
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
PipsFunction(); //OnInit内でプログラム起動時に一度だけ実行
//---
return(INIT_SUCCEEDED);
}
pips変数は他の関数内でも使われる変数なので、グローバルエリア(どの関数にも所属していない場所)に宣言することで、グローバル変数としてどの関数内でも使うことができるようになります。
この関数は最初に一度だけ実行できればいいので、OnInit()内におきます。プログラムの初期化時に実行され、pips変数に適切な値が代入されることで、他の関数内で損切り値や利確値を計算させるのに使うことができるようになります。
例えば、プログラムの起動前、pips変数には0が入っています。ストラテジーテスターでこのEAを起動するとプログラムが実行され、OnInit()関数が完了したときにPipsFunction()が呼び出され、pips変数に値が代入されます。
今回は通貨ペアごとに桁数が異なる場合でもpipsを合わせるための関数、PipsFunction()の作り方を解説しました。
この関数はOnInit()内で初期化時に一度だけ実行させれば、pips変数に適切な値が代入され、正しい損切りや利確値を計算することができます。
[itemlink post_id=”2350″]
この記事は【EAの教科書シリーズ】の一部です。全記事は以下から確認できます。
MT4をまだダウンロードしていない方はこちらのページでダウンロード方法を案内しているので、事前にMT4を用意しておいてください。
今回作成していく関数は全ての実行中のトレードを決済し、待機中の注文も全てキャンセルするための関数になります。関数名はCloseAllOrders()とします。
先に関数のコードを紹介したあとで順番に解説していきます。
void CloseAllOrders() //1
{
int Total=OrdersTotal(); //2
for(int i=Total;i>0;i--) //3
{
if(OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES)) //4
{
if(OrderType()==OP_SELL) //5
{
bool res1=OrderClose(OrderTicket(),OrderLots(),Ask,3,clrNONE); //6
}
if(OrderType()==OP_BUY) //7
{
bool res2=OrderClose(OrderTicket(),OrderLots(),Bid,3,clrNONE); //8
}
if(OrderType()==OP_BUYLIMIT || OrderType()==OP_BUYSTOP || OrderType()==OP_SELLLIMIT || OrderType()==OP_SELLSTOP) 9
{
bool res3=OrderDelete(OrderTicket(),clrNONE); //10
}
}
}
return;
}
コメント部分の数字順に何を行っているか解説していきます。
今回は全ての注文を決済・キャンセルする関数の作り方を解説しました。
またこの関数を使うタイミングは各々のトレード戦略によって異なりますが、ローソク足が何本かでき終わるタイミングで行わせたりなどが考えられます。それらについては今後解説していこうと思います。
[itemlink post_id=”2350″]
この記事は【EAの教科書シリーズ】の一部です。全記事は以下から確認できます。
MT4をまだダウンロードしていない方はこちらのページでダウンロード方法を案内しているので、事前にMT4を用意しておいてください。
上の記事で作成したコードに付け足していく形で解説していきます。先に読んでおくとより理解しやすいかもしれませんが、今回解説していく内容とは切り離せるので、以下のコードを使うことだけさらっと目を通して確認しておければと思います。
前回までのコード:
//+------------------------------------------------------------------+
//| test.mq4 |
//| Kanrinin |
//| https://codelabsjp.net |
//+------------------------------------------------------------------+
#property copyright "Kanrinin"
#property link "https://codelabsjp.net"
#property version "1.00"
#property strict
extern double TakeProfit=0.0050;
extern double StopLoss=0.0025;
extern double LotSize=0.01;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//---
if(IsNewCandle()){
NewOrder();
}
}
//+------------------------------------------------------------------+
void NewOrder()
{
int Result=OrderSend(Symbol(),OP_BUY,LotSize,Ask,3,Ask-StopLoss,Ask+TakeProfit,NULL,0123,0,clrNONE);
return;
}
bool IsNewCandle()
{
static int BarsOnChart=0;
if(Bars==BarsOnChart){
BarsOnChart = Bars;
return(false);
}
BarsOnChart = Bars;
return(true);
}
今回は既に注文しているトレードの数を調べる関数を作成していきます。
目的は一度に成行注文を複数回出さないようにするために現在までの成行注文の数を知ることです。
関数名はTotalOpenOrders()としましょう。先に関数の中身を公開しておきます。
int TotalOpenOrders()
{
int Orders=0;
int Total=OrdersTotal();
for(int i=Total; i>0; i--)
{
bool res=OrderSelect(i-1,SELECT_BY_POS,MODE_TRADES);
if(OrderType()==OP_BUY || OrderType()==OP_SELL)
{
Orders++;
}
}
return(Orders);
}
この関数の戻り値は注文数なので、戻り値の型はintにしてください。それでは順番に解説していきます。
TotalOpenOrders()についての解説が終わったので、具体的な使い方について説明します。
この関数はOnTick()の中の前回作成したIsNewCandle()がtrueの場合に呼び出し、注文が存在しない場合、つまりTotalOpenOrders()<1である場合のみトレードを行わせるようにします。そうすることで一度に複数のトレードが行われることを回避できます。
確認:IsNewCandle()は現在のチャート上に新しいローソク足が作成されたかを確認するための関数です。時間軸を一時間にしている場合は一時間ごとにtrueを返します。
void OnTick()
{
//---
if(IsNewCandle()){
if(TotalOpenOrders()<1){
NewOrder();
}
}
}
現在行われているトレードの合計数を確認するための関数TotalOpenOrders()について解説しました。この関数は現在行われているトレードの合計数を返すもので、これが0である場合はトレードが何も行われていないということになります。
自動売買で注文を一つずつ行わせたい場合はこの関数を利用して、値が1未満(=0)である場合のみ注文を出す処理を行わせるようにしましょう。
[itemlink post_id=”2273″]