恥は/dev/nullへ by 初心者

~ PC初心者による右往左往の記録 ~

新規の成行注文を出す関数(試作)

MQL5のヘルプを見ながら試作した成行注文を出す関数です。

新規の成行注文を出す関数(試作)

//  [留意点] ・現在チャートを想定しているので、_Symbol を使用
//           ・magicフィールドには #defineで定義する EXPERT_MAGICを使用

bool SendMarketOrder(ENUM_ORDER_TYPE         orderType,   // ORDER_TYPE_BUY 成行買い / ORDER_TYPE_SELL 成行売り
                     double                  orderVolume, // ロット数
                     double                  slPrice,     // SL価格
                     double                  tpPrice,     // TP価格
                     ENUM_ORDER_TYPE_FILLING fillPolicy,  // Fill policy
                     uint&                   recipient)   // リターンコードを受け取る変数
{
    // このSYMBOLのExecution mode
    ENUM_SYMBOL_TRADE_EXECUTION executionMode =
        (ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_EXEMODE);

    MqlTradeRequest request = {};

    // 宣言部分は、以下のように書いてもOK
    //     MqlTradeRequest request;
    //     ZeroMemory(request);

    // いずれのExecution modeでも、action, symbol, volume, type, type_filling
    // は必須項目
    request.action       = TRADE_ACTION_DEAL;
    request.symbol       = _Symbol;
    request.volume       = orderVolume;
    request.type         = orderType;          // ORDER_TYPE_BUY 成行買い注文 / ORDER_TYPE_SELL 成行売り注文
    request.type_filling = fillPolicy;
    request.magic        = EXPERT_MAGIC;

    //================================================
    // Execution modeが INSTANT または REQUESTの場合
    //================================================
    // price, sl, tp, deviationを設定する必要がある
    if(executionMode == SYMBOL_TRADE_EXECUTION_INSTANT ||
       executionMode == SYMBOL_TRADE_EXECUTION_REQUEST    ) {

        request.sl        = slPrice;
        request.tp        = tpPrice;
        request.deviation = 10;
        request.price = (orderType == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
                                                      : SymbolInfoDouble(_Symbol, SYMBOL_BID);
    }

    //--------------------------------------------------------------------------------
    // OrderSend処理
    //--------------------------------------------------------------------------------
    // サーバーが返してきた処理結果を格納する構造体
    MqlTradeResult result = {};

    ResetLastError();                           // OrderSend実行前に _LastError をリセット
    bool success = OrderSend(request, result);  // OrderSendを実行

    //================================
    // 処理結果をエキスパートに表示
    //================================
    string content = ReturnCodeToString(result.retcode);  // リターンコード番号の意味

    printf("[%s] Return code = %s.   Comment = %s.",
           request.symbol, content, result.comment);
    // [備考] symbolフィールドはresultには存在しない

    printf("[%s] retcode_external(外部取引システムのReturn code) = %u",
           request.symbol, result.retcode_external);

    // ヘルプに載っていたPrintFormat文
    //PrintFormat("deal=%I64u  order=%I64u", result.deal, result.order);

    recipient = result.retcode;

    //=================================================
    // 注文がサーバーによって受け付けられなかった場合
    //=================================================
    if(!success) {
        Alert("Order was not accepted by the server. [Return code = ",
              IntegerToString(result.retcode), "]"        );
        printf("OrderSend error = %d", GetLastError());
        return false;
    }

    //================================
    // サーバーが注文を受け付けた場合
    //================================
    else {
        // 注文は受け付けられたが、処理が失敗した可能性がある場合
        if(RequestFailed(result.retcode)) {
            Alert("[", request.symbol, "] Your request may have failed.");
            printf("[%s] Return code = %u", request.symbol, result.retcode);
        }

        // ポジションの全部または一部が約定されなかった場合
        if(result.volume != orderVolume) {
            Alert("Part(or All) of position was not executed.");
            printf("[%s] 注文したLot数 = %f ,   約定したLot数 = %f",
                   request.symbol, orderVolume, result.volume);
        }
    }

    return true;
}


MqlTradeRequest構造体で指定するフィールドはExecution modeに応じて異なるので、上記コードでは、Execution modeが INSTANT または REQUEST の時に指定するフィールドをif文内に入れてあります。

しかし、過去記事「MqlTradeRequest構造体(2)」に書いたように、Execution modeが MARKETやEXCHANGEの時にdeviationフィールドやpriceフィールドに値を指定しても実害は無いようです(無視されるだけ)。

そこで、私は次のように書いています。

bool SendMarketOrder(ENUM_ORDER_TYPE orderType,
                     double orderVolume,
                     double slPrice,
                     double tpPrice,
                     ENUM_ORDER_TYPE_FILLING fillPolicy,
                     uint& recipient)
{

(中略)

    request.action       = TRADE_ACTION_DEAL;
    request.symbol       = _Symbol;
    request.volume       = orderVolume;
    request.type         = orderType;
    request.type_filling = fillPolicy;
    request.magic        = EXPERT_MAGIC;
    request.sl           = slPrice;
    request.tp           = tpPrice;
    request.deviation    = 10;
    request.price = (orderType == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
                                                  : SymbolInfoDouble(_Symbol, SYMBOL_BID);

(以下略)


OrderSend関数の戻り値

MT5がサーバーに送信した項目(MqlTradeRequest型構造体のフィールド)のうち、必須項目の内容をブローカーのサーバーがチェックし、問題がなければOrderを受け付けます。この場合に(サーバーがOrderを受け付けた場合に)、OrderSend関数はtrueを返します。

つまり、OrderSend関数が返すtrueは、Orderがサーバー内に配置されたことを意味するだけで、こちらが指定した内容通りに注文が実行されたという意味ではありません。

その他の関数

上のコードに登場する自作関数は以下のとおりです。

//--------------------------------------------------------------------------------------------------
// 成行注文で使用可能なFill policyを返す関数(優先順位は IOC > FOK > RETURN)
//--------------------------------------------------------------------------------------------------
ENUM_ORDER_TYPE_FILLING ValidFillPolicyOfMarketOrder(string symbol)
{
    int mode = (int)SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE);
    if((mode & SYMBOL_FILLING_IOC)      == SYMBOL_FILLING_IOC) return ORDER_FILLING_IOC;
    else if((mode & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK) return ORDER_FILLING_FOK;
    else                                                       return ORDER_FILLING_RETURN;
}

この関数でExecution modeがMARKETかどうか調べていない点に関しては、過去記事「Fill policyに関する備忘録(MQL5)」の最後に書いたとおりです。

//--------------------------------------------------------------------------------------------------
// Requestの処理に失敗した可能性を返す関数 (リクエスト失敗時に true を返す)
//--------------------------------------------------------------------------------------------------
bool RequestFailed(uint returnCodeNum)
{
    if(returnCodeNum == 10008 || returnCodeNum == 10009) return false;
    else return true;
}

この関数では、リターンコードが10008と10009の場合は注文が成功したと見なしています。しかし、全てのリターンコードの内容を理解しているわけではないので、これで良いのか自信がありません(汗)。

//--------------------------------------------------------------------------------------------------
// 取引サーバーのリターンコードを文字列で返す関数
//--------------------------------------------------------------------------------------------------
string ReturnCodeToString(uint returnCodeNum)
{
    switch(returnCodeNum) {
    case 10004 : return "TRADE_RETCODE_REQUOTE(リクオート)";
    case 10006 : return "TRADE_RETCODE_REJECT(リクエストの拒否)";
    case 10007 : return "TRADE_RETCODE_CANCEL(トレーダーによるリクエストのキャンセル)";
    case 10008 : return "TRADE_RETCODE_PLACED(注文が出されました)";
    case 10009 : return "TRADE_RETCODE_DONE(リクエスト完了)";
    case 10010 : return "TRADE_RETCODE_DONE_PARTIAL(リクエストが一部のみ完了)";
    case 10011 : return "TRADE_RETCODE_ERROR(リクエスト処理エラー)";
    case 10012 : return "TRADE_RETCODE_TIMEOUT(リクエストが時間切れでキャンセル)";
    case 10013 : return "TRADE_RETCODE_INVALID(無効なリクエスト)";
    case 10014 : return "TRADE_RETCODE_INVALID_VOLUME(無効なボリューム)";
    case 10015 : return "TRADE_RETCODE_INVALID_PRICE(無効な価格)";
    case 10016 : return "TRADE_RETCODE_INVALID_STOPS(無効なストップ [ストップレベル違反])";
    case 10017 : return "TRADE_RETCODE_TRADE_DISABLED(取引が無効化されています)";
    case 10018 : return "TRADE_RETCODE_MARKET_CLOSED(市場が閉鎖中)";
    case 10019 : return "TRADE_RETCODE_NO_MONEY(リクエストを完了するのに必要な資金が不足)";
    case 10020 : return "TRADE_RETCODE_PRICE_CHANGED(価格変更)";
    case 10021 : return "TRADE_RETCODE_PRICE_OFF(リクエストを処理するための気配値(Ask, Bid)が存在しない)";
    case 10022 : return "TRADE_RETCODE_INVALID_EXPIRATION(無効な注文有効期限)";
    case 10023 : return "TRADE_RETCODE_ORDER_CHANGED(注文状態の変化)";
    case 10024 : return "TRADE_RETCODE_TOO_MANY_REQUESTS(頻繁過ぎるリクエスト)";
    case 10025 : return "TRADE_RETCODE_NO_CHANGES(リクエストに変更なし)";
    case 10026 : return "TRADE_RETCODE_SERVER_DISABLES_AT(サーバが自動取引を無効化)";
    case 10027 : return "TRADE_RETCODE_CLIENT_DISABLES_AT(クライアント端末が自動取引を無効化)";
    case 10028 : return "TRADE_RETCODE_LOCKED(リクエストが処理のためにロック中)";
    case 10029 : return "TRADE_RETCODE_FROZEN(注文やポジションが凍結)";
    case 10030 : return "TRADE_RETCODE_INVALID_FILL(無効なfill policy)";
    case 10031 : return "TRADE_RETCODE_CONNECTION(取引サーバに未接続)";
    case 10032 : return "TRADE_RETCODE_ONLY_REAL(操作は、ライブ口座のみで許可)";
    case 10033 : return "TRADE_RETCODE_LIMIT_ORDERS(未決注文[pending orders]の数が上限に達しました)";
    case 10034 : return "TRADE_RETCODE_LIMIT_VOLUME(このsymbolの注文やポジションのボリュームが限界に達しました)";
    case 10035 : return "TRADE_RETCODE_INVALID_ORDER(不正または禁止された注文の種類[order type])";
    case 10036 : return "TRADE_RETCODE_POSITION_CLOSED(指定されたPOSITION_IDENTIFIER を持つポジションがすでに閉鎖)";
    case 10038 : return "TRADE_RETCODE_INVALID_CLOSE_VOLUME (決済ボリュームが現在のポジションのボリュームを超過)";
    case 10039 : return "TRADE_RETCODE_CLOSE_ORDER_EXIST(指定されたポジションの決済注文が既存)";
    case 10040 : return "TRADE_RETCODE_LIMIT_POSITIONS(説明が長いのでヘルプを読んでください)";
    case 10041 : return "TRADE_RETCODE_REJECT_CANCEL(説明が長いのでヘルプを読んでください)";
    case 10042 : return "TRADE_RETCODE_LONG_ONLY(説明が長いのでヘルプを読んでください)";
    case 10043 : return "TRADE_RETCODE_SHORT_ONLY(説明が長いのでヘルプを読んでください)";
    case 10044 : return "TRADE_RETCODE_CLOSE_ONLY(説明が長いのでヘルプを読んでください)";
    case 10045 : return "TRADE_RETCODE_FIFO_CLOSE(説明が長いのでヘルプを読んでください)";
    case 10046 : return "TRADE_RETCODE_HEDGE_PROHIBITED(説明が長いのでヘルプを読んでください)";
    default : return "該当するリターンコードが見つかりませんでした";
    }
}

リターンコードの意味を調べるのが億劫なので用意しました。何のロジックもない力技な関数です。