English Deutsch
preview
スマートマネーコンセプト(BOS)とRSI指標をEAに統合する方法

スマートマネーコンセプト(BOS)とRSI指標をEAに統合する方法

MetaTrader 5 | 3 9月 2024, 10:05
21 0
Hlomohang John Borotho
Hlomohang John Borotho

はじめに

ペースの速い外国為替取引の世界では、信頼性が高く効率的な取引システムを持つことが成功の鍵です。取引にはさまざまな用語や概念、戦略が存在し、特に、まだ取引業界で足場を固めようとしている新人トレーダーにとっては、圧倒されることもしばしばです。  スマートマネーコンセプト(Smart Money Concept: SMC)はFX取引におけるトレンドの一つですが、初心者トレーダーや他の誰にとっても、スマートマネーコンセプトを取引に利用するのは難しい場合があります。

この問題を解決するためには、市場構造とプライスアクションに基づく取引決定を自動化するための強固なツールが必要です。その解決策は、スマートマネーコンセプト(ブレイクオブストラクチャー)と人気の高い相対力指数(Relative Strength Index: RSI)指標を組み合わせることです。この組み合わせは、プライスアクションとモメンタム分析の両方を活用することで、戦略的な優位性を提供し、取引のエントリとエグジットの精度を高め、取引パフォーマンスを最適化することを目指しています。


エキスパートアドバイザー(EA)の例の考え方

このEAの例のアイデアと機能は、いくつかのスマートマネーコンセプトがスイングを利用するように、EAがスイングローとスイングハイを検出することです。RSI指標は、買われすぎの場合は70レベル、売られすぎの場合は30レベルという伝統的な設定を使用し、期間は8とします。市場価格が以前に検出された高値を上回れば、それは上昇の構造が崩れたことを示します。同様に、市場価格が以前に検出された安値より下にある場合、それはダウンサイドの構造のブレイクを示します。


では、EAの例を作ってみましょう。

 このEAは、市場の状況とRSIレベルに基づいて買い注文と売り注文を出すことを目的としています。具体的にはこうです。

  1. 市場のスイングハイとスイングローを特定します。
  2. 市場が直前のスイングハイ(売りシグナルの場合)より上にあるか、直前のスイングロー(買いシグナルの場合)より下にあるかを確認します。
  3. その後、RSIレベルでシグナルを確認します。

基本的には、EAは、構造体のブレイク、または過去のスイング(高値/安値)のブレイクを検索し、RSIの値が指定された設定内にある場合、成行注文が実行されます(買い/売り)。


コードの内訳

1. プロパティとインクルード

#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <Trade/Trade.mqh>

これらの行はEAのプロパティを定義し、取引を実行するために必要な取引ライブラリを含みます。

2. グローバル変数と入力

long MagicNumber = 76543;
double lotsize = 0.01;
input int RSIperiod = 8;
input int RSIlevels = 70;
int stopLoss = 200;
int takeProfit = 500;
bool closeSignal = false;
  • MagicNumber:EAの取引のための一意の識別子
  • Lotsize:各取引の規模
  • RSIperiod:RSIの計算期間
  • RSIlevels:RSIの閾値レベル
  • StoplossおよびtakeProfit:SLとTPのレベルをポイントで表示
  • CloseSignal:現在オープンしているポジションと反対のシグナルに基づいてポジションをクローズするためのフラグ

3. RSI変数

int handle;
double buffer[];
MqlTick currentTick;
CTrade trade;
datetime openTimeBuy = 0;
datetime openTimeSell = 0;

  • Handle:RSI指標のハンドル
  • Buffer:RSI値を格納する配列
  • currentTick:現在の市場価格を保存する構造体
  • Trade:取引操作オブジェクト
  • openTimeBuyおよびopenTimeSell:最後の売買シグナルと操作のタイムスタンプ

4. 初期化関数

int OnInit() {
    if (RSIperiod <= 1) {
        Alert("RSI period <= 1");
        return INIT_PARAMETERS_INCORRECT;
    }
    if (RSIlevels >= 100 || RSIlevels <= 50) {
        Alert("RSI level >= 100 or <= 50");
        return INIT_PARAMETERS_INCORRECT;
    }
    trade.SetExpertMagicNumber(MagicNumber);
    handle = iRSI(_Symbol, PERIOD_CURRENT, RSIperiod, PRICE_CLOSE);
    if (handle == INVALID_HANDLE) {
        Alert("Failed to create indicator handle");
        return INIT_FAILED;
    }
    ArraySetAsSeries(buffer, true);
    return INIT_SUCCEEDED;
}
  • RSIの期間とレベルを検証する
  • 取引識別用のマジックナンバーを設定する
  • RSI 指標のハンドルを作成する
  • RSI値を時系列に逆順に格納するシリーズとしてバッファを設定する

5. 初期化解除関数

void OnDeinit(const int reason) {
    if (handle != INVALID_HANDLE) {
        IndicatorRelease(handle);
    }
}

EAが削除されたら、RSI指標のハンドルを解放します。

6. OnTick関数

ご存知のように、OnTick関数にはシグナルを検出して取引を実行するためのコアロジックが含まれています。

void OnTick() {
    static bool isNewBar = false;
    int newbar = iBars(_Symbol, _Period);
    static int prevBars = newbar;
    if (prevBars == newbar) {
        isNewBar = false;
    } else if (prevBars != newbar) {
        isNewBar = true;
        prevBars = newbar;
    }


まず、OnTick関数はTickごとに実行されるため、シグナルを検出したときや取引を実行したときに、1バーにつき1回実行されるようにする必要があります。staticのブーリアン変数isNewBarを宣言することで、これを実現します。最初にfalseに設定し、次にint型変数newBarを宣言し、iBars関数に代入して、すべてのローソク足を追跡できるようにします。

  • static bool isNewBar:新しいバー(ローソク足)が形成されたかどうかを追跡する
  • int newbar = iBars(_symbol, _Period):チャート上の現在のバーの数を取得する
  • static int prevBars = newbar:直前のバーカウントを初期化する
  • if-elseブロックは、新しいバーを示すバーカウントが変化したかどうかを確認する(新しいバーが形成された場合、isNewBar はtrueに設定され、そうでない場合はfalseに設定される)
    const int length = 10;
    int right_index, left_index;
    int curr_bar = length;
    bool isSwingHigh = true, isSwingLow = true;
    static double swing_H = -1.0, swing_L = -1.0;

次に、スイングハイとスイングローを検出するための変数を設定します。

  • const int length = 10:スイングハイとスイングローを検出する範囲を定義する
  • int right_index, left_index:現在のバーの右隣のバーと左隣のバーのインデックス
  • int curr_bar = length:現在のバーインデックスを設定する
  • isSwingHigh = true, isSwingLow = true:バーがスイングハイかスイングローかを判断するためのフラグ
  • static double swing_H = -1.0, swing_L = -1.0:最新のスイングハイ検出値と最新のスイングロー検出値を格納する
    double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits);
    double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits);

変数はdouble型で、変数Askは現在の市場ask価格を取得するために使用され、変数Bidは現在の市場bid価格を取得するために使用されます。

  • double Ask:現在のask価格を取得する
  • double Bid:現在のbid価格を取得する
  • NormalizeDouble:価格を正しい小数点以下の桁数に丸める
    if (isNewBar) {
        for (int a = 1; a <= length; a++) {
            right_index = curr_bar - a;
            left_index = curr_bar + a;

            if ((high(curr_bar) <= high(right_index)) || (high(curr_bar) < high(left_index))) {
                isSwingHigh = false;
            }
            if ((low(curr_bar) >= low(right_index)) || (low(curr_bar) > low(left_index))) {
                isSwingLow = false;
            }
        }

        if (isSwingHigh) {
            swing_H = high(curr_bar);
            Print("We do have a swing high at: ", curr_bar, " H: ", high(curr_bar));
            drawswing(TimeToString(time(curr_bar)), time(curr_bar), high(curr_bar), 32, clrBlue, -1);
        }
        if (isSwingLow) {
            swing_L = low(curr_bar);
            Print("We do have a swing low at: ", curr_bar, " L: ", low(curr_bar));
            drawswing(TimeToString(time(curr_bar)), time(curr_bar), low(curr_bar), 32, clrRed, +1);
        }
    }

次に、スイングハイとスイングローを検索しますが、その前に、新しいバーが検出されると、関数は、定義されたlength内の周囲のバーを確認し、スイングハイまたはスイングローがあるかどうかを識別します。変数lengthは、スイングを検索するローソク足の範囲を定義するもので、範囲内の各バーについて、関数は現在のバーの高値/安値を隣接するバーと比較します。

現在のバーの高値が周囲のバーよりも高い場合、それはスイングハイとして認識されます。同様に、現在のバーの安値が周囲のバーよりも低い場合、それはスイングローとして認識されます。これらの条件が満たされると、スイングハイまたはスイングローが確認され、それぞれの値が記録されます。さらに、drawswing関数を使用してチャートにマーカーが表示されます。

    int values = CopyBuffer(handle, 0, 0, 2, buffer);
    if (values != 2) {
        Print("Failed to get indicator values");
        return;
    }

    Comment("Buffer[0]: ", buffer[0],
            "\nBuffer[1]: ", buffer[1]);
  • copyBuffer(handle, 0, 0, 2, buffer):最新のRSI値をバッファにコピーする
  • RSI値の取得に失敗した場合、この関数は終了する
  • RSI値はチャートにコメントとして表示される
    int cntBuy = 0, cntSell = 0;
    if (!countOpenPositions(cntBuy, cntSell)) {
        return;
    }

countOpenPositions(cntBuy, cntSell):この関数は買いポジションと売りポジションのオープン数をカウントします。関数が失敗した場合、OnTickが終了します。

    if (swing_H > 0 && Ask > swing_H && buffer[0] >= 70) {
        Print("Sell Signal: Market is above previous high and RSI >= 70");
        int swing_H_index = 0;
        for (int i = 0; i <= length * 2 + 1000; i++) {
            if (high(i) == swing_H) {
                swing_H_index = i;
                break;
            }
        }
        drawBreakLevels(TimeToString(time(0)), time(swing_H_index), high(swing_H_index), time(0), high(swing_H_index), clrBlue, -1);

        if (cntSell == 0) {
            double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits);
            double sl = Bid + stopLoss * _Point;
            double tp = Bid - takeProfit * _Point;
            trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, lotsize, currentTick.bid, sl, tp, "RSI EA");
        }

        swing_H = -1.0;
        return;
    }

前回説明したように、売りシグナルを確認します。このロジックでは、Ask価格が直前のスイングハイを上回る必要があります。また、RSI値が70レベルを上回るか、少なくとも等しい必要があります。これらの条件が満たされた場合、スイングハイをマークし、チャート上にブレイクレベルを描画します。未決済の売りポジションが存在しない場合は、新たに売りポジションを建てます。この売り取引は、計算されたストップロスとテイクプロフィットレベルを設定しておこなわれ、その後、スイングハイの値がリセットされます。

    if (swing_L > 0 && Bid < swing_L && buffer[0] <= 30) {
        Print("Buy Signal: Market is below previous low and RSI <= 30");
        int swing_L_index = 0;
        for (int i = 0; i <= length * 2 + 1000; i++) {
            if (low(i) == swing_L) {
                swing_L_index = i;
                break;
            }
        }
        drawBreakLevels(TimeToString(time(0)), time(swing_L_index), low(swing_L_index), time(0), low(swing_L_index), clrRed, +1);

        if (cntBuy == 0) {
            double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits);
            double sl = Ask - stopLoss * _Point;
            double tp = Ask + takeProfit * _Point;
            trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, lotsize, currentTick.ask, sl, tp, "RSI EA");
        }

        swing_L = -1.0;
        return;
    }
}

買いシグナルの場合、逆のロジックが適用されます。Bid価格が直前のスイングローよりも低く、RSI値が30レベル以下であれば、テクニカル的に買いロジックが適切と見なされ、条件は満たされます。条件が満たされたら、スイングローをマークし、チャート上にブレイクレベルを描画します。  未決済の買いポジションがない場合には、計算されたストップロスとテイクプロフィットのレベルで新たに買いポジションを開き、その後、スイングローの値をリセットします。

ここで達成しようとしているのは、次のことです。

すべての条件を満たした売り取引

売り取引


すべての条件を満たした買い取引

買い取引


OnTick関数の概要

EAは、各マーケットティックで以下の主要なアクションを実行します。

  1. 新しいバーの検出:まず、最後のティック以降に新しいローソク足が形成されたかどうかを確認し、すべてのローソク足を追跡します。
  2. スイングハイとスイングローの識別:次に、相場がスイングハイを上回ったり、スイングローを下回ったりする際に、後で基準レベルとして使用するスイングポイントを特定します。
  3. RSI値の取得:シグナル確認のために最新のRSI値を取得します。
  4. ポジションを数える:現在の買いポジションと売りポジションを管理します。
  5. 売買シグナルの生成:スイングポイント(スイングハイ、スイングロー)とRSIレベルを基に売買シグナルを生成します。
  6. 取引の実行:すべての条件が満たされた場合、生成されたシグナルに基づいて新しくポジションを建てます。

最高値、最低値、時間値のカスタム関数

double high(int index){
       return (iHigh(_Symbol, _Period, index));
}

double low(int index){
       return (iLow(_Symbol, _Period, index));
}

datetime time(int index){
       return (iTime(_Symbol, _Period, index));
}

関数high (int index)は、指定したインデックスにおけるバーの高値を返します。この関数は、indexint型のパラメータとして受け取ります。MQL5内蔵の関数iHigh(_symbol,_Period,index)を使用して、現在の銘柄と期間のindexにおけるバーの高値を取得します。次にlow (int index)が続きます。この関数は、指定したindexにおけるバーの安値を返します。また、int型のパラメータとしてindexも受け取り、 iLow(_symbol, _Period, index) となります。また、これはMQL5に内蔵されている関数で、現在の銘柄のindexにおけるバーの安値を取得します。最後に、time (int index)があります。この関数は、指定されたインデックスのバーの時間を返します。iTime(_symbol, _Period, index)もMQL5の組み込み関数で、現在の銘柄と期間のindexのバーの時間を取得します。iTime のデータ型はdatetime型です。

スイングポイントを描画する関数

void drawswing(string objName, datetime time, double price, int arrCode, color clr, int direction){
   if(ObjectFind(0, objName) < 0){
      ObjectCreate(0, objName, OBJ_ARROW, 0, time, price);
      ObjectSetInteger(0, objName, OBJPROP_ARROWCODE, arrCode);
      ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, 10);
      
      if(direction > 0){ObjectSetInteger(0, objName, OBJPROP_ANCHOR, ANCHOR_TOP);}
      if(direction < 0){ObjectSetInteger(0, objName, OBJPROP_ANCHOR, ANCHOR_BOTTOM);}
      
      string text = "";
      string Descr = objName + text;
      ObjectCreate(0, Descr, OBJ_TEXT, 0, time, price);
      ObjectSetInteger(0, Descr, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, Descr, OBJPROP_FONTSIZE, 10);
      
      if(direction > 0){
         ObjectSetString(0, Descr, OBJPROP_TEXT,"  "+text);
         ObjectSetInteger(0, Descr, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
      }
      if(direction < 0){
         ObjectSetString(0, Descr, OBJPROP_TEXT,"  "+text);
         ObjectSetInteger(0, Descr, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
      }
   }
   ChartRedraw(0);
}

この関数は、チャート上にスイングポイント(高値と安値)の視覚的なマーカーを作成します。以下のパラメータを取ります。

  • ObjName:作成するオブジェクトの名前
  • Time:スイングが起こった、または検出された時間
  • Price:スイングが形成された価格
  • ArrCode:視覚的表現のための矢印コード
  • Clr:矢印の色
  • Direction:スイングの方向(高値はプラス、安値はマイナス)

機能性

1. オブジェクトの作成

  • ObjectFind(0, objName) < 0:指定された名前のオブジェクトが既に存在するかどうかを確認する
  • ObjectCreate(0, objName, OBJ-ARROW, 0, time, price):指定された時間と価格でarrowオブジェクトを作成する
  • ObjectSetInteger(0, objName, OBJPROP-ARROWCODE, arrCode):矢印コードを設定する
  • ObjectSetInteger(0, objName, OBJPROP-COLOR, clr):矢印の色を設定する
  • ObjectSetInteger(0, objName, OBJPROP-FONTSIZE, 10):フォントサイズを設定する

2. 方向処理

  • 方向によってアンカーの位置を設定する
  • OBJPROP-ANCHOR:矢印のアンカーポイントの位置を設定する

3. テキストオブジェクトの作成

  • 追加情報を表示するために、矢印に関連付けられたテキストオブジェクトを作成する
  • 方向に基づいて、テキストの色、フォートサイズ、アンカーポイントを設定する

4.チャート更新

  • ChartRedraw(0):変更を反映するためにチャートを再描画する

ブレイクレベルを描画する関数

void drawBreakLevels(string objName, datetime time1, double price1, datetime time2, double price2, color clr, int direction){
   if(ObjectFind(0, objName) < 0){
         ObjectCreate(0, objName, OBJ_ARROWED_LINE, 0, time1, price1, time2, price2);
         ObjectSetInteger(0, objName, OBJPROP_TIME, 0, time1);
         ObjectSetDouble(0, objName, OBJPROP_PRICE, 0, price1);
         ObjectSetInteger(0, objName, OBJPROP_TIME, 1, time2);
         ObjectSetDouble(0, objName, OBJPROP_PRICE, 1, price2);
         ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);
         ObjectSetInteger(0, objName, OBJPROP_WIDTH, 2);
         
         string text = "Break";
         string Descr = objName + text;
         ObjectCreate(0, Descr, OBJ_TEXT, 0, time2, price2);
         ObjectSetInteger(0, Descr, OBJPROP_COLOR, clr);
         ObjectSetInteger(0, Descr, OBJPROP_FONTSIZE, 10);  
         
         if(direction > 0){
            ObjectSetString(0, Descr, OBJPROP_TEXT,text+"  ");
            ObjectSetInteger(0, Descr, OBJPROP_ANCHOR, ANCHOR_RIGHT_UPPER);
         }
         if(direction < 0){
            ObjectSetString(0, Descr, OBJPROP_TEXT,text+"  ");
            ObjectSetInteger(0, Descr, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
         }          
   }
   ChartRedraw(0);
}

この関数は、チャート上で事前に検出されたスイングポイントである価格レベルのブレイクを視覚的に表現します。次のパラメータがあります。

  • objName:作成するオブジェクトの名前
  • time1、time2:開始時刻はスイングが形成された時刻、終了時刻はブレイクが発生した時刻
  • price1、price2:開始価格はスイングハイまたはスイングローが発生した価格、価格2はスイングハイまたはスイングローのブレイクが発生した価格
  • Clr:矢印の色
  • Direction:アンカーテキストの方向


機能性

1. オブジェクトの作成

  • ObjectFind(0, objName) < 0:与えられた名前のオブジェクトが既に存在するかどうかを確認する
  • ObjectCreate(0, objName, OBJ_ARROWED_LINE, 0, time1, price1, time2, price2):指定された時間と価格で矢印オブジェクトを作成する
  • 矢線の始点と終点の時間と価格を設定する
  • ObjectSetInteger(0, objName, OBJPROP-COLOR, clr):線の色を設定する
  • ObjectSetInteger(0, objName, OBJPROP-WIDTH, 2):線幅を設定する

2. テキストオブジェクトの作成

  • 追加情報を表示するために、行に関連付けられたテキストオブジェクトを作成する
  • 方向に基づいて、テキストの色、フォントサイズ、アンカーポイントを設定する

3. チャートの更新

  • chartRedraw(0):変更を反映するためにチャートを再描画する


    結論

    要約すると、カスタム関数はバーの高値、安値、時間値へのアクセスを簡素化し、取引体験を向上させます。RSI指標とSMCの概念を組み合わせることで、指標を視覚化し、EAがその指示に従っていることを確認できます。チャートに矢印を表示することで、スイングポイント(高値と安値)を強調し、ブレイクレベルの観察も可能です。さらに、チャートはリアルタイムで最新のマーカーや指標で更新され、視覚的な分析と意思決定をサポートします。

    この包括的なガイドを通じて、SMCの概念とRSI指標をEAの構造と機能に統合する方法について、詳細に理解できます。ステップごとの解説に従うことで、読者は変数の初期化からシグナルに基づく取引の実行まで、SMC_RSI EAの操作を明確に把握できるでしょう。経験豊富なトレーダーも初心者も、この包括的な洞察を活用して、取引努力にこの強力なツールを効果的に活用し、カスタマイズするための知識を得られるはずです。

    以下にバックテスト結果を示しますが、より高い利益率を得るためには、EA自体にまだある種の最適化が必要であると言えます。

    バックテスト

    また、以下はエクイティ カーブの視覚的表現です。12か月間しかテストしていないので、12年間テストした場合のパフォーマンスが正確にどうなるかは誰にもわかりません。

    エクイティーカーブ

    参照文献

    元記事:https://www.mql5.com/ja/articles/15017

    MetaQuotes Ltdにより英語から翻訳されました。
    元の記事: https://www.mql5.com/en/articles/15030

    添付されたファイル |
    SMC_RSI.mq5 (12.13 KB)
    初心者のためのMQL5によるSP500取引戦略 初心者のためのMQL5によるSP500取引戦略
    MQL5を活用してS&P500指数を正確に予測する方法をご紹介します。古典的なテクニカル分析とアルゴリズム、そして長年の経験に裏打ちされた原理を組み合わせることで、安定性を高め、確かな市場洞察力を得られます。
    知っておくべきMQL5ウィザードのテクニック(第27回):移動平均と迎角 知っておくべきMQL5ウィザードのテクニック(第27回):移動平均と迎角
    迎角はよく引用される指標で、その急勾配は優勢なトレンドの強さと強い相関があると理解されています。一般的にどのように使用され、理解されているかを調べ、それを使用する取引システムの利益のために、その測定方法に導入可能な変更があるかどうかを検討します。
    Candlestick Trend Constraintモデルの構築(第6回):オールインワン統合 Candlestick Trend Constraintモデルの構築(第6回):オールインワン統合
    一つの大きな課題は、異なる機能を持つ同じプログラムを、同じ通貨ペアに対して複数のチャートウィンドウで実行し、管理することです。この問題を解決するには、複数の機能を一つのメインプログラムに統合する方法を検討する必要があります。さらに、プログラムの設定を操作ログに出力する方法や、成功したシグナルのブロードキャストをチャートインターフェイス上に表示する方法についても解説します。連載が進むにつれ、この記事でさらに詳しい情報を提供していきます。
    固有ベクトルと固有値:MetaTrader 5での探索的データ分析 固有ベクトルと固有値:MetaTrader 5での探索的データ分析
    この記事では、データ内の特異な関係性を明らかにするために、固有ベクトルと固有値を探索的データ分析にどのように応用できるかを探ります。