English Русский 中文 Español Deutsch Português
preview
MetaTrader5でのビッド・アスク・スプレッド分析

MetaTrader5でのビッド・アスク・スプレッド分析

MetaTrader 5トレーディング | 29 10月 2021, 14:05
1 123 0
Paul Kelly
Paul Kelly

はじめに

取引のエントリとエグジットの両方に(逆)指値注文を使用しない場合、成行注文を使用します。もちろん、受け取る価格はビッド・アスク・スプレッドのサイズに依存します。

買うボタンを押すと、買いは、実際には、おそらく買いを決定するために使用したビッド価格にスプレッドサイズを足したアスク価格で行われます。

売るボタンを押すと、売りは、実際には、アスク価格からスプレッドサイズを引いたビッド価格で行われます。

もちろん、決済ボタンを押して以前に買ったポジションを決済すると、売りは実際には現在のビッド価格で行われます。

また、その逆も当てはまります。決済ボタンを押して以前にショートしたポジションを決済すると、実際には現在のアスク価格で買い戻すかカバーすることになります。

MetaTrader 5のティックデータを使用すると、最近の真の平均ビッド・アスク・スプレッドが実際に何であったかを分析できます。<

ビッドとアスクの両方の価格ラインを表示すれば現在のスプレッドは使用可能なので、確認する必要はありません。


理由と方法を見てみましょう

チャートから、この証券会社はスプレッドのほとんどが5ポイントであると言っていることがわかります。

その場合、取引の開始と終了の往復に1ピップの費用がかかるはずです。

したがって、ストップロスが10ピップでテイクプロフィットが10ピップの1/1の報酬リスク比の取引の場合、リスク/ステークの10%のコストがかかるはずです。

この種のスプレッドは十分に公平です。たとえば、ブックメーカーのオーバーラウンドブックは通常15%、カジノの利益率は約4%です。


BAS-EURUSD-M30

ただし、実際の平均スプレッドである赤い線と証券会社が文書化したスプレッド(黒い破線)は、以下のデータウィンドウで確認されているように、宣言されたスプレッドのほとんど2倍の大きさです。同じSLとTPを使用した前の例を使用すると、通常、コストは少なくとも2ピップまたは20%になります。 


BAS-EURUSD-M30-DW


あなたが小規模のスカルパーである場合、例えば5ピップのTPと5ピップのSLを使用するか、前の例の10ピップのSLまたはTPがヒットする前に、たとえば5ピップの損失でエグジットすることにした場合、コストは同じ2ピップですが、取引が不利になり始めたときに慎重だったために、コスト率は賭け金/リスクの40%になりました。 

私が初心者トレーダーだったとき、2:1のリスク/報酬比で5ピップSLと10ピップTPを使い始めました(多くの初心者トレーダーがそうしていると思います)。これにはあまり成功しなかったので、

信頼できるジグザグ指標を使用してEURUSD M1チャートを詳細に分析しました。最小の脚のサイズとして5ピップスに設定しました。これは、私が対処できる種類のリトレースメントを意味します。

結果は、ほとんどの小さなスイングは7ピップあたりであり、10ピップの脚が比較的まれであることを示唆しているようです。もちろん、ニュースリリースや市場の不安定さは考慮に入れていたので、結果は主に取引セッションのみの平均期間からのものでした。

その結果、私は10ピップのストップロスを使い始め、テイクプロフィットをオープンのままにしました。よって、取引を注意深く監視し、すでに7ピップの損失または利益に達した場合にいつエグジットするかを決めることができました。これは改善をもたらしましたが、それでも利益は得られませんでした。この時初めて、自分の証券会社のビッド・アスク・スプレッドの高さが取引の敵だと気付き、もちろん、より良い証券会社を探しました。


BAS-EURUSD-M1

ニュースが出たときや市場が不安定になったときに取引すると、実際の平均スプレッドは約15ポイント(標準の5ポイントの3倍)になり、3ピップまたは賭け金の60%を支払う必要のあることがわかります。 


BAS-EURUSD-M1-DW

英国時間20:30(チャートサーバー時間では21:30)以降の取引も考慮しないでください。特に週末に取引ポジションを保持することにした場合は、4、5、6倍、またはそれ以上になる可能性があります。 以下に示すように、ストップロスとテイクプロフィットが非常に大きくない限り、標準の5ポイントスプレッドのほぼ10倍です。

BAS-EURUSD-M30-WeekEnd


OnInit()コード例

#property indicator_separate_window

#property indicator_buffers 2
#property indicator_plots   2

//--- plots
#property indicator_label1  "ActSpread"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

#property indicator_label2  "DeclaredSpread"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrBlack
#property indicator_style2  STYLE_DASH
#property indicator_width2  2

//--- indicator parameters
input int      numRecentBarsBack=100; //#RecentBarsBack M30+~100, M5~200, M1~500
input bool     doPrint=true;          //true=prints to the toolbox\experts log

//--- indicator buffers
double         ActSpreadBuf[], DeclaredSpreadBuf[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()  
{
   int numBars=iBars(_Symbol,PERIOD_CURRENT)-2; 
   
   // Check we have enough data for the request before we begin
   if(numRecentBarsBack>numBars) 
   { 
      Alert("Can't Do ", numRecentBarsBack, "! Only ",  
               numBars, " Bars are Available", 
               " try 100 or so for 30+ minute charts,",
               " 200 for 5 minute, or 500 for 1 minute charts.",
               " Otherwise the indicator may be too slow"
           ); 
           
      return(INIT_PARAMETERS_INCORRECT);
   }

   double sumPrice=0; 
   double avgPrice=0; 

   // Get the standard 5 point spread for the standard EURUSD currency
   double stdSpread=0.00005/iClose("EURUSD",PERIOD_M1,1); // 1.2 ~=  EURUSD std price
   
   //Find out the current average price of the instrument we are using, so we can standardise the spread and _Point
   int CheckAvgPriceBars=MathMin(numRecentBarsBack, 200);
   
   int i=0;
   for(; i<CheckAvgPriceBars; i++)
   {
      sumPrice+=iClose(_Symbol,PERIOD_CURRENT,i);
   }
   avgPrice=sumPrice/(i? i: 1.0);
   
   //convert the stdSpread to stdPoint by dividing by 5, so we compare  apples with apples, not oranges
   double stdPoint=StringToDouble(DoubleToString(avgPrice*stdSpread/5.0,6));

   Print(i, "=bars done, avgPrice=", DoubleToString(avgPrice,6), 
            " std=", DoubleToString(1.2*stdSpread, 6), 
            " stdPoint=", DoubleToString(stdPoint, 6)
         );
   
   SetIndexBuffer(0,ActSpreadBuf,INDICATOR_DATA);         
   SetIndexBuffer(1,DeclaredSpreadBuf,INDICATOR_DATA);    
   
   string indName ="BAS("+_Symbol;
          indName+=" TF="+string(_Period);
          indName+=" stdPoint="+DoubleToString(stdPoint, 6);
          indName+=") Last("+string(numRecentBarsBack)+") Bars";
          
   IndicatorSetString(INDICATOR_SHORTNAME, indName); 
   
   IndicatorSetInteger(INDICATOR_DIGITS,6); 
   
   IndicatorSetDouble(INDICATOR_MINIMUM, 0.0); 

   IndicatorSetInteger(INDICATOR_LEVELS, 20);     
   
   //mark out each standard EURUSD 5 point spread, to compare this currencies spread with EURUSD
   IndicatorSetDouble(INDICATOR_LEVELVALUE,0,  0.000000); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,1,  5*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,2, 10*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,3, 15*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,4, 20*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,5, 25*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,6, 30*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,7, 35*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,8, 40*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,9, 45*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,10,50*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,11,55*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,12,60*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,13,65*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,14,70*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,15,75*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,16,80*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,17,85*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,18,90*stdPoint); 
   IndicatorSetDouble(INDICATOR_LEVELVALUE,19,95*stdPoint); 
    
return(INIT_SUCCEEDED);
}

この単純な2プロットインジケーターの場合、パラメータは2つだけです。最初の「numRecentBarsBack」は、分析するバーの数です。

OnInit()で最初に行うことは、リクエストを満たすのに十分なデータがあることを確認することです。データがない場合は、ユーザーに警告し、使用する現実的な値を提案してから、エラーでインジケーターを早期に終了します。

OnInit()の残りの部分は、インジケーターサブウィンドウで使用されるレベルを除いて、かなり標準的です。レベルは、標準のEURUSDの5ポイントスプレッドの倍数に対応する値に設定されます。 

宣言された平均スプレッド値と実際の平均スプレッド値の比較を確認するだけでなく、通常はすべての通貨のスプレッドのうち最低の使用可能なスプレッドを持つ標準EURUSDと比較してさまざまな通貨のスプレッドがどれだけ大きいかを確認する必要があるため、これはかなり重要なステップです。

現在のEURUSD価格を取得し(存在しない場合は1.2に置き換え)、5 EURUSDポイントをその価格で割って標準スプレッドを作成する必要があるため、この方法はかなり複雑です。次に、現在の外国為替商品のnumRecentBarsBack価格を繰り返し処理して(非外国為替商品ではテストしていません)、その商品の平均価格を取得します。

金融商品の平均価格が得られたらそれに以前に作成された標準スプレッドを掛け、5で割ることにより、丸められた標準ポイントを作成します。

この丸められた標準ポイントは、各レベル値で使用され、以下の「エキゾチック」USDMXNチャートのインジケーター名に示されているように、インジケーターの短縮名にも含まれます。

このUSDMXNの例では、取引日中の宣言されたスプレッドは約0.0025であり、これはゼロから約3スプレッドレベル上であるため、EURUSDチャートの約15ポイントに対応します。また、実際の平均スプレッドは、このブローカーの高水準を上回っても大きく変動することに注意してください。

BAS-USDMXN-M30

以下のGBPAUDチャートは、取引日中の宣言されたスプレッドが約0.00019であることを示しています。これは、ゼロから約2.5のスプレッドレベルであるため、EURUSDチャートの約12ポイントに相当します。また、このチャートでは、実際の平均スプレッド値は、この証券会社の宣言された値にかなり近くなります。

 BAS-GBPAUD-M30

以下のGBPJPYチャートは、取引日中の宣言されたスプレッドが約0.020であることを示しています。これは、ゼロから約3スプレッドレベル上であるため、EURUSDチャートの約15ポイントに相当します。また、このチャートでは、実際の平均スプレッド値は、この証券会社の宣言された値にかなり近くなります。

BAS-GBPJPY-M30

以下のUSDJPYチャートは、取引日中の宣言されたスプレッドが約0.0050であることを示しています。これは、ゼロレベルからおよそ1スプレッドレベル上であるため、EURUSDチャートの標準の約5ポイントに対応します。また、このチャートでは、実際の平均スプレッド値が宣言された値の約2倍であるため、EURUSDのリスク/報酬のパーセンテージレベルと同じコメントがここにも適用されます。

BAS-USDJPY-M30


さらにいくつかの例を示します。スプレッドレベル間の関係について独自の評価を行うことができます。

BAS-GBPUSD-M30

BAS-EURGBP-M30

2番目のパラメータはブール値の「doPrint」です。これはコードでチェックされ、trueの場合、以下の例が示すように、個々のバーの統計がエキスパートログに出力されます。「numRecentBarsBack」の値が大きすぎる場合、これによりインジケーターの速度が低下する可能性があるため、デフォルト値は100です。

「doPrint」パラメータをtrueに設定し、「numRecentBarsBack」を30分チャートの場合は約100、1分チャートの場合は300の妥当な値に設定すると、ログエントリをコピーして、真のビッド・アスク・スプレッドの証拠として証券会社に送信できます。

ビッド・アスク・スプレッドのM20ログ

ビッド・アスク・スプレッドのM1ログ


OnCalculate()コード例

//--- Global variables
//--- Set the date formatting for printing to the log
const uint dtFormat=uint(TIME_DATE|TIME_MINUTES); 

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   //--- Check for no data or Stop flag before we begin               
   if(_StopFlag || rates_total<2)  
   { 
         Alert("Error, StopFlag=", _StopFlag, " #Bars=", rates_total);    
         return(rates_total);    
   }
   
   //only do the report at indicator start up or refresh 
   if(prev_calculated>2) 
   {         
      // if we have already nulled the ActSpreadBuf just do the DeclaredSpreadBuf[] and return.
      if(prev_calculated==rates_total)
      {
         int currBar=rates_total-1;
         DeclaredSpreadBuf[currBar]=spread[currBar]*_Point;
         return(rates_total);
      }
      // else its the start of a new bar so null the ActSpreadBuf 
      else
      {
         int currBar=rates_total-1;
         ActSpreadBuf[currBar]=EMPTY_VALUE;
         return(rates_total);
      }
   }
         
         
   static int start=rates_total-numRecentBarsBack;
   
   MqlTick tickBuf[]; 
   
   double sumSpread=0;
   double thisSpread=0;
   
   int ticks=0; 
   int bid_tick=0; 
   int ask_tick=0; 
   int k=0;
   
   ArrayInitialize(ActSpreadBuf, EMPTY_VALUE);      
   ArrayInitialize(DeclaredSpreadBuf, EMPTY_VALUE); 
   
   for(int i=start; i<rates_total; i++) 
   { 
      sumSpread=0;
      thisSpread=0;
      bid_tick=0;
      ask_tick=0;
      k=0;
      
      ticks=CopyTicksRange(_Symbol, tickBuf, 
                           COPY_TICKS_INFO, // Only bid and ask changes are required
                           time[i-1]*1000,  // Start time of previous bar
                           time[i  ]*1000   // End time of previous bar
                           );
      
      while(k<ticks) 
      {
         if((tickBuf[k].flags&TICK_FLAG_ASK)==TICK_FLAG_ASK)  
            ask_tick++; 
            
         if((tickBuf[k].flags&TICK_FLAG_BID)==TICK_FLAG_BID)  
            bid_tick++; 
         
         sumSpread+=tickBuf[k].ask-tickBuf[k].bid;
         
         k++;
      }
      
      // Ensure no divide by zero errors for any missing tick data
      if(ticks>0) {                    
         thisSpread=sumSpread/ticks;
         ActSpreadBuf[i-1]=thisSpread;  
      }
      else  { 
         thisSpread=0.0; 
         ActSpreadBuf[i-1]=EMPTY_VALUE;  
      }

      DeclaredSpreadBuf[i-1]=spread[i-1]*_Point;
      
      if(doPrint) 
      {            
                  Print(TimeToString(time[i-1], dtFormat), 
                  "  NumTicks="+string(ticks),
                  "  b="+string(bid_tick),
                  "  a="+string(ask_tick),
                  "  AvgSpread=",  DoubleToString(thisSpread/_Point, 1),
                  "  DeclaredSpread=", string(spread[i-1]) 
                  );
      }
   
   }
   
   //don't do stats for incomplete current bar, but can do DeclaredSpread if it has a value
   DeclaredSpreadBuf[rates_total-1]=(spread[rates_total-1]*_Point);

//--- return value of prev_calculated for next call
return(rates_total);
}

上記のOnCalculate()の例から、注目すべき主なポイントは、CopyTicksRange()を使用して、前のインデックスバー/ローソク足の開始と現在のインデックスバー/ローソク足の開始の間のティックデータのみを取得することです。また、日時データは秒までしか正確ではなく、CopyTicksRange()にはミリ秒が必要なため、time[]配列に1000を掛けてミリ秒に変換する必要があります。

ticks=CopyTicksRange(_Symbol, tickBuf, 
                           COPY_TICKS_INFO, // Only bid and ask changes are required
                           time[i-1]*1000,  // Start time of previous bar
                           time[i  ]*1000   // End time of previous bar
                           );
      

また、プロットでは使用しませんが、ビッドとアスクのティックを累積することにもご注意ください。ビッドティックの値は、tick_volume[]配列の値と一致する必要があり、データウィンドウに表示されているとおりになります。


ティックのダウンロードに関する追加の注意...

通常は使用しない通貨を確認する場合は、[表示]/[銘柄]メニュー項目からその通貨をダブルクリックして[銘柄を表示]に追加する必要があります。このウィンドウでは、ティックタブに移動し、最初の日付メニューで今日より1か月ほど前の日付をすべてのティックに要求する必要があります。 次に、2番目の日付メニューを明日に設定して、ローカルのダニデータベースにシードを設定します。


終わりに

通貨を取引する前に、検討している取引の種類(スキャルピング、スイング、ポジションなど)のリスク率を把握し、一般的な標準スプレッドサイズを使用して利用可能な他の通貨と比較する必要があります。

私の研究から、USDに直接接続されている主要通貨(USDCAD、USDCHF、USDJPY、EURUSD、GBPUSD)に固執するようにトレーダーにアドバイスしたいと思います。 全体的なスプレッドが最も低いためです。

手数料のみで取引している場合でも、スプレッドが非常に高いレベルに増加された場合真のビッド・アスク・スプレッドを確認できることを証券会社に知らせる必要があります。ご幸運をお祈りします。取引時間中に妥当なビッド・アスク・スプレッドを持つ証券会社が見つからない場合は、利益を得ることはできないため、取引しないでください。

このプロセスはMetaTrader 5に対してのみ実行できます。これは、ティックデータがMetaTrader 4で利用できないためです。アップグレードするのに十分な理由です。

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

添付されたファイル |
DoEasyライブラリのグラフィックス(第84部): 抽象標準グラフィカルオブジェクトの子孫クラス DoEasyライブラリのグラフィックス(第84部): 抽象標準グラフィカルオブジェクトの子孫クラス
本稿では、ターミナル抽象標準グラフィカルオブジェクトの子孫オブジェクトの作成について検討します。クラスオブジェクトでは、すべてのグラフィカルオブジェクトに共通のプロパティを記述します。つまり、それは単にある種のグラフィカルオブジェクトです。実際のグラフィカルオブジェクトとの関係を明確にするには、この特定のグラフィカルオブジェクトに固有のプロパティを子孫オブジェクトクラスに設定する必要があります。
多色ローソク足を作成するためのオプションの探究 多色ローソク足を作成するためのオプションの探究
この記事では、ローソク足でカスタマイズされたインジケーターを作成する可能性について説明し、それらの長所と短所を指摘します。
DoEasyライブラリのグラフィックス(第85部): グラフィカルオブジェクトコレクション - 新規作成オブジェクトの追加 DoEasyライブラリのグラフィックス(第85部): グラフィカルオブジェクトコレクション - 新規作成オブジェクトの追加
本稿では、抽象グラフィカルオブジェクトクラスの子孫クラスの開発を完了し、これらのオブジェクトをコレクションクラスに格納する機能の実装を開始します。特に、新しく作成した標準のグラフィカルオブジェクトをコレクションクラスに追加する機能を作成します。
DoEasyライブラリのグラフィックス(第83部): 抽象標準グラフィカルオブジェクトのクラス DoEasyライブラリのグラフィックス(第83部): 抽象標準グラフィカルオブジェクトのクラス
本稿では、抽象グラフィカルオブジェクトのクラスを作成します。このオブジェクトは、標準のグラフィカルオブジェクトのクラスを作成するための基礎として機能します。グラフィカルオブジェクトには複数のプロパティがあるため、抽象グラフィカルオブジェクトクラスを実際に作成する前に、多くの準備作業が必要です。この作業には、ライブラリ列挙型のプロパティの設定が含まれます。