331 lines
23 KiB
MQL5
331 lines
23 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;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|