MQL4: Как прочитать CSV из URL

Я использую этот URL для получения контента с веб-сайта Quandl:

https://www.quandl.com/api/v3/datasets/FRED/PAYEMS.csv?exclude_column_names=true&rows=1&api_key=my_api_key

Сервер Quandl возвращает в ответ на вышеуказанный запрос следующее значение:

2016-08-01, 144598.0

Мне нужно использовать значение 144598.0 в пределах MQL4 Script, поэтому:

Q1. Как получить содержимое по указанному выше URL-адресу для использования в MQL4 скрипте?

Очень полезный пользователь из SO (https://stackoverflow.com/users/3666197/user3666197) предоставил следующее script (оригинал находится по адресу MQL4: Чтение одного значения из CSV) (несколько частей добавлено мной), чтобы помочь мне в этом, однако я не смог заставить его работать:

// Code created with the help of Stack Overflow question
// https://stackoverflow.com/questions/39279634/mql4-read-single-value-from-csv/39284875#39284875
// Question by p.luck:
// https://stackoverflow.com/users/5551849/p-luck
// Answer by user3666197:
// https://stackoverflow.com/users/3666197/user3666197

void OnStart()
{    
     string cookie = NULL,
            headers; 
     char   post[],
            result[]; 
     int    res; 

/*   TODO:                                                                             *
 *   Must allow MT4 to access the server URL,                                          *
 *   you should add URL "https://www.quandl.com/api/v3/datasets/FRED/PAYEMS.csv" *
 *   in the list of allowed URLs                                                       *
 *   ( MT4 -> Tools -> Options -> [Tab]: "Expert Advisors" ):                          */

     string aDataSOURCE_URL = "https://www.quandl.com/api/v3/datasets/FRED/PAYEMS.csv";
     string aDataSOURCE_API = "?exclude_column_names=true&rows=1&api_key=my_api_key";

     //-- Create the body of the POST request for API specifications and API-authorization
     ArrayResize( post,
                  StringToCharArray( aDataSOURCE_API, // string   text             |--> [in]  String to copy.
                                     post,            // uchar   &array[]       <--|    [out] Array of uchar type.
                                     0,               // int      start =  0       |--> [in]  Position from which copying starts. Default - 0. 
                                     WHOLE_ARRAY,     // int      count = -1       |--> [in]  Number of array elements to copy. Defines length of a resulting string. Default value is -1, which means copying up to the array end, or till terminating '\0'. Terminating zero will also be copied to the recipient array, in this case the size of a dynamic array can be increased if necessary to the size of the string. If the size of the dynamic array exceeds the length of the string, the size of the array will not be reduced.
                                     CP_UTF8          // uint     cp    = CP_ACP   |--> [in]  The value of the code page. For the most-used code pages provide appropriate constants.
                                     )
                  - 1
                  );

//-- Reset the last error code
     ResetLastError();

//-- Loading a html page from Quandl
     int timeout = 5000;                                                //-- Timeout below 1000 (1 sec.) is not enough for slow Internet connection

     res = WebRequest( "POST",              // const string  method            |--> [in]  HTTP method.
                        aDataSOURCE_URL,    // const string  URL               |--> [in]  URL.
                        cookie,             // const string  cookie            |--> [in]  Cookie value.
                        NULL,               // const string  referrer          |--> [in]  Value of the Referer header of the HTTP request.
                        timeout,            //       int     timeout           |--> [in]  Timeout in milliseconds.
                        post,               // const char   &data              |--> [in]  Data array of the HTTP message body
                        ArraySize( post ),  //       int     data_size         |--> [in]  Size of the data[] array.
                        result,             //       char   &result         <--|    [out] An array containing server response data.
                        headers             //       string &result_headers <--|    [out] Server response headers.
                        );
//-- Check errors
     if ( res == -1 )
     {    Print( "WebRequest Error. Error code  = ", GetLastError() );  //-- Perhaps the URL is not listed, display a message about the necessity to add the address
          MessageBox( "Add the address '" + aDataSOURCE_URL + "' in the list of allowed URLs on tab 'Expert Advisors'", "Error", MB_ICONINFORMATION );
          }
     else //-- Load was successfull
     {    
          PrintFormat( "The data has been successfully loaded, size = %d bytes.", ArraySize( result ) );

          //-- parse the content ---------------------------------------
          /*
              "2016-08-01, 144598.0"

          */
          //-- consume the content -------------------------------------
          //...


          }
     }

Я добавил URL из
https://www.quandl.com/api/v3/datasets/FRED/PAYEMS.csv
в список allowed URLs из MT4.

Если я включаю AutoTrading и перетаскиваю script с именем (tutorial7) на chart из USDCAD,M1,
я получаю следующие сообщения в пределах Experts tab:

  • Script tutorial7 USDCAD,M1: loaded successfuly
  • tutorial7 USDCAD,M1: initialized
  • tutorial7 USDCAD,M1: The data has been successfully loaded, size = 0 bytes
  • tutorial7 USDCAD,M1: uninit reason 0

Ведь если "The data has successfully loaded", то не должно быть "size = 0 bytes"?

Должен ли этот script работать правильно, если я просто скопирую и вставлю его прямо в MetaQuotes Language Editor и compile?

Помимо добавления URL к allowed URLs в MT4 и копирования и вставки этого кода в script, есть ли что-нибудь еще, что я должен сделать?

Если да, то как?

Я запускаю приведенный выше код как Script не как Expert Advisor; это нормально?

Вопрос 2. Могу ли я установить полученное значение 144598,0 в качестве переменной в моем скрипте?

Мне нужно сделать value из 144598.0 переменной, чтобы ее можно было сравнить с другим value.

Будет ли что-то вроде этой работы:

void OnStart()
{

... // above code which fetches the value from the .csv URL

double x = 155876.0     // value manually typed in
       y = 144598.0     // value fetched from the .csv URL using the above code
                        // ignores column 1 consisting of 2016-08-01
   if ( x > y ) {
                        // execute code
} 

else { 
                        // execute other code
}

}

person p.luck    schedule 07.09.2016    source источник


Ответы (1)


A2: ДА! Это самая легкая часть пути.
A1: НО!
Как это работает на практике?
Как MetaTrader Terminal на самом деле общается с Quandl , так чтобы получить его?

Позвольте мне сначала проиллюстрировать проблему удаленной обработки на HttpServer стороне.

Существует простая в прототипировании программа curl (доступны версии для Linux и DOS), которая будет показывать прямо в окне терминала (или в окне Windows cmd), как удаленный HttpServer в Quandl отвечает на различные композиции локально собранных запросов, передаваемых по HTTP-протоколу.

Обратите внимание, что только что повторно введенный URL-адрес создает всю историю для доставки.

C:\>curl https://www.quandl.com/api/v3/datasets/FRED/PAYEMS.csv
DATE,VALUE
2016-08-01,144598.0
2016-07-01,144447.0
...
..
.
1939-03-01,30280.0
1939-02-01,30101.0
1939-01-01,29923.0

Как только мы добавим дополнительные параметры к простому URL, удаленная сторона ( HttpServer ) изменит ответ только на одну интересующую нас строку:

C:\>curl -G --data rows=1 --data exclude_column_names=true https://www.quandl.com/api/v3/datasets/FRED/PAYEMS.csv
2016-08-01,144598.0

Круто, выглядит отлично, почти единственное значение, которое мы хотим!

Но тут приходит волшебство.

Реальный обмен (диалог) между локальным процессом (сейчас curl, но терминал MetaTrader позже) выглядит так (используя параметр --verbose в командной строке curl):

C:\>curl --verbose -G --data rows=1 --data exclude_column_names=true https://www.quandl.com/api/v3/datasets/FRED/PAYEMS.csv
*   Trying 54.174.87.84...
* Connected to www.quandl.com (54.174.87.84) port 443 (#0)
* schannel: SSL/TLS connection with www.quandl.com port 443 (step 1/3)
* schannel: checking server certificate revocation
* schannel: sending initial handshake data: sending 70 bytes...
* schannel: sent initial handshake data: sent 70 bytes
* schannel: SSL/TLS connection with www.quandl.com port 443 (step 2/3)
* schannel: failed to receive handshake, need more data
* schannel: SSL/TLS connection with www.quandl.com port 443 (step 2/3)
* schannel: encrypted data buffer: offset 4096 length 4096
* schannel: encrypted data length: 4017
* schannel: encrypted data buffer: offset 4017 length 4096
* schannel: received incomplete message, need more data
* schannel: SSL/TLS connection with www.quandl.com port 443 (step 2/3)
* schannel: encrypted data buffer: offset 4569 length 5041
* schannel: sending next handshake data: sending 318 bytes...
* schannel: SSL/TLS connection with www.quandl.com port 443 (step 2/3)
* schannel: encrypted data buffer: offset 51 length 5041
* schannel: SSL/TLS handshake complete
* schannel: SSL/TLS connection with www.quandl.com port 443 (step 3/3)
* schannel: incremented credential handle refcount = 1
* schannel: stored credential handle in session cache
> GET /api/v3/datasets/FRED/PAYEMS.csv?rows=1&exclude_column_names=true HTTP/1.1
> Host: www.quandl.com
> User-Agent: curl/7.45.0
> Accept: */*
>
* schannel: client wants to read 16384 bytes
* schannel: encdata_buffer resized 17408
* schannel: encrypted data buffer: offset 0 length 17408
* schannel: encrypted data got 653
* schannel: encrypted data buffer: offset 653 length 17408
* schannel: decrypted data length: 623
* schannel: decrypted data added: 623
* schannel: decrypted data cached: offset 623 length 16384
* schannel: encrypted data buffer: offset 0 length 17408
* schannel: decrypted data buffer: offset 623 length 16384
* schannel: schannel_recv cleanup
* schannel: decrypted data returned 623
* schannel: decrypted data buffer: offset 0 length 16384
< HTTP/1.1 200 OK
< Cache-Control: private
< Content-Disposition: attachment; filename=FRED-PAYEMS.csv
< Content-Transfer-Encoding: binary
< Content-Type: text/csv
< Date: Wed, 07 Sep 2016 12:47:20 GMT
< ETag: W/"adfdb97850c493cdd03e2036574bc404"
< Server: openresty
< Vary: Origin
< X-API-Version: 2015-04-09
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-Rack-CORS: preflight-hit; no-origin
< X-RateLimit-Limit: 50
< X-RateLimit-Remaining: 42
< X-Request-Id: c609e92d-22d2-40e7-b7d4-cacb07467c76
< X-Runtime: 0.023534
< X-XSS-Protection: 1; mode=block
< Content-Length: 20
< Connection: keep-alive
<
2016-08-01,144598.0
* Connection #0 to host www.quandl.com left intact

Обратите внимание на строку GET /api/v3/datasets/FRED/PAYEMS.csv?rows=1&exclude_column_names=true.

Таким образом, магия заключается в том, чтобы заставить терминал MetaTrader собрать то же самое, вместе с разрешением URL-адреса в списке разрешенных в конфигурации (что вы уже сделали в другом посте).

Также могли заметить, что HTTP GET отправляет только <HttpServer_relative_part_of_the.URL>


Магия заключается в том, чтобы заставить код MQL4 отправить тот же запрос, что и выше, и вернуть данные.

WebRequest() должен использовать HTTP GET, так как Quandl HttpServer не отвечает на HTTP POST версию того же примера запроса, возвращая 0 байтов (просто опустите переключатель -G из curl примеров выше ).

Выполнение всех условий сразу должно привести к получению 2016-08-01,144598.0 и использованию:

int    anAmountOfBytesRECVd   = 0;          // how many bytes Quandl sent 
string headers_FromHttpSERVER;              // stores as a string
char   anAnswerFromHttpSERVER[];            // stores as a byte-array ( char[] )
double y_value = NULL;

anAmountOfBytesRECVd = WebRequest( "GET",   // MUST use HTTP GET <- Quandl tested
                                    ...
                                    anAnswerFromHttpSERVER,
                                    headers_FromHttpSERVER
                                    );

y_value = StrToDouble( CharArrayToString( anAnserFromHttpSERVER, // [2|0|1|6|-|0|8|-|0|1|,|1|4|4|5|98|.|0]
                                          11,                    //-----------------------^_______________( -1 == TILL EndOfSTRING )
                                          -1
                                          )
                       );
person user3666197    schedule 07.09.2016
comment
Итак, я добавляю этот сценарий кода в свой первоначальный сценарий в своем вопросе? Я все еще очень не уверен в этом - я ценю ваши усилия и ваш ответ, поскольку они очень полезны, однако я все еще очень новичок в MQL4. Должен ли я скомпилировать все это вместе и сохранить как скрипт? - person p.luck; 08.09.2016
comment
Подтверждено, что WebRequest() разрешено вызывать как из EA, так и из типа кода Script. Сам код необходимо скорректировать, чтобы правильно передать атрибуты, как показано в примере GET с curl --verbose, поэтому будут иметь место небольшие корректировки, однако разбор текста ответа удаленного сервера httpServer в один double y_value продемонстрирован выше и должен предоставить вам нужны преобразования. - person user3666197; 08.09.2016