【EA開発ガイド】Part 3 MQL4開発基本 – Chapter 4 エントリーから決済までをプログラムで実行する

EAを使ったエントリーと決済のサンプルコードを紹介する。エントリーは引数が多くてわかりにくいがそれほど難しい処理はない。サンプルではポジションを1つだけ保有する仕組みなので複数のポジションを保有したい場合は別途修正が必要となる。実用的なコードではないが自動売買の雰囲気がわかればいいと思う。

注意事項

本記事はFXの自動売買に使用されるプログラムについて解説していますが内容を保証するものではありません。金銭にかかわる内容であるためご注意ください。参考にする場合は自己責任でお願いします。

留意事項

本記事ではMT4を使用する。

実行環境

FXDD – MetaTrader
・Version: 4.00 build 1322
・8Feb 2021

MetaEditor
・Version 5.00 build 2375
・7 Jan 2021

エントリーと決済

自動売買の基本となるエントリーと決済のサンプルコード。

エントリー

ストップとリミットは証券会社で非対応のところがあるようなので基本は0を設定し使用しない。復帰値のチケットナンバーは決済処理や注文の確認で使用する。後の引数は特に難しいところはない。

// 新規注文
ticketNumber = OrderSend(
    Symbol(),       // 通貨ペア
    orderType,      // オーダータイプ(OP_BUY or OP_SELL)
    lotSize,        // ロット
    price,          // 注文価格
    slippage,       // スリップ上限
    0,              // ストップロス価格
    0,              // リミット価格
    orderComment,   // オーダーコメント
    magicNumber     // マジックナンバー
);

 

決済

決済処理はオーダー選択と注文クローズの2つの処理に分かれる。

まずはオーダー選択。オーダーを選択することでオーダーの情報を取得できる。

// オーダーを選択
ret = OrderSelect(
    ticketNumber,
    SELECT_BY_TICKET,
    MODE_TRADES
);

 

次はクローズ。選択中の注文をクローズする。スリッページ以外は提携の関数を呼び出すだけなので簡単。

// 注文をクローズ
ret = OrderClose(
    OrderTicket(),       // 選択中のチケット番号
    OrderLots(),         // 選択中のロット
    OrderClosePrice(),   // 選択中の価格
    slippage             // スリッページ
);

 

エントリーと決済のサンプルコード

エントリーと決済の関数の使い方を確認する。

(1) エントリーと決済

mql4の仕様をまだちゃんと理解していないので上手くカプセル化できていない。多少冗長だがEA規模のプログラムならこれで何とかなるだろう。エントリーに失敗したらEAを強制終了する作りにしている。基本的にエントリーでエラーはでない想定なので万が一エラーが起きたら止めた方がいい。

example-entry.mqh

//+------------------------------------------------------------------+
//|                                                example-entry.mqh |
//|                                   Copyright 2021, zerokara-blog. |
//|                                       https://zerokara-blog.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, zerokara-blog."
#property link      "https://zerokara-blog.com/"
#property strict

double lotSize = 0.1;               // オーダーロット数(0.1で1万通貨)
int slippage = 10;                  // スリッページ(10で1pips)
int magicNumber = 0;                // マジックナンバー
string orderComment = "Example";    // オーダーコメント
int ticketNumber = 0;               // チケット番号
int presentOrderType;               // エントリー中のオーダータイプ

void entryFunction(int orderType);  // エントリー
void selectOrder();                 // 注文を選択
void closeFunction();               // 注文をクローズ
double getOrderProfit();            // 含み益(損)を取得
void closeAllFunction();            // すべての注文をクローズ
void abend();                       // プログラムを強制終了する

// エントリー
void entryFunction(int orderType)
{
    double price;
    
    presentOrderType = orderType;
    
    if(OP_BUY == orderType)
    {
        price = Ask;
    }
    else
    {
        price = Bid;
    }
    
    // 新規注文
    ticketNumber = OrderSend(
        Symbol(),       // 通貨ペア
        orderType,      // オーダータイプ(OP_BUY or OP_SELL)
        lotSize,        // ロット
        price,          // 注文価格
        slippage,       // スリップ上限
        0,              // ストップロス価格
        0,              // リミット価格
        orderComment,   // オーダーコメント
        magicNumber     // マジックナンバー
    );
    
    // エラーが発生したらEAを強制終了する
    if(ticketNumber == -1)
    {
        abend();
    }
    
}

// 注文を選択
void selectOrder()
{
    bool ret;

    // オーダーを選択
    ret = OrderSelect(
        ticketNumber,
        SELECT_BY_TICKET,
        MODE_TRADES
    );

    if(ret == false)
    {
        Print("オーダーの選択に失敗しました。");
    }
}

// 注文をクローズ
void closeFunction()
{
    // オーダーを選択
    selectOrder();

    bool ret;

    // 注文をクローズ
    ret = OrderClose(
        OrderTicket(),       // 選択中のチケット番号
        OrderLots(),         // 選択中のロット
        OrderClosePrice(),   // 選択中の価格
        slippage             // スリッページ
    );
     
    if(ret == false)
    {
        Print("注文のクローズに失敗しました。");
    }
}

// 含み益(損)を取得
double getOrderProfit()
{
    // オーダーを選択
    selectOrder();
    return OrderProfit();
    
}

// すべての注文をクローズ
void closeAllFunction()
{
    bool ret;
    
    for(int i = OrdersTotal() -1; i >=0; i--)
    {
        // オーダーを選択
        ret = OrderSelect(i,SELECT_BY_POS , MODE_TRADES);
        
        if(ret == false)
        {
            Print("注文のクローズ処理に失敗しました。(オーダーの取得)");
            continue;
        }
        
        // 選択したオーダーをクローズ
        ret = OrderClose(
            OrderTicket(),       // 選択中のチケット番号
            OrderLots(),         // 選択中のロット
            OrderClosePrice(),   // 選択中の価格
            slippage             // スリッページ
        );
        
        if(ret == false)
        {
            Print("注文のクローズ処理に失敗しました。(オーダーのクローズ)");
        }

    }
}

// プログラムを強制終了する
void abend()
{
    Print("EAを強制終了します。");
    ExpertRemove();
}

 

(2) 呼び出し処理

常にショートし続けるプログラム。損益が100円になったら決済し再度エントリーする。意味のない仕組みで適当に作ったが異常に勝率が悪い。ランダムウォークといってもトレンドが発生するとどちらかに傾くのだろうか。

example-entry.mq4

//+------------------------------------------------------------------+
//|                                                example-entry.mq4 |
//|                                   Copyright 2021, zerokara-blog. |
//|                                       https://zerokara-blog.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, zerokara-blog."
#property link      "https://zerokara-blog.com/"
#property version   "1.00"
#property strict

#include <example-entry.mqh>

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)

{
//---
    // すべての注文をクローズ
    closeAllFunction();
   
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
//---
    // エントリー
    if(OrdersTotal() == 0)
    {
        // 売りでエントリー
        entryFunction(OP_SELL);
    }
    else
    {
        // 100円の利益か損失で決済
        if(getOrderProfit() >= 100 || getOrderProfit() <= -100)
        {
            // 決済
            closeFunction();
        }
    }   
}
//+------------------------------------------------------------------+

 

(3) 実行結果の確認

期間を2020年1月1日から2020年12月27日に設定してバックテスト実行。

適当なプログラムだが勝率5割。確率は収束するのだろうか。

まとめ

エントリー関数の引数が多いので敬遠していたが実際コードを書いてみるとそれほど難しくなかった。ナンピンや複数ポジション持つトレードは好きではないので常に1エントリーの仕組みえしばらく進めようと思う。

ポイント
  • ポジションを一つだけ保有するプログラムは簡単に書ける
  • エントリー関数の引数はそれほど難しくない
  • 適当なエントリープログラムだと全く勝てない

とりあえずエントリーが出来るようになったので次回からエントリーロジックを考えたいと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です