CRUD-операции в Firebase с использованием MQL
Введение
Если вы когда-либо пробовали создавать мобильное или веб-приложение, вы знаете, что управление серверными задачами может быть одной из самых сложных и затратных по времени частей разработки. Именно здесь Firebase, популярная платформа Google типа BaaS (backend-as-a-service), проявляет себя особенно хорошо. Она избавляет от значительной части рутинной серверной работы, позволяя разработчикам больше сосредоточиться на реализации функций и меньше — на администрировании инфраструктуры.
Firebase предлагает широкий инструментарий для разработки приложений — аутентификацию, облачные функции, аналитику, хостинг, отчеты о сбоях и многое другое. Однако к числу самых мощных и часто используемых возможностей относятся инструменты работы с базами данных. Firebase предоставляет две основные NoSQL-базы данных: Firebase Realtime Database и Cloud Firestore. Хотя они решают похожие задачи, у каждой есть свои сильные стороны, а выбор между ними в значительной степени зависит от требований конкретного проекта.
Firebase Realtime Database
Firebase Realtime Database существует с ранних дней Firebase и особенно хорошо подходит для приложений, где критичны низкая задержка и мгновенная синхронизация данных между клиентами. Это делает ее хорошим выбором для чатов, многопользовательских игр, панелей мониторинга в реальном времени и любых других приложений, где пользователи должны видеть обновления в реальном времени без перезагрузки экрана.
Realtime Database использует простое плоское JSON-дерево для хранения данных. Благодаря простоте с ней легко начать работу, а обновления могут выполняться очень быстро даже при медленном соединении. Однако из-за плоской структуры по мере усложнения данных работать с ней становится сложнее. Вложенные данные, глубокие связи и фильтрованные запросы требуют более тщательного планирования по сравнению с традиционной базой данных.
Cloud Firestore
Cloud Firestore, часто называемый просто Firestore, — более новый из двух инструментов Firebase для работы с базами данных. Он создавался с учетом масштабирования, структурированного хранения данных и расширенных запросов. Firestore поддерживает более иерархическую документную модель данных. Он организует данные в коллекции и документы, предоставляя разработчикам более структурированный и масштабируемый способ управления сложными связями данных.
Firestore также включает мощные возможности, такие как:
- Расширенные запросы — фильтрация по нескольким полям, сортировка результатов, пагинация запросов
- Офлайн-поддержка данных — автоматическая синхронизация данных после повторного подключения устройства
- Серверные временные метки и атомарные операции
- Более детализированные правила безопасности
- Более тесная интеграция с другими сервисами Google Cloud
В целом, если ваше приложение относительно небольшое и в нем важны скорость и простота, Realtime Database может оказаться более подходящим вариантом. Если вы создаете более амбициозный проект, которому нужны сложное моделирование данных и поддержка работы на больших масштабах, вероятнее всего, стоит выбрать Firestore.
Настройка проекта Firebase и базы данных (пошагово)
Теперь, когда у вас есть более четкое представление о базах данных Firebase и различиях между ними, разберем настройку с нуля. Мы рассмотрим, как создать проект Firebase, выбрать базу данных и настроить базовые параметры, такие как правила безопасности и структура данных.
Шаг 1: создайте проект Firebase
Чтобы начать использовать Firebase, вам понадобится аккаунт Google. Перейдите в Firebase Console и нажмите кнопку «Add Project». Вам будет предложено указать имя проекта (оно может быть любым), а также при желании включить Google Analytics для проекта. На этом этапе его можно пропустить или включить, если в дальнейшем вы хотите отслеживать поведение пользователей.
После нажатия «Create Project» Firebase выполнит подготовку ресурсов в фоновом режиме. Через несколько секунд проект будет готов.
Шаг 2: добавьте приложение в Firebase
Прежде чем использовать возможности Firebase, необходимо зарегистрировать приложение — веб-приложение, Android или iOS. Нажмите соответствующую иконку платформы (например, символ веб-приложения </>) и следуйте шагам регистрации. Для веб-приложения будет выдан конфигурационный фрагмент с параметрами проекта Firebase, который нужно вставить в код.
Конфигурационный фрагмент (snippet) может выглядеть так:
const firebaseConfig = { apiKey: "YOUR_API_KEY", authDomain: "your-app.firebaseapp.com", projectId: "your-app", storageBucket: "your-app.appspot.com", messagingSenderId: "SENDER_ID", appId: "APP_ID" };Используйте эту конфигурацию для инициализации Firebase в своем приложении.
Шаг 3: включите нужную базу данных
После настройки проекта и приложения пора выбрать базу данных. В Firebase Console найдите раздел «Build» в левом меню и выберите Realtime Database или Cloud Firestore, в зависимости от того, что вы хотите использовать.
Нажмите «Create Database» и выберите местоположение данных. Для большинства приложений подойдет регион по умолчанию, но для лучшей производительности можно выбрать регион ближе к вашей пользовательской базе.
Шаг 4: настройте правила безопасности
Базы данных Firebase очень гибкие, однако такая гибкость требует ответственного подхода. По умолчанию Firebase ограничивает доступ к базе данных из соображений безопасности. Во время настройки вам предложат выбрать режим правил безопасности. Для разработки или тестирования можно временно использовать «test mode», который делает базу данных общедоступной для чтения и записи. Главное — не используйте этот режим в рабочей среде!
Так выглядит тестовый режим:
{
"rules": {
".read": "true",
".write": "true"
}
} Для рабочей среды следует написать более точные правила, которые определяют, кто и какие части данных может читать или записывать. Правила безопасности Firebase могут основываться на аутентификации пользователя, полях документа, параметрах запроса и других условиях.

-
URL базы данных и API-ключ: для аутентификации запросов вам понадобятся API-ключ проекта Firebase и URL базы данных. Они находятся в настройках проекта в разделах «Web API Key» и «Realtime Database URL» (или REST-эндпоинт Firestore).
-
Понимание REST-эндпоинтов: базы данных Firebase доступны через REST API. Например:
-
Realtime Database: https://PROJECT_ID.firebaseio.com/path/to/data.json
-
Firestore: https://firestore.googleapis.com/v1/projects/PROJECT_ID/databases/(default)/documents/path/to/document
-
Чтобы показать, как работают операции чтения и записи, мы начнем с создания новой коллекции — логического контейнера для организации документов — и вручную заполним ее тестовыми записями пользователей. Эта настройка станет основой для дальнейшей практики получения данных и даст целостное представление о том, как работает Firestore.
Создайте первую коллекцию
Нажмите кнопку Start Collection. Откроется модальное окно, где нужно указать имя коллекции. Назовем ее users, поскольку мы будем имитировать небольшую базу пользователей.
После задания имени коллекции вам предложат создать первый документ внутри нее. Firestore автоматически генерирует случайный ID документа, если вы не хотите задавать его вручную, но для наглядности создадим его сами — используем простой вариант, например users .
Добавьте поля в документ
После того как документ получил ID, нужно заполнить его полями. Думайте о них как о столбцах в традиционной базе данных, но с гораздо большей гибкостью.
Можно начать, например, с такого поля:
-
name → "Jane Doe"
Поскольку наша цель — автоматически добавлять данные с помощью кода MQL5, описанная выше ручная часть не так важна. Теперь подключимся к базе данных.
Подключение к базе данных
REST-эндпоинт для нашей базы данных "my-users-mql5" будет таким:
https://firestore.googleapis.com/v1/projects/my-users-mql5/databases/(default)/documents/users/
Если вставить этот URL в браузер сейчас, вы, скорее всего, увидите:

Когда коллекция users настроена, вы готовы начать изучать чтение данных из Firestore с помощью кода. На следующих шагах мы разберем, как добавлять данные, получать документы, отслеживать обновления в реальном времени и использовать запросы для фильтрации и сортировки данных.
Создание данных
HTTP-метод POST предназначен для отправки данных в указанный ресурс, другими словами — для передачи данных на сервер. При выполнении POST-запроса данные обычно передаются в теле запроса, а сервер затем обрабатывает их и создает новый ресурс (например, новый аккаунт пользователя, новую запись блога или новую запись в базе данных).
Это отличается от GET-запроса, который используется только для получения данных и не должен ничего изменять на сервере. POST, напротив, связан с отправкой и изменением — он создает что-то новое.
В каждом случае клиент отправляет структурированные данные (обычно в формате JSON) на сервер или напрямую в бессерверную функцию, которая затем сохраняет эти данные где-либо — часто в базе данных вроде Firestore.
void add_data() { string url = "https://firestore.googleapis.com/v1/projects/my-users-mql5/databases/(default)/documents/users"; string jsonData = "{\"fields\":{\"Name\":{\"stringValue\":\"Jane Doe\"}}}"; uchar postData[], result[]; StringToCharArray(jsonData, postData, 0, StringLen(jsonData), CP_UTF8); string header; int res = WebRequest("POST", url, "Content-Type: application/json\r\n", 5000, postData, result, header); if(res == 200) { Print("Success: ", CharArrayToString(result)); } else { Print("Error ", res, ": ", CharArrayToString(result)); } }

С помощью приведенного выше кода мы добавили новую запись "Jane Doe" с уникальным ID, сгенерированным Firebase, что предотвращает конфликты ключей даже при одновременном добавлении данных. Можно добавлять сколько угодно записей, включая вложенные поля вроде email, age или timestamp, задавая их как пары «ключ — значение» в соответствующей структуре данных. Следующий шаг — получить эти данные.
Чтение данных
Теперь, когда база данных уже создана и содержит данные, пора начать полноценно с ней работать. Мы уже создали коллекцию с тестовыми документами и научились отправлять данные в Firestore методом POST. Это хорошее начало, но чтение данных не менее важно, чем запись. Во многих приложениях именно получение и отображение данных является основой пользовательского опыта.
Загружаете ли вы список пользователей, показываете новостную ленту или выводите каталог товаров, вам понадобится способ получать информацию из базы данных. Здесь и применяется метод GET.
GET — один из основных методов HTTP-запросов. В отличие от POST, который используется для отправки данных и создания новых ресурсов, GET применяется для получения информации с сервера. По сути, это операция только для чтения: отправляя GET-запрос, вы просите сервер вернуть данные, но не меняете ничего на стороне сервера.
Простая аналогия: POST-запрос похож на отправку формы для создания нового аккаунта, а GET-запрос — на вход в систему и просмотр информации профиля. Вы ничего не меняете, а только получаете уже существующие данные.
void authorize() { // 1. Construct Firestore URL string url = "https://firestore.googleapis.com/v1/projects/my-users-mql5/databases/(default)/documents/users/"; // 2. Prepare headers string headers; headers += "Content-Type: application/json\r\n"; // 3. Send request char data[], result[]; int timeout = 5000; int status = WebRequest("GET", url, headers, timeout, data, result, headers); // 4. Handle response if(status == 200) { // Print("Firestore Data Received:"); int filehandle = FileOpen("firebase-temp.txt", FILE_WRITE | FILE_BIN); if(filehandle != INVALID_HANDLE) { //--- Saving the contents of the result[] array to a file FileWriteArray(filehandle, result, 0, ArraySize(result)); //--- Closing the file FileFlush(filehandle); FileClose(filehandle); } else Print("Error in FileOpen. Error code =", GetLastError()); } else { Print("Error Code: ", status); Print("Response: ", CharArrayToString(result)); } }
Всего несколькими строками кода мы получили файл, содержащий всю информацию из базы данных "firebase-temp.txt"
Права доступа: убедитесь, что правила безопасности Firestore разрешают чтение там, где это необходимо. Для публичного тестирования можно использовать открытые правила ( allow read: if true; ), но для рабочей среды их нужно ужесточить!
Обновление данных
Как вы, вероятно, уже заметили, каждый раз при добавлении нового документа в базу данных Firestore — вручную через консоль или программно с помощью Firebase SDK — ему назначается уникальный ID документа. Такой ID может выглядеть как случайная строка букв и цифр, например kJ73sd98ASQv, и на первый взгляд может показаться не таким важным.
Firestore устроен вокруг коллекций и документов. Коллекция содержит несколько документов, а каждый документ хранит данные в виде пар ключ-значение. При добавлении документа в коллекцию можно либо позволить Firestore автоматически сгенерировать ID, либо указать его вручную.
Например, при добавлении пользователя в коллекцию users у вас может получиться такой путь:
users/kJ73sd98ASQv
Это означает, что документ этого пользователя находится внутри коллекции users и однозначно идентифицируется ID kJ73sd98ASQv. Если позже нужно получить, обновить или удалить именно этот документ, необходимо сослаться на полный путь, включая ID документа.
Но важно понимать: ID документа критически важен. Фактически это уникальный идентификатор, который позволяет Firestore (и, соответственно, вашему приложению) эффективно находить, изменять или удалять конкретную часть данных. Думайте о нем как об отпечатке пальца: у двух документов не бывает одного и того же ID, и именно этот ID обеспечивает прямое и точное взаимодействие с каждым отдельным документом.
Поэтому при обновлении или удалении данных в Firestore знание и использование ID документа абсолютно необходимо.
В примере с "Jane Doe" REST-эндпоинт добавленного документа выглядит так:
https://firestore.googleapis.com/v1/projects/my-users-mql5/databases/(default)/documents/users/NoQ8m2vYLnGkykNhUjFEИтак, ID нашего документа: NoQ8m2vYLnGkykNhUjFE Чтобы обновить эти данные, можно использовать метод "PATCH":
void update_entry(string doc_id) { string url = "https://firestore.googleapis.com/v1/projects/my-users-mql5/databases/(default)/documents/users/" + doc_id; //end point url string jsonData = "{\"fields\":{\"exampleField\":{\"stringValue\":\"Princess Doe\"}}}"; //specifiy data to be added uchar postData[], result[]; StringToCharArray(jsonData, postData, 0, StringLen(jsonData), CP_UTF8); //convert plain string to char array to be sent to the db string header; int res = WebRequest("PATCH", url, "Content-Type: application/json\r\n", 5000, postData, result, header); if(res == 200) { Print("Success: ", CharArrayToString(result)); //if successful, print } else { Print("Error ", res, ": ", CharArrayToString(result)); //return error } }
С помощью этого кода мы обновим имя с "Jane Doe" на "Princess Doe".

Удаление записей
Последняя операция в этой серии — удаление записи. Чтобы удалить запись вроде "Jane Doe" из Cloud Firestore, можно использовать метод DELETE для конкретного пути к документу, указывающего на уникальный ID записи. Например, DELETE-запрос к документу с автоматически сгенерированным ID навсегда удалит эту запись из базы данных. Обработку успешного выполнения и ошибок можно добавить в код приложения, чтобы пользователь получал корректную обратную связь. Кроме того, правила безопасности Firebase должны ограничивать права на удаление, предотвращая случайное или злонамеренное удаление данных неавторизованными пользователями.
void delete_entry(string documentId) { string url = "https://firestore.googleapis.com/v1/projects/my-users-mql5" + "/databases/(default)/documents/users/" + documentId; //end point url with specified document ID uchar result[]; uchar postData[]; // Empty payload for DELETE string headers = "Content-Type: application/json\r\n"; string responseHeaders; // To store response headers (unused here) // Correct WebRequest overload: int res = WebRequest( "DELETE", // HTTP method url, // Full URL with document ID headers, // Request headers 5000, // Timeout (5 seconds) postData, // Empty payload (uchar array, not NULL) result, // Response data responseHeaders // Response headers (ignored) ); if(res == 200) { Print("Document deleted"); //sucess } else { Print("Error ", res, ": ", CharArrayToString(result)); } }
При указании ID документа запись будет удалена.
Использование Firebase в алгоритмической торговле
Теперь, когда мы изучили применение простых CRUD-функций в MQL, можно рассмотреть практический пример использования этих знаний в алгоритмической торговле. В этом примере мы будем отправлять торговую информацию из терминала в Firebase, а затем отображать сигнал на панели мониторинга.
Отправка торговой информации в Firebase
Мы можем использовать базовый код MQL5 для получения информации о закрытых позициях, после чего эти данные будут отправляться в базу данных для обработки.
datetime last_closed_time; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { last_closed_time = TimeCurrent(); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void add_data(ulong ticket, string symbol, double lot, double profit) { string url = "https://firestore.googleapis.com/v1/projects/my-users-mql5/databases/(default)/documents/users"; // Build JSON dynamically using function parameters string trade_id = "Trade_" + IntegerToString((int)ticket); string jsonData = "{" "\"fields\":{" "\"" + trade_id + "\":{" "\"mapValue\":{" "\"fields\":{" "\"symbol\":{\"stringValue\":\"" + symbol + "\"}," "\"lot\":{\"doubleValue\":" + DoubleToString(lot, 2) + "}," "\"profit\":{\"doubleValue\":" + DoubleToString(profit, 2) + "}" "}" "}" "}" "}" "}"; uchar postData[], result[]; StringToCharArray(jsonData, postData, 0, StringLen(jsonData), CP_UTF8); string header; int res = WebRequest("POST", url, "Content-Type: application/json\r\n", 5000, postData, result, header); if(res == 200) { Print("Success: ", CharArrayToString(result)); } else { Print("Error ", res, ": ", CharArrayToString(result)); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnTrade() { HistorySelect(0, TimeCurrent()); for(int i = 0; i < HistoryDealsTotal(); i++) { if(deal.SelectByIndex(i)) { if(deal.Time() >= last_closed_time && last_closed_time != 0) { if(deal.Entry() == DEAL_ENTRY_OUT) { add_data(deal.Ticket(), deal.Symbol(), deal.Volume(), deal.Profit()); } } } } last_closed_time = TimeCurrent(); }

Синхронизация портфеля из MQL5 в Firestore
Каждая отправленная запись может быть сохранена, загружена и даже прочитана построчно перед отображением на панели мониторинга. Такой подход дает понятный и организованный способ анализа торговой истории без бесконечной прокрутки журналов или использования скриншотов. Благодаря такой структуре трейдеры могут эффективнее визуализировать свою результативность и находить закономерности, которые иначе могли бы остаться незамеченными.
int OnInit() { //--- draw_dashboard(); get_data(); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void draw_dashboard() { ObjectCreate(0, "dash-board", OBJ_RECTANGLE_LABEL, 0, 0, 0); ObjectSetInteger(0, "dash-board", OBJPROP_XDISTANCE, 20); ObjectSetInteger(0, "dash-board", OBJPROP_YDISTANCE, 20); ObjectSetInteger(0, "dash-board", OBJPROP_XSIZE, 250); ObjectSetInteger(0, "dash-board", OBJPROP_YSIZE, 150); ObjectSetInteger(0, "dash-board", OBJPROP_BGCOLOR, clrDarkSlateGray); ObjectCreate(0, "dash-tv", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "dash-tv", OBJPROP_XDISTANCE, 50); ObjectSetInteger(0, "dash-tv", OBJPROP_YDISTANCE, 40); ObjectSetString(0, "dash-tv", OBJPROP_TEXT, "Total Volume: " + DoubleToString(trading_volume_from_file(), 2)); ObjectSetInteger(0, "dash-tv", OBJPROP_COLOR, clrWhite); ObjectCreate(0, "dash-profit", OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, "dash-profit", OBJPROP_XDISTANCE, 50); ObjectSetInteger(0, "dash-profit", OBJPROP_YDISTANCE, 80); ObjectSetString(0, "dash-profit", OBJPROP_TEXT, "Total Profit: " + DoubleToString(pnl_from_file(), 2)); ObjectSetInteger(0, "dash-profit", OBJPROP_COLOR, clrWhite); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Get total trading volume | //+------------------------------------------------------------------+ double trading_volume_from_file(string filename = "firebase-temp-trade.txt") { double tv = 0; int filehandle = FileOpen(filename, FILE_READ | FILE_TXT | FILE_ANSI); if(filehandle == INVALID_HANDLE) { Print("Error opening file: ", GetLastError()); return 0; } string line; while(!FileIsEnding(filehandle)) { line = FileReadString(filehandle); // Look for lot values if(StringFind(line, "lot") != -1) { string raw0 = FileReadString(filehandle); int stat_int = StringFind(raw0, "d", 0); string raw = StringSubstr(raw0, stat_int + 14, -1); double profit = StringToDouble(raw); //--- tv += NormalizeDouble(profit, 2); } } FileClose(filehandle); return tv; } //+------------------------------------------------------------------+ //| Get total PnL | //+------------------------------------------------------------------+ double pnl_from_file(string filename = "firebase-temp-trade.txt") { double p_l = 0; int filehandle = FileOpen(filename, FILE_READ | FILE_TXT | FILE_ANSI); if(filehandle == INVALID_HANDLE) { Print("Error opening file: ", GetLastError()); return 0; } int line_no = 0; while(!FileIsEnding(filehandle)) { string line = FileReadString(filehandle); line_no++; // Look for profit values if(StringFind(line, "profit") != -1) { string raw0 = FileReadString(filehandle); int stat_int = StringFind(raw0, "d", 0); string raw = StringSubstr(raw0, stat_int + 14, -1); double profit = StringToDouble(raw); //--- p_l += profit; } } FileClose(filehandle); return p_l; } //+------------------------------------------------------------------+ void get_data() { // 1. Construct Firestore URL string url = "https://firestore.googleapis.com/v1/projects/my-users-mql5/databases/(default)/documents/users"; // 2. Prepare headers string headers; headers += "Content-Type: application/json\r\n"; // 3. Send request char data[], result[]; int timeout = 5000; int status = WebRequest("GET", url, headers, timeout, data, result, headers); // 4. Handle response if(status == 200) { // Print("Firestore Data Received:"); int filehandle = FileOpen("firebase-temp-trade.txt", FILE_WRITE | FILE_BIN); if(filehandle != INVALID_HANDLE) { //--- Saving the contents of the result[] array to a file FileWriteArray(filehandle, result, 0, ArraySize(result)); //--- Closing the file FileFlush(filehandle); FileClose(filehandle); } else Print("Error in FileOpen. Error code =", GetLastError()); } else { Print("Error Code: ", status); Print("Response: ", CharArrayToString(result)); } } //+------------------------------------------------------------------+
Результат выглядит так:

Заключение
Базы данных — основа современных приложений, и их освоение является обязательным навыком для разработчиков. Научившись взаимодействовать с Firebase — будь то мгновенная синхронизация Realtime Database или мощные запросы Firestore, — вы осваиваете важный инструментарий для создания динамических приложений, работающих с данными.
| Имя файла | Описание |
|---|---|
| firebase-users.mq5 | Файл с кодом для выполнения CRUD-операций в Firestore. |
| Download_Infor.mq5 | Файл с кодом для загрузки информации из Firestore и отображения ее на графике. |
| Sending_Trading_Info.mq5 | Файл с кодом для отправки торговой информации в Firestore. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/17854
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
MetaTrader и Google Таблицы через PythonAnywhere: Руководство по безопасному потоку данных
Торговые инструменты MQL5 (Часть 22): Построение гистограммы и функции вероятностной массы (PMF) биномиального распределения
Повышение эффективности торговли с использованием Smart Money Concepts (SMC): OB, BOS и FVG
От начального до среднего уровня: Объекты (IV)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования