<
  • Главная
Статьи

Створення бота для Telegram на мові MQL5

  1. Вступ
  2. Реєстрація нового бота
  3. Режими роботи ботів
  4. Обробка потоку повідомлень
  5. GetMe
  6. GetUpdates
  7. Робота з клавіатурою
  8. SendMessage
  9. Робота з мультимедіа
  10. SendPhoto
  11. SendChartAction
  12. приклади ботів
  13. Висновок

Вступ

12 квітня 2016 року у конференції F8 у Сан-Франциско компанія Facebook анонсувала впровадження API для ботів в свій месенджер. В цей же день вийшло велике оновлення для платформи Telegram Bot Platform. Версія 2.0 порадувала новими функціями. Схоже, що до добре забутого старого, а саме роботам, які були популярні ще в епоху ICQ, повертається інтерес. На новому витку спіралі розвитку ботам дають більш продуману функціональність, відкритий інтерфейс для програмування, мультимедійну підтримку. Загалом, тепер у них є все, щоб вони стали незамінні, коли ви хочете щось знайти, подивитися чи купити.

Ця стаття задумана як покрокове керівництво для створення ботів для Telegram на мові MQL5. Що ж таке бот? Бот (скорочення від «робот») - це спеціальний рахунок у Telegram, для якого передбачена можливість програмно обмінюватися повідомленнями. Бот працює на вашій (клієнтської) стороні, і взаємодіє з сервером Telegram за допомогою спеціального набору команд, що входять в Bot API . Перш ніж ми приступимо до створення безпосередньо самого бота, будь ласка, встановіть Telegram і авторизуйтесь в цьому сервісі. Реєстрація прив'язана до номера телефону, але крім цього, у вас може бути нік @username, за яким вас можна буде знайти через пошук. Тепер прийшов час познайомитися з папкою всіх ботів.

Реєстрація нового бота

За реєстрацію та налаштування ботів відповідає спеціальний бот @BotFather. Знайдемо його через пошук. Після додавання в список контактів почнемо спілкування з ним командою / start. У відповідь він надішле список всіх доступних команд, як показано на малюнку 1.


.

Рис.1. Список команд @BotFather.

Командою / newbot почнемо реєстрацію нового бота. Необхідно придумати два назви. Перше - ім'я (name) бота, яке можна задати на вашій рідній мові. Друге - ім'я користувача бота (username) на латиниці, що закінчується на приставку "bot". В результаті ми отримаємо токен - ключ доступу для роботи з ботом через API. Приклад реєстрації наведено на малюнку 2.


Рис.2. Реєстрація нового бота.

При бажанні можна змінити деякі параметри. Налаштування для inline режиму пропоную не чіпати. Наші боти не будуть з ним працювати. Рекомендую поставити виключно косметичні властивості:
  • / Setcommands - завдання списку підтримуваних команд. Цей список буде показуватися користувачам у вигляді підказки при введенні символу "/" у вікні чату.
  • / Setuserpic - установка фотографії профілю. Без фото бот виглядає несолідно.
  • / Setdescription - текст, який буде відображатися в якості привітання при додаванні бота в месенджер. Зазвичай тут в декількох реченнях вказується призначення бота.

Отже, бот зареєстрований. Поговоримо про те, в яких режимах його можна використовувати.

Режими роботи ботів

У Telegram є три схеми взаємодії ботів з користувачами. Перша - це приватні чати. Кожен користувач спілкується з ботом в режимі запит-відповідь незалежно один від одного, як показано на малюнку 3.


Рис.3. Бот і приватні чати.

Користувачів заохочують відправляти звіти боту. Ці повідомлення зберігаються на сервері не більше доби, потім видаляються. У бота є час, щоб запросити ці повідомлення і відповісти на них. Це основний режим, в якому будуть працювати наші боти.

Другий режим - це групові чати. Тут повідомлення, відправлене будь-яким з учасників групи, бачать все (рисунок 4).

Рис.4. Бот в груповому чаті.

Що стосується ботів, то їм через команду / setjoingroups можна дозволити вступати в групи. Якщо бот доданий в групу, то йому командою / setprivacy можна задати можливість або отримувати всі повідомлення, або тільки ті, які починаються з ознаки команди символу "/". Чесно кажучи, мені вдалося придумати тільки одне призначення для бота в цьому режимі - це збір статистики повідомлень для подальшого аналізу.

Третій режим - це робота на каналі. Канали в Telegram - це акаунти для трансляції повідомлень для широкої аудиторії, які підтримують необмежену кількість передплатників. Важливою особливістю каналів є неможливість користувачів залишати коментарі та лайки в стрічці повідомлень (зв'язок тільки одностороння). Створювати повідомлення на стрічці можуть тільки адміністратори каналу (малюнок 5).

Рис.5. Бот під обліковим записом адміністратора каналу.

У список адміністраторів можна також додавати і спамерських пошукових роботів. Таким чином це робить канал ідеальним інструментом для роздачі торгових сигналів. Трохи пізніше ми напишемо простого бота, який публікує сигнали від стандартного індикатора MACD. Створити новий публічний канал можна через меню "New Channel" месенджера. Не забудьте внести вашого бота в список адміністраторів каналу. Це робиться через вікно властивостей самого каналу. На цьому підготовчі роботи завершені, і ми можемо приступити до програмування.


Обробка потоку повідомлень

В процесі написання цієї статті стояло завдання створити клас, який візьме на себе рутину обробки повідомлень і дозволить зосередитися на логіці роботи самого бота. В результаті був написаний клас CCustomBot, який реалізує мінімально необхідний функціонал для роботи.

Спілкування з сервером відбувається за допомогою POST запитів з використанням функції WebRequst . Для кожної команди передбачений свій URL:

https://api.telegram.org/bot <TOKEN> / METHOD_NAME

де, TOKEN - токен зареєстрованого бота; METHOD_NAME - список підтримуваних методів.

Відповіді від сервера приходять в JSON форматі, тому знадобився хороший JSON парсер. Я застосував нативний парсер JSON Serialization and Deserialization . Хочу подякувати Олексію ( sergeev ) За виконану ним роботу. Також застосована панель для відображення деяких параметрів. клас CComment , Взятий з Codebase підійшов для цього завдання. Для універсальності найменування публічних методів класу були запозичені з документації по Bot API. Список методів, які вдалося реалізувати в класі, наведено нижче:

Заглибимося в програмування, щоб зрозуміти як користуватися цими функціями.


GetMe

Так як токен відсилається в кожному запиті, перш за все реалізована функція GetMe, яка перевіряє його на достовірність. Цю перевірку бажано робити при старті експерта і в разі невдачі інформувати користувача про це.

int GetMe () Значення, що повертається код помилки

У разі успіху GetMe повертає 0, і через метод Name () можна дізнатися ім'я (username) бота. Це ім'я в роботі не бере участь. Однак воно буде виведено на панель в якості інформації. Така адреса як telegram.me/ <botname> дозволяє скористатися Web-версією месенджера і буде служити посиланням для реклами вашого бота. Експерт з перевіркою токена в OnInit може мати наступний вигляд:

#property copyright "Copyright 2014 року, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property strict #include <Telegram.mqh> input string InpToken = "177791741: AAH0yB3YV7ywm80af_-AGqb7hzTR_Ud9DhQ"; CCustomBot bot; int getme_result; int OnInit () {bot.Token (InpToken); getme_result = bot.GetMe (); EventSetTimer (3); OnTimer (); return (INIT_SUCCEEDED); } Void OnDeinit (const int reason) {Comment ( ""); } Void OnTimer () {if (getme_result! = 0) {Comment ( "Error:", GetErrorDescription (getme_result)); return; } Comment ( "Bot name:", bot.Name ()); }

GetUpdates

Головна функція GetUpdates читає масив повідомлень, що зберігаються на сервері. Її необхідно буде викликати за таймером. Період оновлення таймера небажано ставити менше 1 секунди, щоб не навантажувати сервера.

int GetUpdate () Значення, що повертається код помилки

Розглянемо докладніше внутрішній зміст цієї функції. При її виклику відбувається читання і парсинг всіх непрочитаних повідомлень від користувачів. Приклад одного з таких повідомлень наведено нижче:

{ "Ok": true, "result": [{ "update_id": 349778698, "message": { "message_id": 2, "from": { "id": 198289825, "first_name": "Andriy", " last_name ":" Voitenko "," username ":" avaticks "}," chat ": {" id ": 198289825," first_name ":" Andriy "," last_name ":" Voitenko "," username ":" avaticks " , "type": "private"}, "date": 1459775817, "text": "\ / start"}}]}

Користувач з ніком avaticks надіслав боту команду / start. Завдання полягає в тому, щоб зберігати такі повідомлення і надалі відповідати на них. При цьому унікальним ідентифікатором є номер чату chat [id]. Один і той же користувач, спілкується з ботом через різні пристрої, має різні ідентифікатори чатів. Цей параметр підходить в якості унікального ключа для побудови списку чатів. В процесі роботи бот буде накопичувати масив чатів і оновлювати в кожному з них останнім прислане повідомлення. Якщо ми відповіли на нього, то таке повідомлення вважається обробленим і для нього встановлюється прапор done. Тип чату також відомий. Він може бути або приватним (private), або груповим (group).

Для написання свого бота нам необхідно все лише успадковуватися від CCustomBot і перевизначити в своєму класі віртуальну функцію ProcessMessage, яка передбачена для того, щоб реалізувати в ній свою логіку роботи. Повноцінний бот, згідно з документацією Telegram, повинен вміти відповідати на дві команди "/ start" і "/ help". Давайте ж напишемо першого бота, який буде відповідати на ці команди.

#property copyright "Copyright 2014 року, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property strict #include <Telegram.mqh> class CMyBot: public CCustomBot {public: void ProcessMessages (void) {for (int i = 0 ; i <m_chats.Total (); i ++) {CCustomChat * chat = m_chats.GetNodeAtIndex (i); if (! chat.m_new_one.done) {chat.m_new_one.done = true; string text = chat.m_new_one.message_text; if (text == "/ start") SendMessage (chat.m_id, "Hello, world! I am bot. \ xF680"); if (text == "/ help") SendMessage (chat.m_id, "My commands list: \ n / start-start chatting with me \ n / help-get help"); }}}}; input string InpToken = "177791741: AAH0yB3YV7ywm80af_-AGqb7hzTR_Ud9DhQ"; CMyBot bot; int getme_result; int OnInit () {bot.Token (InpToken); getme_result = bot.GetMe (); EventSetTimer (3); OnTimer (); return (INIT_SUCCEEDED); } Void OnDeinit (const int reason) {Comment ( ""); } Void OnTimer () {if (getme_result! = 0) {Comment ( "Error:", GetErrorDescription (getme_result)); return; } Comment ( "Bot name:", bot.Name ()); bot.GetUpdates (); bot.ProcessMessages (); } Результат його роботи представлений на малюнку 6.


Рис.6. Бот з мінімальним набором команд.

Робота з клавіатурою

Для інтерактивного спілкування з користувачем для ботів придумали "клавіатури". При відправці повідомлення для кожного чату можна відображати "клавіатуру" з наперед заданим набором клавіш. Натискаючи на клавішу користувач відправляє повідомлення з текстом, зазначеним на ній. Таким чином помітно спрощується взаємодія бота з користувачем.

У класі є три функції для роботи з клавіатурою. Перша з них створює об'єкт клавіатури.

string ReplyKeyboardMarkup (const string keyboard, const bool resize, const bool one_time) keyboard
рядок, що задає розташування клавіш resize дозвіл на зміну розмірів клавіатури one_time показувати клавіатуру тільки один раз. Після натискання на клавішу клавіатура зникає. Значення, що повертається рядок (JSON об'єкт), яку необхідно передати в якості параметра reply_markup при відправці повідомлення за допомогою SendMessage

Друга функція ховає клавіатуру.

string ReplyKeyboardHide () Значення, що повертається рядок (JSON об'єкт), яку необхідно передати в якості параметра reply_markup при відправці повідомлення за допомогою SendMessage

Третя функція дозволяє надіслати невелику панель, вид якої вказує на те, що бот очікує від вас відповіді в текстовому вигляді (клавіатура не показується).

string ForceReply () Значення, що повертається рядок (JSON об'єкт), яку необхідно передати в якості параметра reply_markup при відправці повідомлення за допомогою SendMessage

Тепер перейдемо до розгляду того, як користуватися цими функціями.

SendMessage

Клавіатура не може бути відображена або захована сама по собі. Дія надсилається разом з повідомленням. Функція SendMessage для відправки повідомлень в чат має наступний вигляд:

int SendMessage (const long chat_id, const string text, const string reply_markup = NULL) chat_id
номер чату text текст повідомлення reply markup клавіатура (JSON об'єкт) Значення, що повертається код помилки

В даному випадку клавіатура є опціональною. Ми можемо відправляти прості текстові повідомлення з наших MQL-програм. На мій погляд, ця функція більш цікава, ніж нативна SendNotification . По-перше, ми можемо відправляти повідомлення частіше (приблизно раз в секунду). По-друге, підтримується формат HTML. Також важливим бонусом є можливість відправляти смайлики.

Тelegram підтримує велику кількість смайликів (Emoji) таблицю яких можна подивитися тут . Як ви можете помітити, переважна більшість кодів смайликів знаходиться в діапазоні (1F300 - 1F700). Їх розрядність виходить за межі двобайтового кодування рядків, прийнятої в MQL5. Якщо ж прибрати старші розряди так, щоб залишилося двухбайтовое число, то отриманий діапазон (F300 - F700) потрапляє в область (E000- F8FF), яка в таблиці Unicode зарезервована для приватного використання. Таким чином, нам ніщо не заважає для відправки смайлика використовувати два молодших байти. Рядок повідомлення з класичним усміхненим смайликом з кодом U + 1F642 може мати наступний вигляд:

string text = "Have a nice day. \ xF642";

Це ж справедливо і для клавіш, адже вони, по суті, є текстом. Ніщо не заважає нам використовувати смайлики на клавішах. Давайте напишемо приклад для відображення трьох клавіш з обробником подій.

#property copyright "Copyright 2014 року, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property strict #include <Telegram.mqh> class CMyBot: public CCustomBot {private: string m_button [3]; public: void CMyBot :: CMyBot (void) {m_button [0] = "Button # 1"; m_button [1] = "Button # 2"; m_button [2] = "Button # 3"; } String GetKeyboard () {return ( "[[\" "+ m_button [0] +" \ "], [\" "+ m_button [1] +" \ "], [\" "+ m_button [2] + "\"]] "); } Void ProcessMessages (void) {for (int i = 0; i <m_chats.Total (); i ++) {CCustomChat * chat = m_chats.GetNodeAtIndex (i); if (! chat.m_new_one.done) {chat.m_new_one.done = true; string text = chat.m_new_one.message_text; if (text == "/ start" || text == "/ help") bot.SendMessage (chat.m_id, "Click on the buttons", bot.ReplyKeyboardMarkup (GetKeyboard (), false, false)); int total = ArraySize (m_button); for (int k = 0; k <total; k ++) {if (text == m_button [k]) bot.SendMessage (chat.m_id, m_button [k], bot.ReplyKeyboardMarkup (GetKeyboard (), false, false)) ; }}}}}; input string InpToken = "177791741: AAH0yB3YV7ywm80af_-AGqb7hzTR_Ud9DhQ"; CMyBot bot; int getme_result; int OnInit () {bot.Token (InpToken); getme_result = bot.GetMe (); EventSetTimer (1); OnTimer (); return (INIT_SUCCEEDED); } Void OnDeinit (const int reason) {Comment ( ""); } Void OnTimer () {if (getme_result! = 0) {Comment ( "Error:", GetErrorDescription (getme_result)); return; } Comment ( "Bot name:", bot.Name ()); bot.GetUpdates (); bot.ProcessMessages (); }

В результаті ми отримаємо повідомлення з клавіатурою, показане на малюнку 7.


Рис.7. Повідомлення з клавіатурою.

Тепер спробуємо реалізувати аналог елементів управління RadioButton і CheckBox. Наприклад, нам потрібно вибрати одну з трьох опцій, а також включити або виключити певну опцію. Зміни торкнуться лише нашого класу, тому решті код експерта залишається незмінним з попереднього прикладу.
#property copyright "Copyright 2014 року, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property strict #include <Telegram.mqh> #define MUTE_TEXT "Mute" #define UNMUTE_TEXT "Unmute" #define LOCK_TEXT "Lock" #define UNLOCK_TEXT "Unlock" #define RADIO_SELECT "\ xF518" #define RADIO_EMPTY "\ x26AA" #define MUTE_CODE "\ xF515" #define UNMUTE_CODE "\ xF514" #define LOCK_CODE "\ xF512" #define UNLOCK_CODE "\ xF513" class CMyBot: public CCustomBot {private: string m_radio_button [3]; int m_radio_index; bool m_lock_state; bool m_mute_state; public: void CMyBot :: CMyBot (void) {m_radio_button [0] = "Radio Button # 1"; m_radio_button [1] = "Radio Button # 2"; m_radio_button [2] = "Radio Button # 3"; m_radio_index = 0; m_lock_state = false; m_mute_state = true; } String GetKeyboard () {string radio_code [3] = {RADIO_EMPTY, RADIO_EMPTY, RADIO_EMPTY}; if (m_radio_index> = 0 && m_radio_index <= 2) radio_code [m_radio_index] = RADIO_SELECT; string mute_text = UNMUTE_TEXT; string mute_code = UNMUTE_CODE; if (m_mute_state) {mute_text = MUTE_TEXT; mute_code = MUTE_CODE; } String lock_text = UNLOCK_TEXT; string lock_code = UNLOCK_CODE; if (m_lock_state) {lock_text = LOCK_TEXT; lock_code = LOCK_CODE; } Return (StringFormat ( "[[\"% s% s \ "], [\"% s% s \ "], [\"% s% s \ "], [\"% s% s \ ", \ "% s% s \"]] ", radio_code [0], m_radio_button [0], radio_code [1], m_radio_button [1], radio_code [2], m_radio_button [2], lock_code, lock_text, mute_code, mute_text) ); } Void ProcessMessages (void) {for (int i = 0; i <m_chats.Total (); i ++) {CCustomChat * chat = m_chats.GetNodeAtIndex (i); if (! chat.m_new_one.done) {chat.m_new_one.done = true; string text = chat.m_new_one.message_text; if (text == "/ start" || text == "/ help") {bot.SendMessage (chat.m_id, "Click on the buttons", bot.ReplyKeyboardMarkup (GetKeyboard (), false, false)); } Int total = ArraySize (m_radio_button); for (int k = 0; k <total; k ++) {if (text == RADIO_EMPTY + "" + m_radio_button [k]) {m_radio_index = k; bot.SendMessage (chat.m_id, m_radio_button [k], bot.ReplyKeyboardMarkup (GetKeyboard (), false, false)); }} If (text == LOCK_CODE + "" + LOCK_TEXT) {m_lock_state = false; bot.SendMessage (chat.m_id, UNLOCK_TEXT, bot.ReplyKeyboardMarkup (GetKeyboard (), false, false)); } If (text == UNLOCK_CODE + "" + UNLOCK_TEXT) {m_lock_state = true; bot.SendMessage (chat.m_id, LOCK_TEXT, bot.ReplyKeyboardMarkup (GetKeyboard (), false, false)); } If (text == MUTE_CODE + "" + MUTE_TEXT) {m_mute_state = false; bot.SendMessage (chat.m_id, UNMUTE_TEXT, bot.ReplyKeyboardMarkup (GetKeyboard (), false, false)); } If (text == UNMUTE_CODE + "" + UNMUTE_TEXT) {m_mute_state = true; bot.SendMessage (chat.m_id, MUTE_TEXT, bot.ReplyKeyboardMarkup (GetKeyboard (), false, false)); }}}}};

В результаті ми отримаємо вікно ось такого виду (малюнок 8).

Рис.8. Елементи управління RadioButton і CheckBox

Як бачите, тут смайли надають налаштувань велику наочність. Крім цих елементів управління, ми також легко можемо реалізовувати ієрархічні меню з навігацією в кожне підменю. Загалом, все буде залежати від того функціоналу, який ви придумаєте і захочете реалізувати.

Варто відзначити, що в цих прикладах як одержувач повідомлення ми передаємо номер чату chat_id. Ми не можемо знати його заздалегідь, щоб відіслати повідомлення користувачу, поки він першим не почне спілкування з ботом. Звернутися до користувача за його номером мобільного телефону або @username також немає можливості. Це зроблено, очевидно, з метою безпеки, щоб боти не надсилали спам. А ось публікувати повідомлення на каналі боти можуть, звертаючись по його імені використовуючи другий варіант SendMessage.

int SendMessage (const string channel_name, const string text) channel_name
ім'я каналу у вигляді @name text текст повідомлення. Підтримуються теги HTML. Значення, що повертається код помилки

Результат роботи цієї функції можна побачити нижче, на малюнку 9.

Робота з мультимедіа

Боти вміють обмінюватися фотографіями, аудіо- і відеофайлами, а також голосовими повідомленнями, стікерами та координатами. На момент написання статті вийшла нова версія Bot API 2.0, в яку додана можливість обміну контактними даними і запрошеннями на зустріч. З усього переліку для наших ботів актуально обмінюватися тільки фотографіями.

SendPhoto

У класі реалізована можливість відправки фото з двома варіантами використання.

int SendPhoto (const long chat_id, const string local_path, string & photo_id, const string caption = NULL, const bool common_flag = false, const int timeout = 10000) chat_id номер чату local_path локальний шлях до файлу в папці <каталог Даних> \ MQL5 \ Files photo_id ідентифікатор завантаженої на сервер фотографії caption текст підпису під фото common_flag прапор розташування файлу в загальнодоступному місці всіх клієнтських терміналів \ Terminal \ Common \ Files timeout таймаут операції в мілісекундах

Приклад коду, що відправляє фото:

CCustomBot bot; string token = "208375865: AAFnuOjlZ3Wsdan6PAjeqqUtBybe0Di1or8"; bot.Token (token); string photo_id; int result = bot.SendPhoto (198289825, "EURUSD1.gif", photo_id, "screenshot"); if (result == 0) Print ( "Photo ID:", photo_id); else Print ( "Error:", GetErrorDescription (result));

Думаю, у вас будуть випадки, коли виникне необхідність відправити фото декільком користувачам або відправляти одну і ту ж фото повторно. У цьому випадку буде раціональніше завантажити фото одноразово, а для повторного відправлення використовувати ідентифікатор photo_id разом з другим варіантом функції SendPhoto:

int SendPhoto (const long chat_id, const string photo_id, const string caption = NULL) chat_id номер чату photo_id ідентифікатор завантаженої на сервер фотографії caption текст підпису під фото

SendChartAction

Уявіть, що ви обробляєте відповідь від користувача і вже готові видати йому результат. Але на створення відповіді у вас йде кілька секунд. Було б хорошим тоном оповістити користувача про те, що ви в процесі. Для цього існують події. Наприклад, поки відбувається формування скриншота графіка, щоб відправити його користувачеві, ви можете відіслати подія "відправка фото". Робиться це за допомогою SendChatAction.

int SendChatAction (const long chat_id, const ENUM_CHAT_ACTION actiona) chat_id номер чату action ідентифікатор події Всі описані вище функції були реалізовані в трьох демонстраційних ботах, про які йтиметься далі.

приклади ботів

Перший бот Telegram_Bot_EA дозволяє отримати інформацію про стан рахунку, котирування і скріншоти графіків. Його робота показана в цьому відео.


Другий бот Telegram_Search_EA надсилає результати пошуку на сайті MQL5.com. Напевно вам буде цікаво подивитися, як це працює, в наступному відео.

Третій бот Telegram_Signal_EA публікує на каналі сигнали від стандартного індикатора MACD. Думаю, ви з легкістю зможете замінити MACD на свій улюблений індикатор і задіяти цей код для себе.

#property copyright "Copyright 2014 року, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #include <Telegram.mqh> input string InpChannelName = "@forexsignalchannel"; input string InpToken = "177791741: AAH0yB3YV7ywm80af_-AGqb7hzTR_Ud9DhQ"; CCustomBot bot; int macd_handle; datetime time_signal = 0; int OnInit () {time_signal = 0; bot.Token (InpToken); macd_handle = (NULL, 0, 12, 26, 9, PRICE_CLOSE); if (macd_handle == INVALID_HANDLE) return (INIT_FAILED); return (INIT_SUCCEEDED); } Void OnTick () {datetime time [1]; if (CopyTime (NULL, 0, 0, 1, time)! = 1) return; if (time_signal! = time [0]) {if (time_signal == 0) {time_signal = time [0]; return; } Double macd [2] = {0.0}; double signal [2] = {0.0}; if (CopyBuffer (macd_handle, 0, 0, 2, macd)! = 2) return; if (CopyBuffer (macd_handle, 1, 0, 2, signal)! = 2) return; time_signal = time [0]; if (macd [1]> signal [1] && macd [0] <= signal [0]) {string msg = StringFormat ( "Name: MACD Signal \ nSymbol:% s \ nTimeframe:% s \ nType: Buy \ nPrice :% s \ nTime:% s ", _Symbol, StringSubstr (EnumToString (_Period), 7), DoubleToString (SymbolInfoDouble (_Symbol, SYMBOL_ASK), _Digits), TimeToString (time [0])); int res = bot.SendMessage (InpChannelName, msg); if (res! = 0) Print ( "Error:", GetErrorDescription (res)); } If (macd [1] <signal [1] && macd [0]> = signal [0]) {string msg = StringFormat ( "Name: MACD Signal \ nSymbol:% s \ nTimeframe:% s \ nType: Sell \ nPrice:% s \ nTime:% s ", _Symbol, StringSubstr (EnumToString (_Period), 7), DoubleToString (SymbolInfoDouble (_Symbol, SYMBOL_BID), _Digits), TimeToString (time [0])); int res = bot.SendMessage (InpChannelName, msg); if (res! = 0) Print ( "Error:", GetErrorDescription (res)); }}}

В результаті ви будете отримувати повідомлення, показані на малюнку 9.


Рис.9. Сигнали індикатора MACD.

Висновок

Ті хто хоче підключити аналітику на базі Yandex.AppMetrika для свого бота, можуть скористатися ресурсом Botan . Суть сервісу в тому, що ви надсилаєте їм повідомлення, одержувані від користувачів і можете запросити такі показники як сегментація, трекінг, когортний аналіз та інше. При цьому не потрібно залишати екосистему месенджера, тому що статистика буде надсилатися спеціальним ботом у вигляді графіків, а більш деталізований звіт буде доступний через сайт.

Сподіваюся, ця стаття спонукала вас використовувати Telegram в трейдингу. Я не ставив собі за мету зупинятися на подробицях, так як вони добре викладені в документації по Bot API. Коди, прикріплені до статті, адаптовані для роботи на обох платформах - і MetaTrader 4, і MetaTrader 5.

5. Що ж таке бот?


Новости
    Без плагина
    На сайте WordPress имеется файл, именуемый как .htaccess. Многие пользователи не предают ему особого внимания и не используют все его возможности. На самом деле файл .htaccess – это дополнительные конфигурации

    Плагин подписки wordpress
    Очень трудно найти один плагин подписки wordpress , который объединил бы в себе все виды подписок, которые так необходимы сайту. Именно поэтому я решил сделать подборку лучших плагинов, которые смогут

    Слайд-шоу с помощью плагина для WordPress UnPointZero Slider
    Плагин для cms WordPress UnPointZero Slider – новостной слайдер. Он отражает в форме слайд-шоу изображения со ссылками на ваши статьи и краткие выдержки оттуда. Его можно установить и на новостной сайт,

    Плагины для Wordpress
    С помощью этого плагина вы легко сможете интегрировать Google диск на ваш WordPress сайт или блог . Gravity Forms — лучший плагин для создания форм на WordPress, от самых простых (например, форма

    Подписки плагином JetPack: размещение и редакция формы подписки
    Вступление Здравствуйте! В этой статье я покажу, как использовать плагин JetPack для создания пользовательской формы подписки и как эту формы подписки плагином JetPack добавлять в статьи сайта, а при

    Чистка сайта WordPress плагином WP-optimize
    От автора Со временем использования система WordPress накапливает не нужные файлы, комментарии и неиспользуемые данные в базе данных. Эти файлы и данные создаются в процессе работы и нужны для этого,

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

    Резервное копирование WordPress сайта без плагинов
    Вступление Резервное копирование WordPress это второе, что нужно научиться делать после установки WordPress. Можно сколько угодно говорить о безопасности сайта и его защите, но лучшего варианта защиты

    Плагины на приват для Майнкрафт ПЕ
    > > Плагины на приват для Майнкрафт ПЕ Порой всем нам хочется попробовать себя в роли администратора сервера и испытать эту ответственность, но, к сожалению, вы не всегда все знаете о создании

    Плагин WordPress Database Backup. Архивация базы данных блога на WordPress
    Привет друзья! Сегодня на очереди еще один простой, НО, необходимый и полезный плагин — плагин WordPress Database Backup , который с легкостью и самостоятельно произведет процесс, который научно называется:

  • Виртуальный хостинг

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

  • Редизайн сайта

    Редизайн сайта – это полное либо частичное обновление дизайна существующего сайта.... 
    Читать полностью

  • Консалтинг, услуги контент-менеджера

    Сопровождение любых интернет ресурсов;- Знание HTML и CSS- Поиск и обновление контента;-... 
    Читать полностью

  • Трафик из соцсетей

    Сравнительно дешевый способ по сравнению с поисковым и контекстным видами раскрутки... 
    Читать полностью

  • Поисковая оптимизация

    Поисковая оптимизация (англ. search engine optimization, SEO) — поднятие позиций сайта в результатах... 
    Читать полностью