600 lines
48 KiB
MQL5
600 lines
48 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| VirtualAdvisor.mqh |
|
|
//| Copyright 2019-2025, Yuriy Bykov |
|
|
//| https://www.mql5.com/ru/users/antekov |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2019-2025, Yuriy Bykov"
|
|
#property link "https://www.mql5.com/ru/users/antekov"
|
|
#property version "1.11"
|
|
|
|
class CVirtualStrategyGroup;
|
|
|
|
#include "../Base/Advisor.mqh"
|
|
#include "../Utils/NewBarEvent.mqh"
|
|
#include "../Utils/SymbolsMonitor.mqh"
|
|
#include "VirtualRiskManager.mqh"
|
|
#include "VirtualInterface.mqh"
|
|
#include "VirtualReceiver.mqh"
|
|
#include "VirtualStrategyGroup.mqh"
|
|
#include "TesterHandler.mqh"
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Класс эксперта, работающего с виртуальными позициями (ордерами) |
|
|
//+------------------------------------------------------------------+
|
|
class CVirtualAdvisor : public CAdvisor {
|
|
protected:
|
|
CSymbolsMonitor *m_symbols; // Объект монитора символов
|
|
CVirtualReceiver *m_receiver; // Объект получателя, выводящий позиции на рынок
|
|
CVirtualInterface *m_interface; // Объект интерфейса для показа состояния пользователю
|
|
CVirtualRiskManager *m_riskManager; // Объект риск-менеджера
|
|
|
|
string m_fileName; // Название файла с базой данных эксперта
|
|
datetime m_lastSaveTime; // Время последнего сохранения
|
|
bool m_useOnlyNewBar; // Обрабатывать только тики нового бара
|
|
|
|
datetime m_fromDate; // Дата начала работы
|
|
string m_paramsNorm; // Параметры группы стратегий после нормировки
|
|
|
|
virtual void Add(CVirtualStrategyGroup *p_group); // Метод добавления группы стратегий
|
|
|
|
static int s_groupId; // ID загруженной из базы данных группы стратегий
|
|
CVirtualAdvisor(string p_param); // Конструктор
|
|
public:
|
|
STATIC_CONSTRUCTOR(CVirtualAdvisor);
|
|
~CVirtualAdvisor(); // Деструктор
|
|
|
|
virtual string operator~() override; // Преобразование объекта в строку
|
|
|
|
virtual void Tick() override; // Обработчик события OnTick
|
|
virtual double Tester() override; // Обработчик события OnTester
|
|
|
|
// Обработчик события OnChartEvent (пока не используется)
|
|
virtual void ChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam);
|
|
|
|
|
|
virtual bool Save(); // Сохранение состояния
|
|
virtual bool Load(); // Загрузка состояния
|
|
|
|
// Замена названий символов
|
|
bool SymbolsReplace(const string p_symbolsReplace);
|
|
|
|
// Проверка наличия новой группы стратегий в базе данных эксперта
|
|
bool CheckUpdate();
|
|
|
|
// Экспорт текущей группы стратегий в заданную базу данных эксперта
|
|
void Export(string p_groupName, string p_advFileName);
|
|
|
|
// Обработчик события OnTesterInit
|
|
static int TesterInit(ulong p_idTask = 0, string p_fileName = NULL);
|
|
static void TesterPass(); // Обработчик события OnTesterDeinit
|
|
static void TesterDeinit(); // Обработчик события OnTesterDeinit
|
|
|
|
// Имя файла с базой данных эксперта
|
|
static string FileName(string p_name, ulong p_magic = 1);
|
|
|
|
// Получение строки инициализации группы стратегий
|
|
// из базы данных эксперта с заданным идентификатором
|
|
static string Import(string p_fileName, int p_groupId = 0);
|
|
};
|
|
|
|
int CVirtualAdvisor::s_groupId = 0;
|
|
|
|
REGISTER_FACTORABLE_CLASS(CVirtualAdvisor);
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Метод добавления группы стратегий |
|
|
//+------------------------------------------------------------------+
|
|
void CVirtualAdvisor::Add(CVirtualStrategyGroup *p_group) {
|
|
// Если в этой группе содержатся другие группы, то добавляем каждую из них
|
|
FOREACH(p_group.m_groups) {
|
|
CVirtualAdvisor::Add(p_group.m_groups[i]);
|
|
delete p_group.m_groups[i];
|
|
}
|
|
// Если в этой группе содержатся стратегии, то добавляем каждую из них
|
|
FOREACH(p_group.m_strategies) CAdvisor::Add(p_group.m_strategies[i]);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Конструктор |
|
|
//+------------------------------------------------------------------+
|
|
CVirtualAdvisor::CVirtualAdvisor(string p_params) {
|
|
// Запоминаем строку инициализации
|
|
m_params = p_params;
|
|
|
|
// Читаем строку инициализации объекта группы стратегий
|
|
string groupParams = ReadObject(p_params);
|
|
|
|
// Читаем строку инициализации объекта риск-менеджера
|
|
string riskManagerParams = ReadObject(p_params);
|
|
|
|
// Читаем магический номер
|
|
ulong p_magic = ReadLong(p_params);
|
|
|
|
// Читаем название эксперта
|
|
string p_name = ReadString(p_params);
|
|
|
|
// Читаем признак работы на только на открытии бара
|
|
m_useOnlyNewBar = (bool) ReadLong(p_params);
|
|
|
|
// Если нет ошибок чтения, то
|
|
if(IsValid()) {
|
|
// Создаём группу стратегий
|
|
CREATE(CVirtualStrategyGroup, p_group, groupParams);
|
|
|
|
// Инициализируем монитор символов статическим монитором символов
|
|
m_symbols = CSymbolsMonitor::Instance();
|
|
|
|
// Инициализируем получателя статическим получателем
|
|
m_receiver = CVirtualReceiver::Instance(p_magic);
|
|
|
|
// Инициализируем интерфейс статическим интерфейсом
|
|
m_interface = CVirtualInterface::Instance(p_magic);
|
|
|
|
// Формируем из имени эксперта и параметров имя файла базы данных эксперта для сохранения состояния
|
|
m_fileName = FileName(p_name, p_magic);
|
|
|
|
// Запоминаем время начала работы (тестирования)
|
|
m_fromDate = TimeCurrent();
|
|
|
|
// Сбрасываем время последнего сохранения
|
|
m_lastSaveTime = 0;
|
|
|
|
// Добавляем к эксперту содержимое группы
|
|
Add(p_group);
|
|
|
|
// Удаляем объект группы
|
|
delete p_group;
|
|
|
|
// Создаём объект риск-менеджера
|
|
m_riskManager = NEW(riskManagerParams);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Деструктор |
|
|
//+------------------------------------------------------------------+
|
|
void CVirtualAdvisor::~CVirtualAdvisor() {
|
|
if(!!m_symbols) delete m_symbols; // Удаляем монитор символов
|
|
if(!!m_receiver) delete m_receiver; // Удаляем получатель
|
|
if(!!m_interface) delete m_interface; // Удаляем интерфейс
|
|
if(!!m_riskManager) delete m_riskManager; // Удаляем риск-менеджер
|
|
DestroyNewBar(); // Удаляем объекты отслеживания нового бара
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Преобразование объекта в строку |
|
|
//+------------------------------------------------------------------+
|
|
string CVirtualAdvisor::operator~() {
|
|
return StringFormat("%s(%s)", typename(this), m_params);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Обработчик события OnTick |
|
|
//+------------------------------------------------------------------+
|
|
void CVirtualAdvisor::Tick(void) {
|
|
// Определяем новый бар по всем нужным символам и таймфреймам
|
|
bool isNewBar = UpdateNewBar();
|
|
|
|
// Если нигде нового бара нет, а мы работаем только по новым барам, то выходим
|
|
if(!isNewBar && m_useOnlyNewBar) {
|
|
return;
|
|
}
|
|
|
|
// Монитор символов обновляет котировки
|
|
m_symbols.Tick();
|
|
|
|
// Получатель обрабатывает виртуальные позиции
|
|
m_receiver.Tick();
|
|
|
|
// Запуск обработки в стратегиях
|
|
CAdvisor::Tick();
|
|
|
|
// Риск-менеджер обрабатывает виртуальные позиции
|
|
m_riskManager.Tick();
|
|
|
|
// Корректировка рыночных объемов
|
|
m_receiver.Correct();
|
|
|
|
// Сохранение состояния
|
|
Save();
|
|
|
|
// Отрисовка интерфейса
|
|
m_interface.Redraw();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Обработчик события OnTester |
|
|
//+------------------------------------------------------------------+
|
|
double CVirtualAdvisor::Tester() {
|
|
// Максимальная абсолютная просадка
|
|
double balanceDrawdown = TesterStatistics(STAT_EQUITY_DD);
|
|
|
|
// Прибыль
|
|
double profit = TesterStatistics(STAT_PROFIT);
|
|
|
|
// Фиксированный баланс для торговли из настроек
|
|
double fixedBalance = CMoney::FixedBalance();
|
|
|
|
// Коэффициент возможного увеличения размеров позиций для просадки 10% от fixedBalance_
|
|
double coeff = fixedBalance * 0.1 / MathMax(1, balanceDrawdown);
|
|
|
|
// Пресчитываем прибыль в годовую
|
|
long totalSeconds = TimeCurrent() - m_fromDate;
|
|
double totalYears = totalSeconds / (365.0 * 24 * 3600);
|
|
double fittedProfit = profit * coeff / totalYears;
|
|
|
|
// Если он не указан, то берём начальный баланс (хотя это будет давать искажённый результат)
|
|
if(fixedBalance < 1) {
|
|
fixedBalance = TesterStatistics(STAT_INITIAL_DEPOSIT);
|
|
balanceDrawdown = TesterStatistics(STAT_EQUITY_DDREL_PERCENT);
|
|
coeff = 0.1 / MathMax(1, balanceDrawdown);
|
|
fittedProfit = fixedBalance * MathPow(1 + profit * coeff / fixedBalance, 1 / totalYears);
|
|
}
|
|
|
|
// Воссоздаём группу использованных стратегий для последующей нормировки
|
|
CVirtualStrategyGroup* group = NEW(ReadObject(m_params));
|
|
|
|
if(!!group) {
|
|
// Строка инициализации нормированной группы
|
|
m_paramsNorm = group.ToStringNorm(coeff);
|
|
|
|
FOREACH(m_strategies) ((CVirtualStrategy*)m_strategies[i]).Scale(coeff);
|
|
|
|
// Выполняем формирование фрейма данных на агенте тестирования
|
|
CTesterHandler::Tester(fittedProfit, // Нормированная прибыль
|
|
m_paramsNorm // Строка инициализации нормированной группы
|
|
);
|
|
|
|
PrintFormat(__FUNCTION__" | Scale = %.2f\nParams:\n%s", coeff, m_paramsNorm);
|
|
PrintFormat(__FUNCTION__" | Scale = %.2f", coeff);
|
|
|
|
delete group;
|
|
}
|
|
|
|
PrintFormat(__FUNCTION__" |\n%s = %.2f\n%s = %.2f\n%s = %.2f\n%s = %.2f\n%s = %.2f\n",
|
|
EnumToString(STAT_BALANCE_DD), TesterStatistics(STAT_BALANCE_DD),
|
|
EnumToString(STAT_BALANCE_DD_RELATIVE), TesterStatistics(STAT_BALANCE_DD_RELATIVE),
|
|
EnumToString(STAT_EQUITY_DD), TesterStatistics(STAT_EQUITY_DD),
|
|
EnumToString(STAT_EQUITY_DD_RELATIVE), TesterStatistics(STAT_EQUITY_DD_RELATIVE),
|
|
EnumToString(STAT_EQUITY_DDREL_PERCENT), TesterStatistics(STAT_EQUITY_DDREL_PERCENT)
|
|
);
|
|
|
|
return fittedProfit;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Экспорт текущей группы стратегий в заданную базу данных эксперта |
|
|
//+------------------------------------------------------------------+
|
|
void CVirtualAdvisor::Export(string p_groupName, string p_advFileName) {
|
|
CTesterHandler::Export(m_strategies, p_groupName, p_advFileName);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Обработчик событий ChartEvent |
|
|
//+------------------------------------------------------------------+
|
|
void CVirtualAdvisor::ChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) {
|
|
m_interface.ChartEvent(id, lparam, dparam, sparam);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Инициализация перед началом оптимизации |
|
|
//+------------------------------------------------------------------+
|
|
int CVirtualAdvisor::TesterInit(ulong p_idTask, string p_fileName) {
|
|
return CTesterHandler::TesterInit(p_idTask, p_fileName);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Действия после завершения очередного прохода при оптимизации |
|
|
//+------------------------------------------------------------------+
|
|
void CVirtualAdvisor::TesterPass() {
|
|
CTesterHandler::TesterPass();
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Действия после завершения оптимизации |
|
|
//+------------------------------------------------------------------+
|
|
void CVirtualAdvisor::TesterDeinit() {
|
|
CTesterHandler::TesterDeinit();
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Сохранение состояния |
|
|
//+------------------------------------------------------------------+
|
|
bool CVirtualAdvisor::Save() {
|
|
// Сохраняем состояние, если:
|
|
if(true
|
|
// появились более поздние изменения
|
|
&& m_lastSaveTime < CVirtualReceiver::s_lastChangeTime
|
|
// и сейчас не оптимизация
|
|
&& !MQLInfoInteger(MQL_OPTIMIZATION)
|
|
// и сейчас не тестирование либо сейчас визуальное тестирование
|
|
&& (!MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_VISUAL_MODE))
|
|
) {
|
|
// Если подключение к базе данных эксперта установлено
|
|
if(CStorage::Connect(m_fileName)) {
|
|
// Сохраняем время последних изменений
|
|
CStorage::Set("CVirtualReceiver::s_lastChangeTime", CVirtualReceiver::s_lastChangeTime);
|
|
CStorage::Set("CVirtualAdvisor::s_groupId", CVirtualAdvisor::s_groupId);
|
|
|
|
// Сохраняем все стратегии
|
|
FOREACH(m_strategies) ((CVirtualStrategy*) m_strategies[i]).Save();
|
|
|
|
// Сохраняем риск-менеджер
|
|
m_riskManager.Save();
|
|
|
|
// Обновляем время последнего сохранения
|
|
m_lastSaveTime = CVirtualReceiver::s_lastChangeTime;
|
|
PrintFormat(__FUNCTION__" | OK at %s to %s",
|
|
TimeToString(m_lastSaveTime, TIME_DATE | TIME_MINUTES | TIME_SECONDS),
|
|
m_fileName);
|
|
|
|
// Закрываем соединение
|
|
CStorage::Close();
|
|
|
|
// Возвращаем результат
|
|
return CStorage::Res();
|
|
} else {
|
|
PrintFormat(__FUNCTION__" | ERROR: Can't open database [%s], LastError=%d",
|
|
m_fileName, GetLastError());
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Загрузка состояния |
|
|
//+------------------------------------------------------------------+
|
|
bool CVirtualAdvisor::Load() {
|
|
bool res = true;
|
|
ulong groupId = 0;
|
|
|
|
// Загружаем состояние, если:
|
|
if(true
|
|
// файл существует
|
|
&& FileIsExist(m_fileName, FILE_COMMON)
|
|
// и сейчас не оптимизация
|
|
&& !MQLInfoInteger(MQL_OPTIMIZATION)
|
|
// и сейчас не тестирование либо сейчас визуальное тестирование
|
|
&& (!MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_VISUAL_MODE))
|
|
) {
|
|
// Если подключение к базе данных эксперта установлено
|
|
if(CStorage::Connect(m_fileName)) {
|
|
// Если время последних изменений загружено и меньше текущего времени
|
|
if(CStorage::Get("CVirtualReceiver::s_lastChangeTime", m_lastSaveTime)
|
|
&& m_lastSaveTime <= TimeCurrent()) {
|
|
|
|
PrintFormat(__FUNCTION__" | LAST SAVE at %s",
|
|
TimeToString(m_lastSaveTime, TIME_DATE | TIME_MINUTES | TIME_SECONDS));
|
|
|
|
// Если идентификатор сохранённой группы стратегий загружен
|
|
if(CStorage::Get("CVirtualAdvisor::s_groupId", groupId)) {
|
|
// Загружаем все стратегии, игнорируя возможные ошибки
|
|
FOREACH(m_strategies) {
|
|
res &= ((CVirtualStrategy*) m_strategies[i]).Load();
|
|
}
|
|
|
|
if(groupId != s_groupId) {
|
|
// Действия при запуске эксперта с новой группой стратегий.
|
|
PrintFormat(__FUNCTION__" | UPDATE Group ID: %I64u -> %I64u", groupId, s_groupId);
|
|
|
|
// Сбрасываем возможный признак ошибки при загрузке стратегий
|
|
res = true;
|
|
|
|
string symbols[]; // Массив для названий символоа
|
|
|
|
// Получаем список всех используемых предыдущей группой символов
|
|
CStorage::GetSymbols(symbols);
|
|
|
|
// Для всех символов создаём символьный получатель.
|
|
// Это нужно для корректного закрытия виртуальных позиций
|
|
// старой группы стратегий сразу после загрузки новой
|
|
FOREACH(symbols) m_receiver[symbols[i]];
|
|
}
|
|
|
|
if(res) {
|
|
// Загружаем риск-менеджер
|
|
res &= m_riskManager.Load();
|
|
|
|
if(!res) {
|
|
PrintFormat(__FUNCTION__" | ERROR loading risk manager from DB [%s]", m_fileName);
|
|
}
|
|
} else {
|
|
PrintFormat(__FUNCTION__" | ERROR loading strategies from DB [%s]", m_fileName);
|
|
}
|
|
}
|
|
} else {
|
|
// Если время последних изменений не найдено или находится в будущем,
|
|
// то начинаем работу с чистого листа
|
|
PrintFormat(__FUNCTION__" | NO LAST SAVE [%s] - Clear Storage",
|
|
TimeToString(m_lastSaveTime, TIME_DATE | TIME_MINUTES | TIME_SECONDS));
|
|
CStorage::Clear();
|
|
m_lastSaveTime = 0;
|
|
}
|
|
|
|
// Закрываем соединение
|
|
CStorage::Close();
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Замена названий символов |
|
|
//+------------------------------------------------------------------+
|
|
bool CVirtualAdvisor::SymbolsReplace(string p_symbolsReplace) {
|
|
// Избавляемся от пробелов в строке замен
|
|
StringReplace(p_symbolsReplace, " ", "");
|
|
|
|
// Если строка замен пустая, то ничего не делаем
|
|
if(p_symbolsReplace == "") {
|
|
return true;
|
|
}
|
|
|
|
// Переменная для результата
|
|
bool res = true;
|
|
|
|
string symbolKeyValuePairs[]; // Массив для отдельных замен
|
|
string symbolPair[]; // Массив для двух имён в одной замене
|
|
|
|
// Делим строку замен на части, представляющие одну отдельную замену
|
|
StringSplit(p_symbolsReplace, ';', symbolKeyValuePairs);
|
|
|
|
// Словарь для соответствия целевого символа исходному символу
|
|
CHashMap<string, string> symbolsMap;
|
|
|
|
// Для всех отдельных замен
|
|
FOREACH(symbolKeyValuePairs) {
|
|
// Получаем исходный и целевой символы как два элемента массива
|
|
StringSplit(symbolKeyValuePairs[i], '=', symbolPair);
|
|
|
|
// Проверяем наличие целевого символа в списке доступных символов (не кастомных)
|
|
bool custom = false;
|
|
res &= SymbolExist(symbolPair[1], custom);
|
|
|
|
// Если целевой символ не найден, то сообщаем об ошибке и выходим
|
|
if(!res) {
|
|
PrintFormat(__FUNCTION__" | ERROR: Target symbol %s for mapping %s not found", symbolPair[1], symbolKeyValuePairs[i]);
|
|
return res;
|
|
}
|
|
|
|
// Добавляем в словарь новый элемент: ключ - исходный символ, значение - целевой символ
|
|
res &= symbolsMap.Add(symbolPair[0], symbolPair[1]);
|
|
|
|
// Если целевой символ не удалось добавить в словарь, то сообщаем об ошибке и выходим
|
|
if(!res) {
|
|
PrintFormat(__FUNCTION__" | ERROR: Can't add symbol map pair %s to HashMap. Check your parameter:\n%s",
|
|
symbolKeyValuePairs[i], p_symbolsReplace);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
// Если ошибок не возникло, то для всех стратегий вызываем соответствующий метод замены
|
|
FOREACH(m_strategies) res &= ((CVirtualStrategy*) m_strategies[i]).SymbolsReplace(symbolsMap);
|
|
|
|
return res;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Проверка наличия новой группы стратегий в базе данных эксперта |
|
|
//+------------------------------------------------------------------+
|
|
bool CVirtualAdvisor::CheckUpdate() {
|
|
// Запрос на получение стратегий заданной группы либо последней группы
|
|
string query = StringFormat("SELECT MAX(id_group) FROM strategy_groups"
|
|
" WHERE to_date <= '%s'",
|
|
TimeToString(TimeCurrent(), TIME_DATE));
|
|
|
|
// Открываем базу данных эксперта
|
|
if(DB::Connect(m_fileName, DB_TYPE_ADV)) {
|
|
// Выполняем запрос
|
|
int request = DatabasePrepare(DB::Id(), query);
|
|
|
|
// Если нет ошибки
|
|
if(request != INVALID_HANDLE) {
|
|
// Структура данных для чтения одной строки результата запроса
|
|
struct Row {
|
|
int groupId;
|
|
} row;
|
|
|
|
// Читаем данные из первой строки результата
|
|
while(DatabaseReadBind(request, row)) {
|
|
// Запоминаем идентификатор группы стратегий
|
|
// в статическом свойстве класса эксперта
|
|
return s_groupId < row.groupId;
|
|
}
|
|
} else {
|
|
// Сообщаем об ошибке при необходимости
|
|
PrintFormat(__FUNCTION__" | ERROR: request \n%s\nfailed with code %d", query, GetLastError());
|
|
}
|
|
|
|
// Закрываем базу данных эксперта
|
|
DB::Close();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Получение строки инициализации группы стратегий |
|
|
//| из базы данных эксперта с заданным идентификатором |
|
|
//+------------------------------------------------------------------+
|
|
string CVirtualAdvisor::Import(string p_fileName, int p_groupId = 0) {
|
|
string params[]; // Массив для строк инициализации стратегий
|
|
|
|
// Запрос на получение стратегий заданной группы либо последней группы
|
|
string query = StringFormat("SELECT id_group, params "
|
|
" FROM strategies"
|
|
" WHERE id_group = %s;",
|
|
(p_groupId > 0 ? (string) p_groupId
|
|
: "(SELECT MAX(id_group) FROM strategy_groups WHERE to_date <= '"
|
|
+ TimeToString(TimeCurrent(), TIME_DATE) +
|
|
"')"));
|
|
|
|
|
|
// Открываем базу данных эксперта
|
|
if(DB::Connect(p_fileName, DB_TYPE_ADV)) {
|
|
// Выполняем запрос
|
|
int request = DatabasePrepare(DB::Id(), query);
|
|
|
|
// Если нет ошибки
|
|
if(request != INVALID_HANDLE) {
|
|
// Структура данных для чтения одной строки результата запроса
|
|
struct Row {
|
|
int groupId;
|
|
string params;
|
|
} row;
|
|
|
|
// Читаем данные из первой строки результата
|
|
while(DatabaseReadBind(request, row)) {
|
|
// Запоминаем идентификатор группы стратегий
|
|
// в статическом свойстве класса эксперта
|
|
s_groupId = row.groupId;
|
|
|
|
// Добавляем очередную строку инициализации стратегии в массив
|
|
APPEND(params, row.params);
|
|
}
|
|
} else {
|
|
// Сообщаем об ошибке при необходимости
|
|
PrintFormat(__FUNCTION__" | ERROR: request \n%s\nfailed with code %d", query, GetLastError());
|
|
}
|
|
|
|
// Закрываем базу данных эксперта
|
|
DB::Close();
|
|
}
|
|
|
|
// Строка инициализации группы стратегий
|
|
string groupParams = NULL;
|
|
|
|
// Общее количество стратегий в группе
|
|
int totalStrategies = ArraySize(params);
|
|
|
|
// Если стратегии есть, то
|
|
if(totalStrategies > 0) {
|
|
// Соединяем их строки инициализации через запятую
|
|
JOIN(params, groupParams, ",");
|
|
|
|
// Создаём строку инициализации группы стратегий
|
|
groupParams = StringFormat("class CVirtualStrategyGroup([%s], %.5f)",
|
|
groupParams,
|
|
totalStrategies);
|
|
}
|
|
|
|
// Возвращаем строку инициализации группы стратегий
|
|
return groupParams;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Имя файла с базой данных эксперта |
|
|
//+------------------------------------------------------------------+
|
|
string CVirtualAdvisor::FileName(string p_name, ulong p_magic = 1) {
|
|
return StringFormat("%s-%d%s.db.sqlite",
|
|
(p_name != "" ? p_name : "Expert"),
|
|
p_magic,
|
|
(MQLInfoInteger(MQL_TESTER) ? ".test" : "")
|
|
);
|
|
}
|
|
//+------------------------------------------------------------------+
|