Связь ICQ и эксперта в MQL5

Andriy Voitenko | 26 апреля, 2010

Введение

ICQ является централизованной службой мгновенного и оффлайнового обмена текстовыми сообщениями, использующей протокол OSCAR. Для трейдера ICQ может стать как терминалом для отображения своевременной информации, так и пультом управления. В статье будет показан пример того, как в советнике реализовать ICQ клиента с минимальным набором функций.

За основу был взят и переработан проект IcqMod с открытым исходным кодом. Протокол обмена с сервером ICQ реализован в DLL модуле icq_mql5.dll. Он написан на С++ и задействует единственную Windows библиотеку winsock2. Скомпилированный модуль и исходный код для Visual Studio 2005 прилагаются к статье.

Особенности и ограничения данной реализации клиента:

Описание библиотечных функций

Описание констант и функций dll модуля находится во влючаемом файле icq_mql5.mqh.

Для установки соединения с сервером используется функция ICQConnect:

uint ICQConnect (
      ICQ_CLIENT &cl,  // переменная для хранения данных о подключении
          string host,  // имя сервера, например login.icq.com
          ushort port,  // порт сервера, например 5190
          string login, // номер учетной записи (UIN)
          string pass   // пароль для учетной записи
                );

Описание возвращаемых значений функцией ICQConnect:

Имя константы
    Значение
Описание
 ICQ_CONNECT_STATUS_OK
0xFFFFFFFF Подключение установлено
 ICQ_CONNECT_STATUS_RECV_ERROR
0xFFFFFFFE
 Ошибка чтения данных
 ICQ_CONNECT_STATUS_SEND_ERR
0xFFFFFFFD
 Ошибка отправки данных
 ICQ_CONNECT_STATUS_CONNECT_ERROR
0xFFFFFFFC
 Ошибка соединения с сервером
 ICQ_CONNECT_STATUS_AUTH_ERROR0xFFFFFFFB Ошибка авторизации: неправильный пароль или превышен лимит подключений

 

Структура для хранения данных о подключении:

struct ICQ_CLIENT
    {
        uchar  status;   // код состояния подключения 
        ushort sequence; // счетчик последовательности 
        uint   sock;     // номер сокета
    };

 На практике, для анализа состояния подключения, в этой структуре используется только переменная status, которая может принимать следующие значения:

Имя константы
Значение 
Описание
 ICQ_CLIENT_STATUS_CONNECTED0x01 Существует связь с сервером
 ICQ_CLIENT_STATUS_DISCONNECTED
0x02 Отсутствует связь с сервером

 

Частые попытки подключения к серверу могут привести к временной блокировке доступа к учетной записи. Ввиду этого необходимо выдерживать таймаут между попытками соединения с сервером.

Рекомендуемое значение таймаута - 20-30 секунд.

Для закрытия соединения с сервером служит функция ICQClose:

void ICQClose (
      ICQ_CLIENT &cl // переменная для хранения данных о подключении
              );

Для отправки текстовых сообщений используется функция ICQSendMsg:

uint ICQSendMsg (
    ICQ_CLIENT &cl,  // переменная для хранения данных о подключении.
         string uin, // номер учетной записи получателя
         string msg  // текст сообщения
                );

Возвращаемое значение равно 0x01, если сообщение успешно отправлено, и 0x00 в случае ошибки. 

Для проверки наличия входящих сообщений служит функция ICQReadMsg:

uint ICQReadMsg (
      ICQ_CLIENT &cl,  // переменная для хранения данных о подключении 
          string &uin,  // номер учетной записи отправителя 
          string &msg,  // текст сообщения 
            uint &len  // количество принятых символов в сообщении
                ); 

Возвращаемое значение равно 0x01 при наличии входящего сообщения и 0x00 если сообщение отсутствует.

Класс COscarClient

Для удобства работы с ICQ в объектно-ориентированной среде MQL5, был написан класс COscarClient. В нем, кроме базовых функций, описанных выше, реализован механизм автоматического переподключения к серверу (при autocon = true), в случае потери связи, через определенный, задаваемый таймаут (переменная timeout). Описание класса находится во включаемом файле icq_mql5.mqh и приведено ниже: 

//+------------------------------------------------------------------+
class COscarClient
//+------------------------------------------------------------------+
{
private:
  ICQ_CLIENT  client;       // хранение данных о подключении
          uint connect;      // флаг состояния подключения
      datetime timesave;     // хранение последнего времени подключения к серверу
      datetime time_in;      // хранение последнего времени чтения сообщений

public:
      string uin;            // буффер для хранения uin отпавителя для принятого сообщения
      string msg;            // буффер для хранения текста принятого сообщения
        uint len;            // количество символов в принятом сообщении
     
      string login;          // номер учетной записи отправителя (UIN)
      string password;       // пароль для UIN 
      string server;         // имя сервера    
        uint port;           // сетевой порт  
        uint timeout;        // заданеи таймаута(в секундах) между попытками подключения к сервру
        bool autocon;        // автоматическое восстановление соединения
        
           COscarClient();   // конструктор для инициализации переменных класса
      bool Connect(void);     // Установка соединения с сервером
      void Disconnect(void);  // Разрыв соединения с сервером
      bool SendMessage(string  UIN, string  msg); // Отсылка сообщения  
      bool ReadMessage(string &UIN, string &msg, uint &len); // Прием сообщения
};

Советник на основе COscarClient

Минимальный код советника для работы с ICQ используя класс COscarClient находится в файле icq_demo.mq5 и приведен ниже:

#include <icq_mql5.mqh>

COscarClient client;

//+------------------------------------------------------------------+
int OnInit()
//+------------------------------------------------------------------+
  {
   printf("Start ICQ Client");
   
   client.login      = "641848065";     //<- логин
   client.password   = "password";      //<- пароль
   client.server     = "login.icq.com";
   client.port       = 5190;
   client.Connect();
   
   return(0);
  }
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
//+------------------------------------------------------------------+
  {
   client.Disconnect();
   printf("Stop ICQ Client");
  }
//+------------------------------------------------------------------+
void OnTick()
//+------------------------------------------------------------------+
  {
   string text;
   static datetime time_out;
   MqlTick last_tick;

   // чтение сообщений
   while(client.ReadMessage(client.uin,client.msg,client.len))
     printf("Receive: %s, %s, %u", client.uin, client.msg, client.len);

   // передача котировок каждые 30 сек
   if((TimeCurrent()-time_out)>=30)
     {
      time_out = TimeCurrent();
      SymbolInfoTick(Symbol(), last_tick);
      
      text = Symbol()+" BID:"+DoubleToString(last_tick.bid, Digits())+
                  " ASK:"+DoubleToString(last_tick.ask, Digits()); 
      
      if (client.SendMessage("266690424",        //<- номер получателя 
                                        text)) //<- текст сообщения 
         printf("Send: " + text);
     }
  }
//+------------------------------------------------------------------+

В качестве демонстрации работы советника, позволяющего обмениваться текстовыми сообщениями с ICQ клиентом приведен рисунок 1.

 Рисунок 1. Обмен текстовыми сообщениями между MetaTrader 5 и ICQ2Go

Наращивание возможностей

Усложним задачу, приблизив её для практического применения.  Например, нам необходимо управлять работой нашего советника, и получать необходимую информацию удалённо, используя свой мобильный телефон или другой ПК подключенный к сети интернет. Для этого опишем набор команд для управления будущим советником. Также дополним советник функцией парсинга для декодирования принимаемых команд.

Формат, общий для всех команд, будет иметь следующий вид: 

[? | !] [команда] [параметр] [значение],

где ? - признак операции чтения; ! - признак операции записи.

Список команд приведен в таблице:

   
 help чтение Вывод справки по синтаксису и перечню команд
 info чтение Вывод данных о состоянии счета
 symb чтение Вывод рыночной цены для указанной валютной пары
 ords чтение/запись Управление открытыми ордерами
 param чтение/запись Управление параметрами советника
 close запись Завершение работы советника и закрытие терминала
 shdwn запись Выключение ПК


Советник, реализующий обработку данного набора команд находится в файле icq_power.mq5.

На рисунке 2 показана наглядная демонстрация работы советника. Команды управления поступают от КПК c установленным ICQ клиентом (рисунок 2a), а также через WAP сервер http://wap.ebuddy.com реализующий работу с ICQ (рисунок 2б). Второй вариант предпочтителен для тех, кто не хочет заниматься поиском, установкой и настройкой ПО работы с ICQ для вашего мобильного телефона.

         

Рисунок 2. Работа с советником через ICQ клиента для КПК (рисунок 2a), а также через wap сайт wap.ebuddy.com (рисунок 2б).

Визуальный компонент ICQ

В данном разделе будет кратко рассмотрен пример скрипта icq_visual.mq5 реализующего компонент, внешний вид которого приведен на рисунке 3.

 Рисунок 3. Визуальный компонент ICQ

 

Форма компонента напоминает окно Windows и построена из массивов элементов управления, таких как кнопка, поле ввода и текстовая метка.

Для удобства, на форме реализован составной элемент управления, для хранения списка учетных записей и списка контактов. Выбор значений из списка осуществляется через соответствующие кнопки навигации.

Для создания окна в стиле ICQ 6.5 заменим кнопки на графические метки. На рисунке 4 приведен внешний вид компонента, реализованный в скрипте icq_visual_skin.mq5. Для желающих создать собственное оформление компонента, достаточно разработать и заменить файл skin.bmp, отвечающий за внешний вид окна.


Рисунок 4. Цветовое оформление визуального компонента ICQ

Заключение

В статье показан один из простых способов реализации ICQ клиента для MetaTrader 5 средствами встроенного языка программирования.