第2回 FXの自動売買シミュレーションを試してみる(毎日同時刻にエントリー・決済)

FX

テストプログラム作成

ということで、書籍やWebサイトを参考に、まずは毎日同じ時刻にエントリーして、同じ時刻に決済する自動売買プログラムを作りました。

メタトレーダー4はC言語によく似たMQL4言語というプログラム言語でプログラムを記述します。ポインタが使えないなどC言語ほど自由度は無く、その一方でC言語では実装が煩雑だった文字列処理は実装しやすく作られており、ハードルの高めのプログラム言語であるC言語ベースとはいいながらも、思ったよりも敷居は低いと思います。

全体のソースは以下となります。ソースの後に説明をします。結果だけが気になる方は第3回の記事をご覧ください。 

ソース全体


//+------------------------------------------------------------------+
//|                                                FXexp_sametime.mq4|
//|                                 Copyright (c) 2021, Langtainment |
//|                                    https://tsunagaru-info.com/dr |
//+------------------------------------------------------------------+
#property copyright "Copyright (c) 2021, Langtainment"
#property link      "https://tsunagaru-info.com/dr"

#include <stdlib.mqh>

//Magic Number
#define MAGIC_NUMBER  20211010

//SIGNAL
#define ENTRY_BUY_SIGNAL   1
#define ENTRY_SELL_SIGNAL  2
#define CLOSE_BUY_SIGNAL   4
#define CLOSE_SELL_SIGNAL  8


//Common Parameters
extern double Lots =  1;
extern int Slippage = 3;

//Unique Parameters
extern int Open_Time      = 420;//Value = Hour(24H)*60+Minutes
extern int Holding_Period = 720;//Value = Minutes
extern string Comments    = "Same Time Trade";
extern int Order_Mode     = 0; // 0:BUY Others:SELL

int ticket = 0;
int errorcode = 0;
int Adjusted_Slippage = 0;
datetime Bar_Time     = 0;
bool closed = false;

int Open_Hour     = 7;
int Open_Minute   = 0;
int Close_Hour    = 19;
int Close_Minute  = 0;


//+------------------------------------------------------------------+
//| Adjust Slippage according to Number of digits after the decimal point                                                 |
//+------------------------------------------------------------------+
int AdjustSlippage(string Currency,int Slippage_Pips)
{
   int Symbol_Digits = MarketInfo(Currency,MODE_DIGITS);
   
   if(Symbol_Digits == 2 || Symbol_Digits == 4)
   {
      int Calculated_Slippage = Slippage_Pips;
   } 
   else if(Symbol_Digits == 3 || Symbol_Digits == 5)
   {
      Calculated_Slippage = Slippage_Pips * 10;
   }
   else
   {
      //unreachable
   }  
   return(Calculated_Slippage);
   
}

//+------------------------------------------------------------------+
//| init                                                             |
//+------------------------------------------------------------------+
int init()
{
   Adjusted_Slippage = AdjustSlippage(Symbol(),Slippage);
   
   Open_Hour      = MathCeil(Open_Time / 60);
   Open_Minute    = MathMod(Open_Time,60);
   Close_Hour     = MathMod(MathCeil((Open_Time + Holding_Period) / 60),24);
   Close_Minute   = MathMod((Open_Time + Holding_Period),60); 
   
   return(0);
}

//+------------------------------------------------------------------+
//| Calculate Current Orders                                         |
//+------------------------------------------------------------------+
int CalculateCurrentOrders()
{
   //The number of Current Orders(+:BUY -:SELL)
   int pos=0;

   for(int i=0; i<OrdersTotal(); i++)
   {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) == false) break;
      if(OrderSymbol() == Symbol() && OrderMagicNumber() == MAGIC_NUMBER)
      {
         if(OrderType() == OP_BUY)  pos++;
         if(OrderType() == OP_SELL) pos--;
      }
   }
   return(pos);
}

//+------------------------------------------------------------------+
//| Close Positions                                             |
//+------------------------------------------------------------------+
void ClosePositions()
{
   for(int i=0; i<OrdersTotal(); i++)
   {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) == false) break;
      if(OrderMagicNumber() != MAGIC_NUMBER || OrderSymbol() != Symbol()) continue;
      //Check Order type 
      if(OrderType() == OP_BUY)
      {
         closed = OrderClose(OrderTicket(),OrderLots(),Bid,Adjusted_Slippage,White);
         if(closed==False)
         {
            errorcode=GetLastError(); 
            printf("OrderClose Error! error_code:%d , detail:%s ",errorcode,ErrorDescription(errorcode));
         }
         break;
      }
      else if(OrderType() == OP_SELL)
      {
         closed = OrderClose(OrderTicket(),OrderLots(),Ask,Adjusted_Slippage,White);
         if(closed==False)
         {
            errorcode=GetLastError(); 
            printf("OrderClose Error! error_code:%d , detail:%s ",errorcode,ErrorDescription(errorcode));
         }
         break;
      }
      else
      {
         //unreachable
      }
   }
}

//+------------------------------------------------------------------+
//| SIGNAL                                                      |
//+------------------------------------------------------------------+
int AnalyzeSignal()
{
   int flags = 0;
   
   if(Order_Mode==0)
   {
      if(Hour()==Close_Hour && Minute() == Close_Minute)
      {
         flags = CLOSE_BUY_SIGNAL;
      }  
      else if(Hour()==Open_Hour && Minute() == Open_Minute)
      {
         flags = ENTRY_BUY_SIGNAL;
      }
      else
      {
         //no operation
      }
   }
   else
   {
      if(Hour()==Close_Hour && Minute() == Close_Minute)
      {
         flags = CLOSE_SELL_SIGNAL;
      }  
      else if(Hour()==Open_Hour && Minute() == Open_Minute)
      {
         flags = ENTRY_SELL_SIGNAL;
      }
      else
      {
         //no operation
      }
   }      
 
   return flags;
   
}


//+------------------------------------------------------------------+
//| Main Function                                                    |
//+------------------------------------------------------------------+
int start(){
   //Check if allowable trade at opening-price
   if(Volume[0] > 1 || IsTradeAllowed() == false)
   {
      return(0);
   }

   //Calculate Current Orders
   int pos = CalculateCurrentOrders();
   
   //Check signal
   int signal = AnalyzeSignal();

   //BUY signal
   if(pos <= 0 && signal == ENTRY_BUY_SIGNAL)
   {
      ClosePositions();
      ticket = OrderSend(Symbol(),OP_BUY,Lots,Ask,Adjusted_Slippage,0,0,"",MAGIC_NUMBER,0,Blue);
      if(ticket < 0){
         errorcode=GetLastError();
         printf("Send Error! error_code:%d , detail:%s ",errorcode,ErrorDescription(errorcode));
      }
   }
   //SELL signal
   else if(pos >= 0 && signal == ENTRY_SELL_SIGNAL)
   {
      ClosePositions();
      ticket = OrderSend(Symbol(),OP_SELL,Lots,Bid,Adjusted_Slippage,0,0,"",MAGIC_NUMBER,0,Red);
      if(ticket < 0){
         errorcode=GetLastError();
         printf("Send Error! error_code:%d , detail:%s ",errorcode,ErrorDescription(errorcode));
      }
   }
   //Close BUY position
   else if(pos > 0  && signal == CLOSE_BUY_SIGNAL)
   {
      ClosePositions();
   }
   //Close SELL position
   else if(pos < 0  && signal == CLOSE_SELL_SIGNAL)
   {
      ClosePositions();
   }
   else
   {
      //no operation
   }
   return(0);
}

 

宣言部分

#include <stdlib.mqh> は、ErrorDescription関数を使うのに必要なインクルード宣言。

最適化しやすさを考慮して、エントリー時刻と決済時刻の指定方法は、エントリー時刻は24時間制で分単位で、決済時刻はエントリーしてから何分たったかで指定することにしました。

なので、Open_Time = 420 は420÷60=7でAM7時。Holding_Period = 720は、720分経ってから決済するということで、AM7時の12時間後のPM7時を指定しています。


#property copyright "Copyright (c) 2021, Langtainment"
#property link      "https://tsunagaru-info.com/dr"

#include <stdlib.mqh>

//Magic Number
#define MAGIC_NUMBER  20211010

//SIGNAL
#define ENTRY_BUY_SIGNAL   1
#define ENTRY_SELL_SIGNAL  2
#define CLOSE_BUY_SIGNAL   4
#define CLOSE_SELL_SIGNAL  8


//Common Parameters
extern double Lots =  1;
extern int Slippage = 3;

//Unique Parameters
extern int Open_Time      = 420;//Value = Hour(24H)*60+Minutes
extern int Holding_Period = 720;//Value = Minutes
extern string Comments    = "Same Time Trade";
extern int Order_Mode     = 0; // 0:BUY Others:SELL

int ticket = 0;
int errorcode = 0;
int Adjusted_Slippage = 0;
datetime Bar_Time     = 0;
bool closed = false;

int Open_Hour     = 7;
int Open_Minute   = 0;
int Close_Hour    = 19;
int Close_Minute  = 0;

スプレッド調整

提示レートが3桁又は5桁の業者において、提示レートが2桁又は4桁の業者と同様のpips単位で許容スリッページ数の設定を行うものです。

int AdjustSlippage(string Currency,int Slippage_Pips)
{
   int Symbol_Digits = MarketInfo(Currency,MODE_DIGITS);
   
   if(Symbol_Digits == 2 || Symbol_Digits == 4)
   {
      int Calculated_Slippage = Slippage_Pips;
   } 
   else if(Symbol_Digits == 3 || Symbol_Digits == 5)
   {
      Calculated_Slippage = Slippage_Pips * 10;
   }
   else
   {
      //unreachable
   }  
   return(Calculated_Slippage);
   
}

初期化関数

init関数は、本EA(エキスパートシステム)適用時や時間足変更時、プロパティ変更時、MT4再起動時のタイミングで動作最初に実行される関数。ここで、スリッページの値の調整と、エントリー時刻と決済時刻を、実際の時刻と比較できるように時分の形式に変更しておきます。

MathCeil()は切り捨て関数。Mathmod()は余りを求める関数。Close Hourがごちゃごちゃしてますが、エントリー時刻+保有時間が24時以上になった場合に24時間減らして0~23の値になるようにしています。

int init()
{
   Adjusted_Slippage = AdjustSlippage(Symbol(),Slippage);
   
   Open_Hour      = MathCeil(Open_Time / 60);
   Open_Minute    = MathMod(Open_Time,60);
   Close_Hour     = MathMod(MathCeil((Open_Time + Holding_Period) / 60),24);
   Close_Minute   = MathMod((Open_Time + Holding_Period),60); 
   
   return(0);
}


スタート関数

start関数は、適用したチャートの通貨ペアのレートが配信されるタイミング(1tick毎)に実行されます。ここでは、以下のようなことを書いてます。

  • まずはじめに、①EAを適用する時間足の開始時刻であることと②トレード可能な状態かを確認します。①はエントリー時刻と決済時刻を今は分単位でセットしているので、同じ分の場合に何度も何度も売買するのを避けるため。②は通信不良などで売買できない場合はこれ以上処理しても意味ないため。
  • CalculateCurrentOrders()で現在売買しているポジションの数を確認。正の値なら買いポジション、負の値なら売りポジションを持っていることを意味する。

  • Analyzesignal()でいま、エントリーすべきか、決済すべきかを計算。本関数内で今指定した時刻かどうかを確認します。(後述)

  • ポジションがなくエントリー時刻になればエントリー、ポジションがあって決済時刻になれば決済を実行。(決済の関数ClosePositions()は後述)

int start(){
   //Check if allowable trade at opening-price
   if(Volume[0] > 1 || IsTradeAllowed() == false)
   {
      return(0);
   }

   //Calculate Current Orders
   int pos = CalculateCurrentOrders();
   
   //Check signal
   int signal = AnalyzeSignal();

   //BUY signal
   if(pos <= 0 && signal == ENTRY_BUY_SIGNAL)
   {
      ClosePositions();
      ticket = OrderSend(Symbol(),OP_BUY,Lots,Ask,Adjusted_Slippage,0,0,"",MAGIC_NUMBER,0,Blue);
      if(ticket < 0){
         errorcode=GetLastError();
         printf("Send Error! error_code:%d , detail:%s ",errorcode,ErrorDescription(errorcode));
      }
   }
   //SELL signal
   else if(pos >= 0 && signal == ENTRY_SELL_SIGNAL)
   {
      ClosePositions();
      ticket = OrderSend(Symbol(),OP_SELL,Lots,Bid,Adjusted_Slippage,0,0,"",MAGIC_NUMBER,0,Red);
      if(ticket < 0){
         errorcode=GetLastError();
         printf("Send Error! error_code:%d , detail:%s ",errorcode,ErrorDescription(errorcode));
      }
   }
   //Close BUY position
   else if(pos > 0  && signal == CLOSE_BUY_SIGNAL)
   {
      ClosePositions();
   }
   //Close SELL position
   else if(pos < 0  && signal == CLOSE_SELL_SIGNAL)
   {
      ClosePositions();
   }
   else
   {
      //no operation
   }
   return(0);
}


シグナル関数

本関数は、エントリータイミングや決済タイミングになった際にシグナルを出す関数。今回は単純に指定した時刻になったらタイミングとなるので、そのように実装。

int AnalyzeSignal()
{
   int flags = 0;
   
   if(Order_Mode==0)
   {
      if(Hour()==Close_Hour && Minute() == Close_Minute)
      {
         flags = CLOSE_BUY_SIGNAL;
      }  
      else if(Hour()==Open_Hour && Minute() == Open_Minute)
      {
         flags = ENTRY_BUY_SIGNAL;
      }
      else
      {
         //no operation
      }
   }
   else
   {
      if(Hour()==Close_Hour && Minute() == Close_Minute)
      {
         flags = CLOSE_SELL_SIGNAL;
      }  
      else if(Hour()==Open_Hour && Minute() == Open_Minute)
      {
         flags = ENTRY_SELL_SIGNAL;
      }
      else
      {
         //no operation
      }
   }      
 
   return flags;
   
}




決済関数

本関数は、持っているポジションをすべて決済する関数です。保有ポジションすべてを確認し、買いポジションの場合は売り成行決済、売りポジションの場合は買い成行決済で手仕舞いします。

void ClosePositions()
{
   for(int i=0; i<OrdersTotal(); i++)
   {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) == false) break;
      if(OrderMagicNumber() != MAGIC_NUMBER || OrderSymbol() != Symbol()) continue;
      //Check Order type 
      if(OrderType() == OP_BUY)
      {
         closed = OrderClose(OrderTicket(),OrderLots(),Bid,Adjusted_Slippage,White);
         if(closed==False)
         {
            errorcode=GetLastError(); 
            printf("OrderClose Error! error_code:%d , detail:%s ",errorcode,ErrorDescription(errorcode));
         }
         break;
      }
      else if(OrderType() == OP_SELL)
      {
         closed = OrderClose(OrderTicket(),OrderLots(),Ask,Adjusted_Slippage,White);
         if(closed==False)
         {
            errorcode=GetLastError(); 
            printf("OrderClose Error! error_code:%d , detail:%s ",errorcode,ErrorDescription(errorcode));
         }
         break;
      }
      else
      {
         //unreachable
      }
   }
}

実際に動かした結果は・・

次回の記事で実際に動かした結果をお見せします。

(第3回へ続く)

コメント

タイトルとURLをコピーしました