forked from antekov/Adwizard
		
	
		
			
				
	
	
		
			331 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
			
		
		
	
	
			331 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
//+------------------------------------------------------------------+
 | 
						|
//|                                                      Storage.mqh |
 | 
						|
//|                                      Copyright 2024, Yuriy Bykov |
 | 
						|
//|                            https://www.mql5.com/ru/users/antekov |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
#property copyright "Copyright 2024, Yuriy Bykov"
 | 
						|
#property link      "https://www.mql5.com/ru/users/antekov"
 | 
						|
#property version   "1.01"
 | 
						|
 | 
						|
#include "Database.mqh"
 | 
						|
#include "../Base/Factorable.mqh"
 | 
						|
#include "../Virtual/VirtualOrder.mqh"
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Класс для работы с базой данных эксперта в виде                  |
 | 
						|
//| хранилища Key-Value для свойств и виртуальных позиций            |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
class CStorage {
 | 
						|
protected:
 | 
						|
   static bool       s_res; // Результат всех операций чтения/записи базы данных
 | 
						|
public:
 | 
						|
   // Подключение к базе данных эксперта
 | 
						|
   static bool       Connect(string p_fileName);
 | 
						|
 | 
						|
   // Закрытие подключения к базе данных
 | 
						|
   static void       Close();
 | 
						|
 | 
						|
   // Очистка данных
 | 
						|
   static void       Clear();
 | 
						|
 | 
						|
   // Сохранение виртуального ордера/позиции
 | 
						|
   static void       Set(int i, CVirtualOrder* order);
 | 
						|
 | 
						|
   // Сохранение одного значения произвольного простого типа
 | 
						|
   template<typename T>
 | 
						|
   static void       Set(string key, const T &value);
 | 
						|
 | 
						|
   // Сохранение массива значений произвольного простого типа
 | 
						|
   template<typename T>
 | 
						|
   static void       Set(string key, const T &values[]);
 | 
						|
 | 
						|
   // Получение значения в виде строки по заданному ключу
 | 
						|
   static string     Get(string key);
 | 
						|
 | 
						|
   // Получение массива виртуальных ордеров/позиций по заданному хешу стратегии
 | 
						|
   static bool       Get(string key, CVirtualOrder* &orders[]);
 | 
						|
 | 
						|
   // Получение значения по заданному ключу в переменную произвольного простого типа
 | 
						|
   template<typename T>
 | 
						|
   static bool       Get(string key, T &value);
 | 
						|
 | 
						|
   // Получение массива значений простого типа по заданному ключу в переменную
 | 
						|
   template<typename T>
 | 
						|
   static bool       CStorage::Get(string key, T &values[]);
 | 
						|
 | 
						|
   static bool       CStorage::GetSymbols(string &symbols[]);
 | 
						|
 | 
						|
   // Результат операций
 | 
						|
   static bool       Res() {
 | 
						|
      return s_res;
 | 
						|
   }
 | 
						|
};
 | 
						|
 | 
						|
// Результат всех операций чтения/записи базы данных
 | 
						|
bool CStorage::s_res = true;
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Подключение к базе данных эксперта                               |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
bool CStorage::Connect(string p_fileName) {
 | 
						|
// Подключаемся к базе данных эксперта
 | 
						|
   if(DB::Connect(p_fileName, DB_TYPE_ADV)) {
 | 
						|
      // Устанавливаем, что пока ошибок нет
 | 
						|
      s_res = true;
 | 
						|
 | 
						|
      // Начинаем транзакцию
 | 
						|
      DatabaseTransactionBegin(DB::Id());
 | 
						|
 | 
						|
      return true;
 | 
						|
   }
 | 
						|
   return false;
 | 
						|
}
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Закрытие подключения к базе данных                               |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
void CStorage::Close() {
 | 
						|
// Если ошибок нет, то
 | 
						|
   if(s_res) {
 | 
						|
      // Подтверждаем транзакцию
 | 
						|
      DatabaseTransactionCommit(DB::Id());
 | 
						|
   } else {
 | 
						|
      // Иначе транзакцию отменяем
 | 
						|
      DatabaseTransactionRollback(DB::Id());
 | 
						|
   }
 | 
						|
 | 
						|
// Закрываем соединение с базой данных
 | 
						|
   DB::Close();
 | 
						|
}
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Очистка данных                                                   |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
void CStorage::Clear() {
 | 
						|
   string query = "DELETE FROM storage;\n"
 | 
						|
                  "DELETE FROM storage_orders;";
 | 
						|
 | 
						|
   DB::Execute(query);
 | 
						|
}
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Сохранение виртуального ордера/позиции                           |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
void CStorage::Set(int i, CVirtualOrder* order) {
 | 
						|
   VirtualOrderStruct o;   // Структура для информации о виртуальной позиции
 | 
						|
   order.Save(o);          // Наполняем её
 | 
						|
 | 
						|
// Экранируем кавычки в комментарии
 | 
						|
   StringReplace(o.comment, "'", "\\'");
 | 
						|
 | 
						|
// Запрос на сохранение
 | 
						|
   string query = StringFormat("REPLACE INTO storage_orders VALUES("
 | 
						|
                               "'%s',%d,%I64u,"
 | 
						|
                               "'%s',%.2f,%d,%I64d,%f,%f,%f,%I64d,%f,%I64d,'%s',%f);",
 | 
						|
                               order.Strategy().Hash(), i, o.ticket,
 | 
						|
                               o.symbol, o.lot, o.type,
 | 
						|
                               o.openTime, o.openPrice,
 | 
						|
                               o.stopLoss, o.takeProfit,
 | 
						|
                               o.closeTime, o.closePrice,
 | 
						|
                               o.expiration, o.comment,
 | 
						|
                               o.point);
 | 
						|
 | 
						|
// Выполняем запрос
 | 
						|
   s_res &= DatabaseExecute(DB::Id(), query);
 | 
						|
 | 
						|
   if(!s_res) {
 | 
						|
      // Сообщаем об ошибке при необходимости
 | 
						|
      PrintFormat(__FUNCTION__" | ERROR: Execution failed in DB [adv], query:\n"
 | 
						|
                  "%s\n"
 | 
						|
                  "error code = %d",
 | 
						|
                  query, GetLastError());
 | 
						|
   }
 | 
						|
}
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Сохранение одного значения произвольного простого типа           |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
template<typename T>
 | 
						|
void CStorage::Set(string key, const T &value) {
 | 
						|
// Экранируем символы одинарных кавычек (пока не можно не использовать)
 | 
						|
// StringReplace(key, "'", "\\'");
 | 
						|
// StringReplace(value, "'", "\\'");
 | 
						|
 | 
						|
// Запрос на сохранение значения
 | 
						|
   string query = StringFormat("REPLACE INTO storage(key, value) VALUES('%s', '%s');",
 | 
						|
                               key, (string) value);
 | 
						|
 | 
						|
// Выполняем запрос
 | 
						|
   s_res &= DatabaseExecute(DB::Id(), query);
 | 
						|
 | 
						|
   if(!s_res) {
 | 
						|
      // Сообщаем об ошибке при необходимости
 | 
						|
      PrintFormat(__FUNCTION__" | ERROR: Execution failed in DB [adv], query:\n"
 | 
						|
                  "%s\n"
 | 
						|
                  "error code = %d",
 | 
						|
                  query, GetLastError());
 | 
						|
   }
 | 
						|
}
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Сохранение массива значений произвольного простого типа          |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
template<typename T>
 | 
						|
void CStorage::Set(string key, const T &values[]) {
 | 
						|
   string value = "";
 | 
						|
 | 
						|
// Соединяем все значения из массива в одну строку через запятую
 | 
						|
   JOIN(values, value, ",");
 | 
						|
 | 
						|
// Сохраняем строку с заданным ключом
 | 
						|
   Set(key, value);
 | 
						|
}
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Получение значения в виде строки по заданному ключу              |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
string CStorage::Get(string key) {
 | 
						|
   string value = NULL; // Возвращаемое значение
 | 
						|
 | 
						|
// Запрос на получение значения
 | 
						|
   string query = StringFormat("SELECT value FROM storage WHERE key='%s'", key);
 | 
						|
 | 
						|
// Выполняем запрос
 | 
						|
   int request = DatabasePrepare(DB::Id(), query);
 | 
						|
 | 
						|
// Если нет ошибки
 | 
						|
   if(request != INVALID_HANDLE) {
 | 
						|
      // Читаем данные из первой строки результата
 | 
						|
      DatabaseRead(request);
 | 
						|
 | 
						|
      if(!DatabaseColumnText(request, 0, value)) {
 | 
						|
         s_res = false;
 | 
						|
         // Сообщаем об ошибке при необходимости
 | 
						|
         PrintFormat(__FUNCTION__" | ERROR: Reading row in DB [adv] for request \n%s\n"
 | 
						|
                     "failed with code %d",
 | 
						|
                     query, GetLastError());
 | 
						|
      }
 | 
						|
   } else {
 | 
						|
      s_res = false;
 | 
						|
      // Сообщаем об ошибке при необходимости
 | 
						|
      PrintFormat(__FUNCTION__" | ERROR: Request in DB [adv] \n%s\nfailed with code %d",
 | 
						|
                  query, GetLastError());
 | 
						|
   }
 | 
						|
 | 
						|
   return value;
 | 
						|
}
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Получение значения по заданному ключу в переменную               |
 | 
						|
//| произвольного простого типа                                      |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
template<typename T>
 | 
						|
bool CStorage::Get(string key, T &value) {
 | 
						|
// Получаем значение в виде строки
 | 
						|
   string res = Get(key);
 | 
						|
 | 
						|
// Если значение получено
 | 
						|
   if(res != NULL) {
 | 
						|
      // Приводим его к типу Т и присваиваем целевой переменной
 | 
						|
      value = (T) res;
 | 
						|
      return true;
 | 
						|
   }
 | 
						|
   return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Получение массива значений произвольного простого типа           |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
template<typename T>
 | 
						|
bool CStorage::Get(string key, T &values[]) {
 | 
						|
   string params = Get(key);
 | 
						|
   int n = ArraySize(values);
 | 
						|
 | 
						|
   if(params != NULL) {
 | 
						|
      string parts[];
 | 
						|
      StringSplit(params, ',', parts);
 | 
						|
 | 
						|
      FOREACH(parts) {
 | 
						|
         if(i < n) {
 | 
						|
            values[i] = (T) parts[i];
 | 
						|
         } else {
 | 
						|
            APPEND(values, (T) parts[i]);
 | 
						|
         }
 | 
						|
      }
 | 
						|
      return true;
 | 
						|
   }
 | 
						|
   return false;
 | 
						|
}
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Получение массива виртуальных ордеров/позиций                    |
 | 
						|
//| по заданному хешу стратегии                                      |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
bool CStorage::Get(string key, CVirtualOrder* &orders[]) {
 | 
						|
// Запрос на получение данных о виртуальных позициях
 | 
						|
   string query = StringFormat("SELECT * FROM storage_orders "
 | 
						|
                               " WHERE strategy_hash = '%s' "
 | 
						|
                               " ORDER BY strategy_index ASC;",
 | 
						|
                               key);
 | 
						|
 | 
						|
// Выполняем запрос
 | 
						|
   int request = DatabasePrepare(DB::Id(), query);
 | 
						|
 | 
						|
// Если нет ошибки
 | 
						|
   if(request != INVALID_HANDLE) {
 | 
						|
      // Структура для информации о виртуальной позиции
 | 
						|
      VirtualOrderStruct row;
 | 
						|
 | 
						|
      // Читаем построчно данные из результата запроса
 | 
						|
      while(DatabaseReadBind(request, row)) {
 | 
						|
         orders[row.strategyIndex].Load(row);
 | 
						|
      }
 | 
						|
   } else {
 | 
						|
      // Запоминаем ошибку и сообщаем об ней при необходимости
 | 
						|
      s_res = false;
 | 
						|
      PrintFormat(__FUNCTION__" | ERROR: Execution failed in DB [adv], query:\n"
 | 
						|
                  "%s\n"
 | 
						|
                  "error code = %d",
 | 
						|
                  query, GetLastError());
 | 
						|
   }
 | 
						|
 | 
						|
   return s_res;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Получение массива символоав виртуальных ордеров/позиций          |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
bool CStorage::GetSymbols(string &symbols[]) {
 | 
						|
// Запрос на получение данных о виртуальных позициях
 | 
						|
   string query = StringFormat("SELECT symbol FROM storage_orders "
 | 
						|
                               " WHERE symbol <> ''"
 | 
						|
                               " GROUP BY symbol", "");
 | 
						|
 | 
						|
// Выполняем запрос
 | 
						|
   int request = DatabasePrepare(DB::Id(), query);
 | 
						|
 | 
						|
// Если нет ошибки
 | 
						|
   if(request != INVALID_HANDLE) {
 | 
						|
      // Структура для информации о виртуальной позиции
 | 
						|
      string symbol;
 | 
						|
 | 
						|
      // Читаем построчно данные из результата запроса
 | 
						|
      while(DatabaseRead(request)) {
 | 
						|
         s_res &= DatabaseColumnText(request, 0, symbol);
 | 
						|
         APPEND(symbols, symbol);
 | 
						|
      }
 | 
						|
   } else {
 | 
						|
      // Запоминаем ошибку и сообщаем об ней при необходимости
 | 
						|
      s_res = false;
 | 
						|
      PrintFormat(__FUNCTION__" | ERROR: Execution failed in DB [adv], query:\n"
 | 
						|
                  "%s\n"
 | 
						|
                  "error code = %d",
 | 
						|
                  query, GetLastError());
 | 
						|
   }
 | 
						|
 | 
						|
   return s_res;
 | 
						|
}
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 |