DLLなしのMT4およびMT5用ネイティブTwitterクライアント
はじめに
Tweeterは、誰もが自分のサイトに投稿できる無料のプラットフォームを提供します。投稿には、金融に関するヒントのように価値があるものと、著名人が自分の考えを表現するのと同じくらい価値がないものとがありますが、この記事ではその内容ではなく媒体に主に焦点を当てているので、さっそく始めましょう。
Tweeterにサインアップして、Tweeter APIへのアクセスに必要な多数のトークンをよくご覧ください。
同じような名前のトークンがたくさんあるため、最初はかなりわかりにくいかもしれません。基本的に、Twitter APIを使用するには、以下のトークンが必要です。- customer_token
- customer_token_secret
- access_token
- access_token_secret
注意:
デジタル署名に使用される公開鍵と秘密鍵に精通している方には、わかりやすいかと思います。
customer_tokenとcustomer_token_secretは、「Twitterアプリ」を識別するための公開鍵と秘密鍵です。Twitterアプリは、簡単に言うと、Twitter APIを利用したサービスやアクセスのIDです。
access_tokenおよびaccess_token_secretは、ユーザを「Twitterユーザ」として識別するための公開鍵と秘密鍵であり、Twitterの用語では「ユーザー認証」または「ユーザーコンテキスト」と呼ばれます。このaccess_tokenに基づいて、Twitter APIは誰がアクセスしているかを識別できます。
Twitter APIを使用して「匿名」アクセスを許可する、別のいわゆるbearer_tokenが存在します。このアクセス方法は、「アプリ認証」または「アプリコンテキスト」と呼ばれます。「ユーザーコンテキスト」がないと、Twitter APIリファレンスに詳しく記載されている一部のTwitter APIにアクセスできません。
他のプログラミング言語でコーディングできる方にとっては、これらのTwitterライブラリが参考として役立つかもしれません。これらは、APIのドキュメントだけを読んだだけでは明らかになるとは限らない実装の詳細への優れた洞察を提供する、優れたリソースです。
Tweeter APIと認証
上記のトークンの使用に焦点を合わせます。
- customer_token
- customer_token_secret
- access_token
- access_token_secret
これらのトークンを取得する方法については、YouTubeにたくさんのガイドやチュートリアルがあります。
OAuthはWebベースのAPIの認証および承認のために広く受け入れられ、広く使用されている標準で、Twitter APIも使用しています。
簡単に言うと、OAuthはデジタル署名であり、コンテンツを操作しようとすると無効になるようにデジタルコンテンツに署名する方法です。
コンテンツとその署名を正しく検証するには、そのコンテンツを作成する特定の方法とプロセスを正確に実行する必要があります。
繰り返しますが、Twitter APIドキュメントはこのプロセス全体を非常によくドキュメント化しています。
URLエンコードとパラメータの並べ替え
デジタル署名されたコンテンツの完全な正確性を保証するために、コンテンツ自体が明確に定義されている必要があります。そのため、Twitter API(正確にはOAuthメソッド)では、デジタル署名される前に、明確に定義された手順を実行するために、HTTP POSTまたはHTTP GETパラメータが必要です。
HTTP POST/HTTP GETパラメータを次のようにエンコードすることが不可欠です。
- 英数字(0~9、A~Z、a~z)とこれらの特殊文字(-、.、_、~)を除く文字はすべて、「パーセントエンコーディング(%XX)」されなければなりません。
- 「パーセントエンコーディング」の16進数値には大文字を使用する必要があります(0-9 A B C D E F)
リファレンスドキュメントおよびこのドキュメントでも、「%」および「+」文字の正しいエンコーディングについてご確認ください。
また、ここにリファレンスドキュメントから引用されているパラメータの並べ替えについてもご注意ください。
- OAuth仕様では、辞書順に並び替えるように指示されています。これは、多くのライブラリのデフォルトのアルファベット順の並び替えです。
- 同じエンコードされたキーを持つ2つのパラメータの場合、OAuth仕様は値に基づいて並び替えを続けるように指示しています。ただし、TwitterはAPIリクエストで重複したキーを受け入れません。
この要件の簡単な実装は次のとおりです。
string hex(int i) { static string h="0123456789ABCDEF"; string ret=""; int a = i % 16; int b = (i-a)/16; if(b>15) StringConcatenate(ret,ret,hex(b),StringSubstr(h,a,1)); else StringConcatenate(ret,ret,StringSubstr(h,b,1),StringSubstr(h,a,1)); return (ret); } string URLEncode(string toCode) { int max=StringLen(toCode); string RetStr=""; for(int i=0; i<max; i++) { string c = StringSubstr(toCode,i,1); ushort asc = StringGetCharacter(c, 0); if((asc >= '0' && asc <= '9') || (asc >= 'a' && asc <= 'z') || (asc >= 'A' && asc <= 'Z') || (asc == '-') || (asc == '.') || (asc == '_') || (asc == '~')) StringAdd(RetStr,c); else { StringConcatenate(RetStr,RetStr,"%",hex(asc)); } } return (RetStr); } string arrayEncode(string &array[][2]) { string ret=""; string key,val; int l=ArrayRange(array,0); for(int i=0; i<l; i++) { key = URLEncode(array[i,0]); val = URLEncode(array[i,1]); StringConcatenate(ret,ret,key,"=",val); if(i+1<l) StringConcatenate(ret,ret,"&"); } return (ret); } void sortParam(string&arr[][2]) { string k1, k2; string v1, v2; int n = ArrayRange(arr,0); // bubble sort int i, j; for(i = 0; i < n-1; i++) { // Last i elements are already in place for(j = 0; j < n-i-1; j++) { int x = j+1; k1 = arr[j][0]; k2 = arr[x][0]; if(k1 > k2) { // swap values v1 = arr[j][1]; v2 = arr[x][1]; arr[j][1] = v2; arr[x][1] = v1; // swap keys arr[j][0] = k2; arr[x][0] = k1; } } } } void addParam(string key,string val,string&array[][2]) { int x=ArrayRange(array,0); if(ArrayResize(array,x+1)>-1) { array[x][0]=key; array[x][1]=val; } }
上記の関数の使用例は次のとおりです。
string params[][2]; addParam("oauth_callback", "oob", params); addParam("oauth_consumer_key", consumer_key, params); sortParam(params);
注意:
簡略化のため、パラメータの並べ替えは不完全であり、複数の同じキーパラメータは考慮されていません。HTMLフォームのラジオボタンやチェックボックスなど、同じキーのパラメータを使用している場合は、この値を改善することをお勧めします。
HMAC-SHA1は可能な限り簡単に
OAuth署名を作成する前のもう1つのハードルは、MQLでのHMAC-SHA1ネイティブサポートの欠如です。MQLのCryptEncode()は、SHA1-HASHの構築のみをサポートしているため、ここではほとんど役に立たないことがわかります(したがって、フラグは CRYPT_HASH_SHA1です)。
それでは、CryptEncode()を使用して独自のHMAC-SHA1をコーディングしてみましょう。
string hmac_sha1(string smsg, string skey, uchar &dstbuf[]) { // HMAC as described on: // https://en.wikipedia.org/wiki/HMAC // uint n; uint BLOCKSIZE=64; uchar key[]; uchar msg[]; uchar i_s[]; uchar o_s[]; uchar i_sha1[]; uchar keybuf[]; uchar i_key_pad[]; uchar o_key_pad[]; string s = ""; if((uint)StringLen(skey)>BLOCKSIZE) { uchar tmpkey[]; StringToCharArray(skey,tmpkey,0,StringLen(skey)); CryptEncode(CRYPT_HASH_SHA1, tmpkey, keybuf, key); n=(uint)ArraySize(key); } else n=(uint)StringToCharArray(skey,key,0,StringLen(skey)); if(n<BLOCKSIZE) { ArrayResize(key,BLOCKSIZE); ArrayFill(key,n,BLOCKSIZE-n,0); } ArrayCopy(i_key_pad,key); for(uint i=0; i<BLOCKSIZE; i++) i_key_pad[i]=key[i]^(uchar)0x36; ArrayCopy(o_key_pad,key); for(uint i=0; i<BLOCKSIZE; i++) o_key_pad[i]=key[i]^(uchar)0x5c; n=(uint)StringToCharArray(smsg,msg,0,StringLen(smsg)); ArrayResize(i_s,BLOCKSIZE+n); ArrayCopy(i_s,i_key_pad); ArrayCopy(i_s,msg,BLOCKSIZE); CryptEncode(CRYPT_HASH_SHA1, i_s, keybuf, i_sha1); ArrayResize(o_s,BLOCKSIZE+ArraySize(i_sha1)); ArrayCopy(o_s,o_key_pad); ArrayCopy(o_s,i_sha1,BLOCKSIZE); CryptEncode(CRYPT_HASH_SHA1, o_s, keybuf, dstbuf); for(int i=0; i < ArraySize(dstbuf); i++) StringConcatenate(s, s, StringFormat("%02x",(dstbuf[i]))); return s; }
正確さを確認するために、Twitter APIドキュメントで作成されたハッシュと比較できます。
uchar hashbuf[]; string base_string = "POST&https%3A%2F%2Fapi.twitter.com%2F1.1%2Fstatuses%2Fupdate.json&include_entities%3Dtrue%26oauth_consumer_key%3Dxvz1evFS4wEEPTGEFPHBog%26oauth_nonce%3DkYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1318622958%26oauth_token%3D370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb%26oauth_version%3D1.0%26status%3DHello%2520Ladies%2520%252B%2520Gentlemen%252C%2520a%2520signed%2520OAuth%2520request%2521"; string signing_key = "kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw&LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE"; string hash = hmac_sha1(base_string, signing_key, hashbuf); Print(hash); // 842b5299887e88760212a056ac4ec2ee1626b549 uchar not_use[]; uchar base64buf[]; CryptEncode(CRYPT_BASE64, hashbuf, not_use, base64buf); string base64 = CharArrayToString(base64buf); Print(base64); // hCtSmYh+iHYCEqBWrE7C7hYmtUk=
WebRequestが救済へ
WebRequest()のおかげで、外部DLLを使用せずに、Web経由でREST APIに簡単にアクセスできるようになりました。
次のコードは、 WebRequest()を使用してTwitter APIへのアクセスを簡略化します。
#define WEBREQUEST_TIMEOUT 5000 //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ string SendRequest(string method, string url, string headers="", string params="", int timeout=WEBREQUEST_TIMEOUT) { char data[]; char result[]; string resp_headers; ResetLastError(); StringToCharArray(params, data); ArrayResize(data, ArraySize(data)-1); int res = WebRequest(method, url, headers, timeout, data, result, resp_headers); if(res != -1) { string resp = CharArrayToString(result); if(verbose) { Print("***"); Print("Data:"); Print(CharArrayToString(data)); Print("Resp Headers:"); Print(resp_headers); Print("Resp:"); Print("***"); Print(resp); } return resp; } else { int err = GetLastError(); PrintFormat("* WebRequest error: %d (%d)", res, err); if(verbose) { Print("***"); Print("Data:"); Print(CharArrayToString(data)); } if (err == 4014) { string msg = "* PLEASE allow https://api.tweeter.com in WebRequest listed URL"; Print(msg); } } return ""; }
WEBREQUEST()のドキュメントをお読みください。
引用:
WebRequest()関数を使用するには、[オプション]ウィンドウの[エキスパートアドバイザー]タブにある許可されたURLのリストに必要なサーバのアドレスを追加します。サーバポートは、指定されたプロトコルに基づいて「http://」の場合は80、「https://」の場合は443が自動的に選択されます。
Twitter REST APIを構築するためのヘルパー関数
以下は、Twitter API署名の作成に役立ついくつかのヘルパー関数です。
string getNonce() { const string alnum = "abcdef0123456789"; char base[]; StringToCharArray(alnum, base); int x, len = StringLen(alnum); char res[32]; for(int i=0; i<32; i++) { x = MathRand() % len; res[i] = base[x]; } return CharArrayToString(res); } string getBase(string¶ms[][2], string url, string method="POST") { string s = method; StringAdd(s, "&"); StringAdd(s, URLEncode(url)); StringAdd(s, "&"); bool first = true; int x=ArrayRange(params,0); for(int i=0; i<x; i++) { if(first) first = false; else StringAdd(s, "%26"); // URLEncode("&") StringAdd(s, URLEncode(params[i][0])); StringAdd(s, "%3D"); // URLEncode("=") StringAdd(s, URLEncode(params[i][1])); } return s; } string getQuery(string¶ms[][2], string url = "") { string key; string s = url; string sep = ""; if(StringLen(s) > 0) { if(StringFind(s, "?") < 0) { sep = "?"; } } bool first = true; int x=ArrayRange(params,0); for(int i=0; i<x; i++) { key = params[i][0]; if(StringFind(key, "oauth_")==0) continue; if(first) { first = false; StringAdd(s, sep); } else StringAdd(s, "&"); StringAdd(s, params[i][0]); StringAdd(s, "="); StringAdd(s, params[i][1]); } return s; } string getOauth(string¶ms[][2]) { string key; string s = "OAuth "; bool first = true; int x=ArrayRange(params,0); for(int i=0; i<x; i++) { key = params[i][0]; if(StringFind(key, "oauth_")!=0) continue; if(first) first = false; else StringAdd(s, ", "); StringAdd(s, URLEncode(key)); StringAdd(s, "=\""); StringAdd(s, URLEncode(params[i][1])); StringAdd(s, "\""); } return s; }
スクリプト例
これで、最初のTwitter API要求を送信する準備ができました。
void verifyCredentials() { string _api_key = consumer_key; string _api_secret = consumer_secret; string _token = access_token; string _secret = access_secret; string url = "https://api.twitter.com/1.1/account/verify_credentials.json"; string params[][2]; addParam("oauth_consumer_key", _api_key, params); string oauth_nonce = getNonce(); addParam("oauth_nonce", oauth_nonce, params); addParam("oauth_signature_method", "HMAC-SHA1", params); string oauth_timestamp = IntegerToString(TimeGMT()); addParam("oauth_timestamp", oauth_timestamp, params); addParam("oauth_token", _token, params); addParam("oauth_version", "1.0", params); sortParam(params); string query = getQuery(params, url); string base = getBase(params, url, "GET"); uchar buf[]; string key = URLEncode(_api_secret); StringAdd(key, "&"); StringAdd(key, URLEncode(_secret)); uchar hashbuf[], base64buf[], nokey[]; string hash = hmac_sha1(base, key, hashbuf); CryptEncode(CRYPT_BASE64, hashbuf, nokey, base64buf); string base64 = CharArrayToString(base64buf); addParam("oauth_signature", base64, params); sortParam(params); string o = getOauth(params); string headers = "Host:api.twitter.com\r\nContent-Encoding: identity\r\nConnection: close\r\n"; StringAdd(headers, "Authorization: "); StringAdd(headers, o); StringAdd(headers, "\r\n\r\n"); string resp = SendRequest("GET", query, headers); Print(resp); // if everything works well, we shall receive JSON-response similar to the following // NOTE: Identity has been altered to protect the innocent. // {"id":122,"id_str":"122","name":"xxx","screen_name":"xxx123","location":"","description":"", ... }
MT5チャートのTwitterクライアント例
次の画像は、インドネシアのニュースチャネルのツイートを表示しているTwitterクライアントを示しています。Twitter APIを実装したフォローアップ記事を準備しており、できるだけ早く公開したいと思っています。
図1: チャートに表示されたツイート
次は、MT5端末から投稿されたツイートを示す別のスクリーンショットです。
将来の機能強化
上記のメソッドは完成には程遠く、すべてのTwitter APIを網羅するものではありませんが、問題なく機能します。Tweeter APIをさらに詳しく知りたい方にとっては、これはよい演習です。チャートのスクリーンショットを含むメディア投稿の詳細については、後続の記事で説明します。
TwitterAPIクラスまたは一般的なOAuthクライアントクラスを構築することもできます。
3-Legged認証とPINベースの認証に関する注意
いわゆる 3-legged承認と PINベースの承認についてさらに詳しく知りたい場合があるかもしれません。
それらについて説明するのはこの記事の範囲外なので、さらにガイダンスが必要な場合は、お気軽にご連絡ください。
終わりに
Twitterは、誰もがほぼ何でも公開できる、広く受け入れられているプラットフォームです。
この記事と上記で説明したコードで、Twitter APIへのアクセスにおけるOAuthの理解についての私のこのささやかな冒険の結果をMQLコミュニティに貢献できたらと思っています。
フィードバックでの励みとなる改善のアイディアを楽しみにしています。提供されているコードはすべて、無料有料にかかわらずあらゆるプロジェクトでご自由にご利用ください。
このライブラリコード(MetaTrader 4用のSHA256、SHA384、SHA512 + HMACライブラリ)を提供してくださったGrzegorz Koryckiさんに感謝します)。これは、私のHMAC-SHA1関数の作成に影響を与えました。
楽しみと利益のためにツイートしましょう。
では、お楽しみください。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/8270
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索