ウェブからの債券利回りデータのスクレイピング

Steven Brown | 27 6月, 2019

イントロダクション

自動売買は、未来の価格行動を予測するために、過去の価格行動を使用するテクニカルインジケータにほぼ依存してます。 しかし、相場を動かしうるファンダメンタルズを無視するトレーダーは、ファンダメンタルズデータを利用するトレーダーに対して不利です。 自動的に収集されたファンダメンタルズデータに基づくインジケータは、EAのパフォーマンスを向上させることができます。 為替レートに最も影響を与えるファンダメンタルズデータは、金利です。 中央銀行の金利は変動しませんが、米国などの国債利回りは変動します。 10年国債は、世界の債券相場のすべての時間枠で変動します。 利回りは、未来の中央銀行の金利がどこに向かうのか、相場が持つ期待値感を反映します。 債券利回りは、多くの場合、金利と為替レートの主要なインジケータです。 外国為替相場では、通貨ペアに適用されるメトリックは、様々な時間枠で、金利差、特にデルタ、または金利差の変化です。 図1は、金利差の動きを基準点で表し、正の方向に、同じ方向のEUR/USD通貨ペアの動きを示すインジケータです。 この記事では、債券利回りデータを Web から収集し、金利差とデルタのデータ表現から派生させる方法について説明します。


金利差算出インジケータ

図1. 時間ごとの EUR/USD チャートの金利差分インジケータ。

スクレイピング 101

ブラウザに表示される Web ページは、通常、書式設定されたテキスト、グラフィックス、画像、サウンド、ビデオなどの多くの要素で構成されます。 これらの要素はすべて Web サーバー上のファイルに存在し、特定のアドレスまたは URL を使用してブラウザによってダウンロードされます。 ただし、その要素には有用な情報があるかもしれませんが、プログラムはページの 1 つの要素をダウンロードし、残りの要素を無視する可能性があります。 "スクレイピング" という方法で情報を取得するには、スクレイパー プログラムは、要素を含むファイルへの URL がある必要があります。 そのファイルをダウンロードし、数値を表すテキストを検索し、そのテキストを数値に変換できます。

URL を取得する

スクレイピングの最初のタスクは、ダウンロードする要素を含むファイルの URL を取得することです。 要素がページの html テキストに埋め込まれている場合は、Web ページの URL になる可能性があります。 その場合、要素はページの html テキストから解析できます。 または、URL をページ上のリンクに埋め込むことができます。 または、ページとスクリプトをダウンロードした後にブラウザが実行するページにリンクされたスクリプトによって、URL をブラウザに渡すことができます。 その場合、スクレーパー プログラムはスクリプトを実行する必要はありませんが、スクリプトが生成する URL を使用できます。 この URL は、Internet Explorer または Google Chrome で利用可能なデベロッパーツールを使用して検出できます。 URL のソースが何であれ、スクレイピング プログラムはWeb サーバーからファイルをダウンロードし、目的の情報を解析します。 債券利回りの金融ウェブサイトがいくつかあります。 まず、 https://www.marketwatch.com/investing/bond/tmubmusd10y?countrycode=bxを見て、スクレイピングの例を作成します。

まず、上記のリンクをクリックして、ブラウザが Web サーバーからダウンロードする html ファイルを見てみましょう。 Chrome ブラウザに表示されるページをクリックし、右上隅にあるツール ボタンをクリックし、マウス カーソルを "その他のツール" に移動し、[ページを保存する] を選択して html ファイルをダウンロードし、メモ帳などのテキスト エディタで開きます。 この Web サイトでは、html ファイルの先頭にある一連のメタ タグの 1 つに含まれているため、ボットがクオートを取得できることが明らかになります。 メタデータはブラウザでは表示されず、表示される内容には影響しませんが、html ファイルをダウンロードするプログラムからアクセスできます。 引用はメタタグに表示 <meta name=""price"" content=""3.066""> され、タグの先頭から28文字が表示され、ブラウザによってページに目立つように表示されるのと同じ値です。 スクレーパー プログラムは、ファイルでテキスト文字列 [<メタ名="price" content==を検索し、メタタグの先頭のインデックスに 28 を追加し、結果のポジションにあるテキストを浮動小数点数に変換できます。 混乱を避けるために、この記事では角かっこを使用して、クオートを頻繁に使用する html テキストを引用します。

より良いボットを構築する

上記にリンクされた html ファイルは、サーバーからダウンロードすると、スタイル シート情報の大きなブロックが含まれ、合計サイズは 295 KB です。 ただし、対象のメタタグは、ファイルの先頭から換算してもわずか 3 KB です。 適切に動作するボットは、必要以上のデータをダウンロードしないため、クオートを取得するたびに最初の 4 KB のみをダウンロードするのが妥当です。 残念ながら、MQL関数のWebRequest() は、ダウンロードするデータの量を制限する方法がありません。 サーバー応答データを含む配列は、動的配列である必要があります。 指定されたサイズの静的配列を使用すると、プログラムはコンパイルされますが、実行時にエラーが発生し、ターミナルがクラッシュします。 Range ヘッダはサーバーへのリクエストにインクルードすることができるが、ほとんどのサーバーは Range ヘッダをサポートしていません。 したがって、Web サーバーからダウンロードされるデータの量は、ほとんどの場合、リクエストされた html ファイルのサイズです。 より良い方法は、Windowsのコンポーネントであるwininet.dllの関数を使用することです。 この関数の 1 つである InternetReadFile() は、Range ヘッダがサポートされておらず、ファイルの先頭からダウンロードを開始する場合でも、指定したバイト数をダウンロードできます。 WinINet関数は、mqlスクリプトまたはエキスパート アドバイザーにインポートできます。 この記事に添付されているファイル ScraperBot01.mq5 は、html ファイルの最初の 4 KB をダウンロードし、ダウンロードされたテキストに関連のあるメタタグを検索し、10 年 T-note の最後の利回りを表すそのタグ内のテキストを検索するスクリプトです。これを使用して、そのテキストを浮動小数点数に変換し、その値をターミナルに出力します。

ScraperBot 01

ソースコードファイルScraperBot01.mq5は、wininet.dllをインポートし、呼び出される関数をプロトタイプ化し、すべてのパラメータがMQL5と互換性のある型を宣言することによって始まります。 WinINet 関数は、https://docs.microsoft.com/en-us/windows/desktop/wininet/wininet-referenceでドキュメント化されています。

#import "wininet.dll"
  int InternetCheckConnectionW(string& lpszUrl, uint dwFlags, uint dwReserved);
  int InternetOpenW(string& lpszAgent, uint dwAccessType, string& lpszProxyName, string& lpszProxyBypass, uint dwFlags);
  int InternetOpenUrlW(int hInternetSession, string& lpszUrl, string& lpszHeaders, uint dwHeadersLength, uint dwFlags, uint dwContext);
  int InternetReadFile(int hFile, uchar& lpBuffer[], uint dwNumberOfBytesToRead, uint& lpdwNumberOfBytesRead);
  int InternetCloseHandle(int hInternet);
#import

uchar uc_Buffer[4096]; //InternetReadFile() は静的バッファを必要とします。
float f_US;

Web サーバーからダウンロードした html テキストを受信する静的配列 uc_Buffer と、テキストから解析された数値に設定される変数 f_US がグローバル スコープで宣言されます。 この記事に添付されているこのファイルおよびその他のファイルのルールは、型指定子と名前の間のアンダースコアを使用してグローバル変数を示します。 uc_Buffer のサイズは、ダウンロードされる特定のバイト数に対応するように設定されます。

OnStart()の上部にローカル変数が宣言され、その目的を明確にするために、必要に応じて宣言されるものもあります。 まず、インターネット接続が利用可能かどうかを確認します。 このスクリプトで呼び出された関数の戻り値は、成功または失敗を示すためにターミナルに出力され、エラーが発生した場合、開いているハンドルは閉じられ、スクリプトは "return" ステートメントによって閉じられます。 インターネット接続が使用可能な場合、ハンドル iNet1 は WinINet 関数へのトレーリングの呼び出しに対して初期化されます。 有効なハンドル値が 0 より大きい。

void OnStart() 
{ bool bResult;  int i, iNet1, iNet2;  

  string stURL = "http://www.msn.com"; 
  bResult = InternetCheckConnectionW(stURL, 1, 0); // 1 == FLAG_ICC_FORCE_CONNECTION
  Print("InternetCheckConnectionW() returned ", bResult);
  if(!bResult) return;
  
  string stAgent = "Mozilla/5.0", stNull = "";
  iNet1 = InternetOpenW(stAgent, //_In_ LPCTSTR lpszAgent
                        1,       // 1 == INTERNET_OPEN_TYPE_DIRECT
                        stNull,  //_In_ LPCTSTR lpszProxyName
                        stNull,  // _In_ LPCTSTR lpszProxyBypass
                        NULL);   //_In_ DWORD dwFlags
  Print("iNet1 == ", iNet1);
  if(iNet1==0) return;


次に、Web サーバーへの接続が確立され、ハンドル iNet2 を初期化して html ファイルをダウンロードします。

  stURL = "https://www.marketwatch.com/investing/bond/tmubmusd10y?countrycode=bx";
  string stHdr = "Accept: text/*";
  iNet2 = InternetOpenUrlW(iNet1,            // HINTERNET hInternet,
                           stURL,            // LPCWSTR   lpszUrl,
                           stHdr,            // LPCWSTR   lpszHeaders,
                           StringLen(stHdr), // DWORD     dwHeadersLength,
                           0x00080000,       // DWORD     dwFlags, 0x00080000 == INTERNET_FLAG_NO_COOKIES
                           NULL);            // DWORD_PTR dwContext
  Print("iNet2 == ", iNet2);
  if(iNet2==0) 
  { InternetCloseHandle(iNet1);
    return;
  }


さて、これでWeb サーバーからデータをダウンロードできるようになりました。

  uint uGet, uGot;
  uGet = 4080; //ダウンロードするバイト数
  bResult = InternetReadFile(iNet2,     // _In_  HINTERNET hFile
                             uc_Buffer, //_Out_ LPVOID lpBuffer
                             uGet,      //_In_ DWORD dwNumberOfBytesToRead
                             uGot);     //_Out_ LPDWORD lpdw NumberOfBytesRead

  Print("InternetReadFile() returned ", bResult, ". 読み取られたバイト数: ", uGot);
  InternetCloseHandle(iNet2);  //ダウンロード完了
  if(!bResult) {InternetCloseHandle(iNet1); return;}
  uc_Buffer[uGot] = 0//null 文字を追加して uc_Buffer で文字列をターミネートします。


次に、ダウンロードしたテキストの対象のメタ タグを検索し、見つかった場合はオフセットに 28 を追加して、uc_Buffer のインデックスとして数値を表すテキストとして使用できるようにします。 このテキストは StringSubstr() を呼び出し、変数 "i" でインデックスを渡すことによってアクセスされます。 そのインデックスのテキストが数値を表さない場合、StringToDouble() は、債券利回りが正確にゼロでない限り、エラーを示すゼロを返します。 文字列内のクオートは、文字列の先頭と末端のクオートと区別するために、\" としてコーディングされます。

  i = StringFind(CharArrayToString(uc_Buffer), "<meta name=\"price\" content=", 0); //0 == 検索開始ポジション
  Print("Offset of \'<meta name=\"price\" content=\' == ", i); 
  if(i == -1) {Print("String not found.");  InternetCloseHandle(iNet1);  return;} 
  i += 28; //債券利回りを表すテキストの既知の場所にインデックスを進める。
  f_US = StringToDouble(StringSubstr(CharArrayToString(uc_Buffer), i, 8));
  Print("US 10-year T-note yield, stored in variable f_US: ", f_US);
  InternetCloseHandle(iNet1); //ウィニネットで終わった
}//END void OnStart()

スクリプトScraperBot01は、任意のチャート上で実行し、ターミナルでその進捗状況を報告し、ウェブから削り取られた値をPrintすることができます。

代替案

Web ページからデータをスクレイピングするステップを示したので、債券利回りデータがメタタグに存在しない場合の取り方を考えてみましょう。 その場合は、Webページのhtmlファイルで、ページに表示される番号のソースを調べます。 債券利回りの最新のクオートを知って、テキストエディタの検索関数を使用して、数字を表すテキストの文字列を見つけることができます。 ダウンロードしたhtmlファイルでは、メタタグ以外の 3 つの場所でクオートを見つけることができます。 3つのうちの1つは、JSON-LD 構造化データ スニペットで、Web ページに関する情報を検索エンジンや Web クローラがアクセスできるように設計された html 要素です。 ここでは、わかりやすくするために改行で書式設定されたデータ スニペットを次に指定します。

<script type="application/ld+json">
{ "@context":"http://schema.org/",
  "@type":"Intangible/FinancialQuote",
  "url":"https://www.marketwatch.com/investing/bond/tmubmusd10y?countrycode=bx",
  "name":"U.S. 10 Year Treasury Note",
  "tickerSymbol":"TMUBMUSD10Y",
  "exchange":"Tullett Prebon",
  "price":"3.061",
  "priceChange":"0.007",
  "priceChangePercent":"0.22%",
  "quoteTime":"Sep 28, 2018 5:07 p.m.",
  "priceCurrency":"PERCENT"
}
</script>

アルゴリズムは最初にタグ <script type="application/ld+json"> のオフセットを検索し、その場所から ["price":"] と </script> のオフセットを検索します。 ["price":"]のオフセットがデータ スニペット内であることを示す </script>オフセットよりも小さい場合は、["price":"]のオフセットに 9 を追加して、クオートを表す数値のオフセットに到達します。 このステップは、添付スクリプト ScraperBot02.mq5 で示されています。

ScraperBot 02

このスクリプトは、uMax で指定されたサイズまで html ファイルをダウンロードします。 初めて実行する場合、uMax は、ダウンロードの予想サイズ (100 万など) をはるかに超える値に設定する必要があります。 このスクリプトはダウンロードされたバイト数をリポートし、uMax に近い場合は uMax の値を増やす必要があります。 スクリプトは、タグのファイル内のオフセットも報告します。<script type=""application/ld+json""></script> uMax の値は、タグのオフセットよりも多少高く設定できます。 この場合、オフセットは 166696 であることが判明したので、uMax は 180224 に設定され、JSON-LD スニペットをインクルードするのに十分なファイルをダウンロードできますが、ファイル全体は含められません。 このスクリプトは、静的配列を使用して、動的配列にコピーおよび累積される 16 KB のチャンクをダウンロードします。 これらの配列は、グローバル スコープで宣言されます。

uchar uc_Buffer[16400], uc_DynBuf[];

ScraperBot02 は、データを Web サーバーからダウンロードする部分まで ScraperBot01 と同じで、データをチャンクにパーセルします。 InternetReadFile は、必要な量のデータがダウンロードされるまで、do-while ループで繰り返し呼び出されます。

  uint uGet, uGot, uDst, uMax;
  uGet = 16384;    //InternetReadFile への呼び出しごとにダウンロードするバイト数は、uc_Buffer のサイズより少なくとも 1 バイト少ない必要があります。
  uGot = uDst = 0; //uGot は、InternetReadFile への呼び出しでダウンロードされたバイト数です。uDst はダウンロードされたバイトの合計数です。
  uMax = 180224;   //ダウンロードするバイトの最大数。

  do
  { bResult = InternetReadFile(iNet2,     //_In_ HINTERNET hFile
                               uc_Buffer, //_Out_ LPVOID lpBuffer
                               uGet,      //_In_ DWORD dwNumberOfBytesToRead
                               uGot);     //_Out_ LPDWORD lpdw NumberOfBytesRead

    uc_Buffer[uGot] = 0; //null 文字を追加して uc_Buffer で文字列を終了します。

    ArrayCopy(uc_DynBuf, //デスティネーション配列 
              uc_Buffer, //ソース配列 
              uDst,      //デスティネーション配列への書き込みが開始されるインデックス 
              0,         //ソース配列からのコピーが開始されるインデックス 
              uGot);     //コピーする要素の数 
    uDst += uGot; //ループ内の次のパスのデスティネーション配列の事前インデックス
  }while(bResult && uGot > 0 && uDst < uMax);
 
  Print("Size of uc_DynBuf == ", ArraySize(uc_DynBuf));
  Print("Bytes downloaded  == ", uDst);

ScraperBot02 はタグ <script type=\"application/ld+json\"> を検索し、オフセットをインデックス変数 i に格納します。 そのオフセットから ["price":"] が位置され、オフセットを j で格納します。 次に、スニペットの最後に</script>を配置し、オフセットを k で格納します。 j が k より小さい場合、9 が j に追加され、数値を表すテキストのオフセットになり、f_US の浮動小数点値に変換され、ターミナルに出力されます。

  int i, j, k; //インデックス

  i = StringFind(CharArrayToString(uc_DynBuf), "<script type=\"application/ld+json\">", 0); //0 == 検索開始ポジション 
  Print("Offset of <script type=\"application/ld+json\"> == ", i); 
  if(i == -1) {Print("<script type=\"application/ld+json\"> not found.");  InternetCloseHandle(iNet1);  return;}

  j = StringFind(CharArrayToString(uc_DynBuf), "\"price\":\"", i); //i == 検索開始ポジション 
  if(j == -1) {Print("\"price\":\" not found.");  InternetCloseHandle(iNet1);  return;}
  Print("Offset of \"price\":\" == ", j); 

  k = StringFind(CharArrayToString(uc_DynBuf), "</script>", i); //i == 検索開始ポジション
  Print("Offset of </script> == ", k); 
  if(j > k) {Print("Offset of \"price\":\" is greater than offset of </script>");  InternetCloseHandle(iNet1);  return;}

  j += 9; //債券利回りを表すテキストの既知の場所にインデックスを進めます。
  f_US = StringToDouble(StringSubstr(CharArrayToString(uc_DynBuf), j, 8));
  Print("US 10-year T-note yield, stored in variable f_US: ", f_US);
  InternetCloseHandle(iNet1); //ウィニネットで終了
}//END void OnStart()

デベロッパーツールの使用

ウェブサーバーからの引用の別のソースは、Chromeブラウザでデベロッパーツールを使用して見つけることができます。 右上隅のメニューボタンから、デベロッパーツールを開き、アドレスバーにhttps://www.marketwatch.com/investing/bond/tmubmusd10y?countrycode=bxを入力します。 中央のペーンで [ネットワーク] タブを選択し、モニターするイベントの種類として "XHR" を選択します。 イベントのいずれかを選択すると、右側にウィンドウが開き、ヘッダや応答などの詳細が表示されます。 "quoteByDialect..."というラベルの付いたイベントに応答があり、そのペーンで右クリックして "Select all." を選択して、Ctrl キーを押しながら C キーを押してクリップボードにテキストをコピーし、テキスト エディタに貼り付けます。 このクオートは、文字列の後のテキストブロックにあります ["CompositeTrading":{"Last":{"Price":{"Iso":"PERCENT","Value":]. テキストブロックを取得する URL は、[ヘッダ] タブの下にあります。 この場合、かなり長いです: >https://api.wsj.net/api/dylan/quotes/v2/comp/quoteByDialect?dialect=official&needed=CompositeTrading|BluegrassChannels&MaxInstrumentMatches=1&accept=application/json&EntitlementToken=cecc4267a0194af89ca343805a3e57af&ckey=cecc4267a0&dialects=Charting&id=Bond-BX-TMUBMUSD10Y,Bond-BX-TMBMKDE-10Y この記事からそのリンクを右クリックすると、ブラウザ ウィンドウにテキストのブロックが表示されます。 URL の末端にはコンマで区切られた 2 つのティッカー シンボル文字列があるため、実際には 2 つのテキスト ブロックが続々と表示されます。 1つ目の"Bond-BX-TMUBMUSD10Y"は米国10年物国債で、2つ目"Bond-BX-TMBMKDE-10Y"はドイツの10年国債です。 URL から 2 番目のティッカー シンボル文字列を削除すると、ダウンロードしたテキストのサイズが 7.1 KB から 3.6 KB に縮小されます。

添付スクリプトScraperBot03は、ティッカーシンボル"TMUBMUSD10Y"のテキストブロックをダウンロードし、文字列["CompositeTrading":{"Last":{"Price":{"Iso":"PERCENT","Value":]を検索し、文字列の先頭のオフセットに61を追加し、それをインデックスとして使用して、数字を表すテキストに変換し、浮動小数点番号を端末に出力します。 このスクリプトは ScrapterBot01 にモデル化されているので、ここではこのコードはクオートで囲みません。 このリソースの利点は、ダウンロードのサイズが小さいことです。 URL でアドレス指定されるファイルはわずか 3.6 KB なので、wininet.dll の関数の代わりに MQL5 関数 WebRequest を使用して、必要以上に多くのデータをダウンロードせずにダウンロードできます。

ScraperBot 04

このスクリプトは、WinINet 関数の代わりに WebRequest を使用して ScraperBot 03 と同じデータをダウンロードします。 WebRequest を機能させるには、サーバーの基本 URL (この場合は "https://api.wsj.net") を MetaTrader プラットフォームの "ToolsOptionsExpert Advisors" の下にある許可されたサーバーの一覧に含める必要があります。 グローバル char 配列 ch_Data は WebRequest にデータを渡せず、その型のパラメータの要件を満たすためだけに存在します。

char ch_Buffer[], ch_Data[16];
float f_US;

void OnStart() 
{ int i;   
  string stURL = "https://api.wsj.net/api/dylan/quotes/v2/comp/quoteByDialect?dialect=official&needed=CompositeTrading|BluegrassChannels&"
                 "MaxInstrumentMatches=1&accept=application/json&EntitlementToken=cecc4267a0194af89ca343805a3e57af&ckey=cecc4267a0&"
                 "dialects=Charting&id=Bond-BX-TMUBMUSD10Y";
  string stHdr = "Accept: text/*, User-Agent: Mozilla/5.0";
  string stRspHdr; //応答ヘッダ
  
  i = WebRequest("GET",     // const string  method,    HTTP method 
                 stURL,     // const string  url,       URL 
                 stHdr,     // const string  headers,  
                 1024,      // int           timeout, 
                 ch_Data,   // const char    &data[],   the array of the HTTP message body 
                 ch_Buffer, //char          &result[] サーバー応答データを含む配列
                 stRspHdr); // string        &result_headers 

  Print("Server response code: ", i);
  if(i == -1) {Print("GetLastError == ", GetLastError());  return;}
  Print("Size of ch_Buffer (bytes downloaded) == ", ArraySize(ch_Buffer));
  Print("Response header:\n", stRspHdr);   
 
  string stSearch = "\"CompositeTrading\":{\"Last\":{\"Price\":{\"Iso\":\"PERCENT\",\"Value\":";
  i = StringFind(CharArrayToString(ch_Buffer), stSearch, 0); //0 == 検索開始ポジション 
  Print("Offset of ", stSearch, " == ", i); 
  if(i == -1) {Print(stSearch, " not found.");  return;}
  i += 61; //債券利回りを表すテキストの既知の場所にインデックスを進める
  f_US = StringToDouble(StringSubstr(CharArrayToString(ch_Buffer), i, 8));
  Print("US 10-year T-note yield, stored in variable f_US: ", f_US);
}//END void OnStart()


その他の債券

金利差を導き出すためには、2つの金利が必要です。 米国10年物国債の利回りを取得するスクリプトに、URLの「TMUBMUSD10Y」にティッカーシンボル"TMBMKDE-10Y"を置き換えることによって、ドイツの10年国債の利回りを取得するように調整することができます。 他の国のティッカーシンボルも使用できます。 他の金融ウェブサイトでは異なるティッカーシンボルを使用する場合がありますが、同じ原則で適用できます。 ドイツ債は、ユーロに関する金利の代理としてよく使われますが、それは逆に、欧州連合の他の国々の影響を無視することになります。 ただし、ユーロの複合金利は、複数の欧州国債から導き出すことができます。 この例では、EU加盟国上位3カ国の10年国債利回り(通貨がユーロ)の観点から、複合"欧州債"レートを導き出すために使用します。 3カ国はドイツ、フランス、イタリアです。 債券利回りは、次の表に示すように、経済の相対的な大きさに応じて加重付けされます。


2017 国内総生産、数十億ユーロ

ドイツ 3,197
フランス 2,241
イタリア 1,681
合計 7,119


各国の加重付け係数は、GDPの3つの合計に対する比率になります。 ドイツは0.449、フランスは0.315、イタリアは0.236です。 これらのファクターは、次の式に従って、ユーロの債券利回りの複合値を計算する際に係数として 1 まで加算されます。

f_EU = 0.449*f_DE + 0.315*f_FR + 0.236*f_IT

ここで、f_EUは複合ヨーロッパ債利回り、f_DEはドイツ債利回り、f_FRはフランス債利回り、f_ITはイタリア債利回りです。

金利差

ユーロ/米ドルの通貨ペアは、米ドルの観点からユーロの値として引用されているので、ユーロの値と同じ方向に移動するトレンドがあり、ドルは反対の方向に移動するトレンドがあります。 同じ方向の通貨ペアの動きを予測する金利差の動きのため、欧州複合債利回りの上昇は金利差をプラス方向に動かし、米国のTノート利回りの増加は負の方向に移動する可能性があります。 したがって、金利差は f_EU - f_US として計算されます。 この記事の執筆時点では、f_EU の計算値は 1.255 で、f_US の値は 3.142 です。 したがって、金利差は 1.255 ~ 3.142 = -1.887 です。 -1.887から-1.798まで、プラス方向の動きは、EUR/USDの上向きの動きの前兆になります。 マイナス方向の動きは-1.887から-1.975まで、EUR/USDの下方の前兆になります。 インジケータの強さまたは信頼性は、金利差の移動の大きさによって異なります。 金利の動きは、通常、ベーシスポイントまたはパーセンテージポイントの100分の1で示されます。 金利差の動き -1.887 から -1.975 はマイナス方向の 8.8 ベーシスポイントの動きであり、日中ベースではかなり強い動きです。時間単位の時間枠で通貨ペアの下方の動きを示している可能性があります。 1つまたは2つのベーシスポイントの動きはノイズと考えられ、通貨ペアの動きの信頼性の高いインジケータになる可能性は低いです。

ScraperBot 05

このスクリプトは、4 つの債券利回りをすべて集め、複合ヨーロッパ債利回りを計算し、金利差をターミナルに出力します。 ScraperBot 04 の後にモデル化されていますが、債券利回りごとにサーバーに個別のリクエストを行う代わりに、4 つのティッカー シンボルすべてが URL に追加され、4 つのクオートはすべて 14 KB の連続したテキスト ブロックを含む 1 つの 14 KB のダウンロードで返されます。 ScraperBot 05 は、クオートの前の文字列を見つける前にファイル内のティッカー シンボルを検索し、文字列が見つからない場合はエラー メッセージを返します。

char ch_Buffer[], ch_Data[16];      //グローバル バッファ
float f_US, f_DE, f_FR, f_IT, f_EU; //債券利回りを保存するためのグローバル変数

void OnStart() 
{ int i;   
  string stURL = "https://api.wsj.net/api/dylan/quotes/v2/comp/quoteByDialect?dialect=official&needed=CompositeTrading|BluegrassChannels&"
                 "MaxInstrumentMatches=1&accept=application/json&EntitlementToken=cecc4267a0194af89ca343805a3e57af&ckey=cecc4267a0&"
                 "dialects=Charting&id=Bond-BX-TMUBMUSD10Y,Bond-BX-TMBMKDE-10Y,Bond-BX-TMBMKFR-10Y,Bond-BX-TMBMKIT-10Y"; //4 つのティッカー シンボル

  string stHdr = "Accept: text/*, User-Agent: Mozilla/5.0";
  string stRspHdr; //応答ヘッダ

  i = WebRequest("GET",     // const string  method,    HTTP method 
                 stURL,     // const string  url,       URL 
                 stHdr,     // const string  headers,  
                 1024,      // int           timeout, 
                 ch_Data,   // const char    &data[],   the array of the HTTP message body 
                 ch_Buffer, //char          &result[] サーバー応答データを含む配列
                 stRspHdr); // string        &result_headers 

  Print("Server response code: ", i);
  if(i == -1) {Print("GetLastError == ", GetLastError());  return;}
  Print("Size of ch_Buffer (bytes downloaded) == ", ArraySize(ch_Buffer));
   
  string stSearch = "\"CompositeTrading\":{\"Last\":{\"Price\":{\"Iso\":\"PERCENT\",\"Value\":";

//米国10年物国債利回りを取得します。
  i = StringFind(CharArrayToString(ch_Buffer),"\"Ticker\":\"TMUBMUSD10Y\"", 0); //0 == 検索開始ポジション
  if(i == -1) {Print("\"Ticker\":\"TMUBMUSD10Y\" not found.");  return;}
  i = StringFind(CharArrayToString(ch_Buffer), stSearch, i); //i == 検索開始ポジション
  Print("Offset of ", stSearch, " == ", i); 
  if(i == -1) {Print(stSearch, " not found.");  return;}
  i += 61; //債券利回りを表すテキストの既知の場所にインデックスを進める
  f_US = StringToDouble(StringSubstr(CharArrayToString(ch_Buffer), i, 8));
  Print("US 10-year T-note yield, stored in variable f_US: ", f_US);

//ドイツの10年国債利回りを取得します。
  i = StringFind(CharArrayToString(ch_Buffer),"\"Ticker\":\"TMBMKDE-10Y\"", i); //i == 検索開始ポジション
  if(i == -1) {Print("\"Ticker\":\"TMBMKDE-10Y\" not found.");  return;}
  i = StringFind(CharArrayToString(ch_Buffer), stSearch, i); //i == 検索開始ポジション
  Print("Offset of ", stSearch, " == ", i); 
  if(i == -1) {Print(stSearch, " not found.");  return;}
  i += 61; //債券利回りを表すテキストの既知の場所にインデックスを進める
  f_DE = StringToDouble(StringSubstr(CharArrayToString(ch_Buffer), i, 8));
  Print("German 10-year government bond yield, stored in variable f_DE: ", f_DE);

//フランスの10年国債利回りを取得
  i = StringFind(CharArrayToString(ch_Buffer),"\"Ticker\":\"TMBMKFR-10Y\"", i); //i == 検索開始ポジション
  if(i == -1) {Print("\"Ticker\":\"TMBMKFR-10Y\" not found.");  return;}
  i = StringFind(CharArrayToString(ch_Buffer), stSearch, i); //i == 検索開始ポジション
  Print("Offset of ", stSearch, " == ", i); 
  if(i == -1) {Print(stSearch, " not found.");  return;}
  i += 61; //債券利回りを表すテキストの既知の場所にインデックスを進める
  f_FR = StringToDouble(StringSubstr(CharArrayToString(ch_Buffer), i, 8));
  Print("French 10-year government bond yield, stored in variable f_FR: ", f_FR);

//イタリアの10年国債利回りを取得
  i = StringFind(CharArrayToString(ch_Buffer),"\"Ticker\":\"TMBMKIT-10Y\"", i); //i == 検索開始ポジション
  if(i == -1) {Print("\"Ticker\":\"TMBMKIT-10Y\" not found.");  return;}
  i = StringFind(CharArrayToString(ch_Buffer), stSearch, i); //i == 検索開始ポジション
  Print("Offset of ", stSearch, " == ", i); 
  if(i == -1) {Print(stSearch, " not found.");  return;}
  i += 61; //債券利回りを表すテキストの既知の場所にインデックスを進める
  f_IT = StringToDouble(StringSubstr(CharArrayToString(ch_Buffer), i, 8));
  Print("Italian 10-year government bond yield, stored in variable f_IT: ", f_IT);

//欧州複合債券利回りを計算
  f_EU = 0.449*f_DE + 0.315*f_FR + 0.236*f_IT;
  Print("European composite bond yield: ", f_EU);

//金利差を計算
  Print("Interest rate differential, f_EU-f_US = ", f_EU-f_US);
}//END void OnStart()

ScraperBot06.mq4 は、MetaTrader4 プラットフォームで信頼性が低いことがわかりました。WebRequest の代わりに WinINet を使用して ScraperBot05.mq5 を実装します。


デルタ

金利差の変化は、金利差そのものよりも通貨ペアのトレードに関連します。 デルタは、足の終わりに金利差の値から、前の足の終わりにその値を引いた値として表すことができます。 この単一期間のデルタは、より大きい時間枠で役立つ場合がありますが、多くの足の変更を考慮に入れるため、小さい時間枠では、差分の指数移動平均が便利です。 EMA は、現在のデルタに平滑化係数またはアルファを適用し、EMA の前の値である指定 EMAp の 1 倍に加算することによって計算されます。

EMA = a*Delta + (1-a)*EMAp

デルタは正または負の値であり、平均値がゼロであるため、EMA の以前の値が使用できない場合は、EMAp をゼロに初期化できます。 アルファの値は任意に割り当てるか、移動平均の任意に割り当てられた期間数から計算することができます。 その場合

a = 2.0 / (n+1)

ここで n は EMA 期間の数で、全体数または小数数にすることができます。 アルファの正規の範囲がゼロより大きいか、1 以下か、0 < a <= 1です。 アルファの値が 1 の場合、EMA の計算では EMA の以前の値は考慮されず、EMA は現在のデルタになります。

金利差のヒストリーデータベースが利用可能な場合、このインジケータは、通貨ペアのチャート上に金利差の時系列またはデルタのEMAを表示するようにコード化することができます。 インジケータはインターネットからデータを集めることはできませんが、インジケータは、インターネットから債券利回りデータをフェッチするスクリプトによって更新されたローカルディスクファイルからデータを集めることができます。 このようなインジケータのプログラミングは、また別の記事のトピックです。

結論

添付されたスクリプトのソースコードをエキスパートアドバイザーにコピーすれば、基本データの収集を自動化できます。 この記事で示した技術は、ウェブから債券利回りデータを取りますが、他の金融ウェブサイトなどでも通貨ペアでも適用することができます。 URL によってアドレス指定されたリソースを変更し、動作しなくなった場合は、プログラマは何が間違っているかを探すことにより、解決策が見つかり知識が身に付きます。 適切に動作するボットは、必要以上に多くのデータをダウンロードせず、あまりにも頻繁なリクエストを行いません。 債券利回りは為替レートよりもボラティリティが低く、5分に1回程度リクエストすれば、債券利回りを更新するために十分な頻度です。 同様に、Web サイトから取得した情報は必ずしもパブリック ドメインではないので、再配布してはなりません。 上手く使えば、ファンダメンタルズデータの自動収集は、自動トレードを収益性の高いものにする可能性を秘めています。