Обсуждение статьи "Применение OLAP в трейдинге (Часть 4): Количественный и визуальный анализ отчетов тестера"
При разборе кода из статьи было постоянное ощущение, что до такого высокого уровня мастерства даже близко не смогу подобраться. К сожалению, разобраться не смог из-за своей некомпетентности.
Прошу Автора показать (готовый код), в качестве примера, как в представленный инструментарий добавить этот функционал?
Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий
fxsaber, 2020.01.14 11:42
uchar Bytes2[]; if (MTTESTER::GetLastTstCache(Bytes2) != -1) // Если получилось прочитать последнюю кеш-запись одиночного прогона { const SINGLETESTERCACHE SingleTesterCache(Bytes2); // Загоняем ее в соответствующий объект.
Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий
fxsaber, 2019.11.11 04:45
uchar Bytes[]; MTTESTER::GetLastOptCache(Bytes); // TESTERCACHE<TestCacheSymbolRecord> Cache; // Оптимизация по символам TESTERCACHE<ExpTradeSummary> Cache; // Стандартная оптимизация if (Cache.Load(Bytes)) // Прочитали оптимизационный кеш.
Прошу уточнить вопрос. SingleTesterCache и TesterCache уже подключены в статье.
Хочу запустить EX5, и он автоматом подхватит последний opt/tst-файл (последнее выполненное соответствующее задание в Тестере) для анализа.
Хочу запустить EX5, и он автоматом подхватит последний opt/tst-файл (последнее выполненное соответствующее задание в Тестере) для анализа.
Вот вариант для отдельного прогона.
//+------------------------------------------------------------------+ //| TSTcube.mqh | //| Copyright (c) 2020, Marketeer | //| https://www.mql5.com/en/users/marketeer | //| Online Analytical Processing of trading hypercubes | //| https://www.mql5.com/ru/articles/6602 | //| https://www.mql5.com/ru/articles/6603 | //| https://www.mql5.com/ru/articles/7656 | //| rev. 5.03.2020 | //+------------------------------------------------------------------+ #include "ReportCubeBase.mqh" #include <fxsaber/SingleTesterCache/SingleTesterCache.mqh> #include <fxsaber/MultiTester/MTTester.mqh> // + class TesterDeal: public Deal { public: TesterDeal(const TradeDeal &td) { time = (datetime)td.time_create + TimeShift; price = td.price_open; string t = dealType(td.action); type = t == "buy" ? +1 : (t == "sell" ? -1 : 0); t = dealDir(td.entry); direction = 0; if(StringFind(t, "in") > -1) ++direction; if(StringFind(t, "out") > -1) --direction; volume = (double)td.volume; profit = td.profit; deal = (long)td.deal; order = (long)td.order; comment = td.comment[]; symbol = td.symbol[]; commission = td.commission; swap = td.storage; // balance - SingleTesterCache.Deals[i].reserve } static string dealType(const ENUM_DEAL_TYPE type) { return type == DEAL_TYPE_BUY ? "buy" : (type == DEAL_TYPE_SELL ? "sell" : "balance"); } static string dealDir(const ENUM_DEAL_ENTRY entry) { string result = ""; if(entry == DEAL_ENTRY_IN) result += "in"; else if(entry == DEAL_ENTRY_OUT || entry == DEAL_ENTRY_OUT_BY) result += "out"; else if(entry == DEAL_ENTRY_INOUT) result += "in out"; return result; } }; template<typename T> class TesterReportAdapter: public BaseReportAdapter<T> { protected: SINGLETESTERCACHE *ptrSingleTesterCache; virtual bool fillDealsArray() override { for(int i = 0; i < ArraySize(ptrSingleTesterCache.Deals); i++) { if(TesterDeal::dealType(ptrSingleTesterCache.Deals[i].action) == "balance") { balance += ptrSingleTesterCache.Deals[i].profit; } else { array << new TesterDeal(ptrSingleTesterCache.Deals[i]); } } return true; } public: ~TesterReportAdapter() { if(CheckPointer(ptrSingleTesterCache) == POINTER_DYNAMIC) delete ptrSingleTesterCache; } virtual bool load(const string file) override { if(StringFind(file, ".tst") > 0) { BaseReportAdapter<T>::load(file); if(CheckPointer(ptrSingleTesterCache) == POINTER_DYNAMIC) delete ptrSingleTesterCache; bool loaded = true; // + ptrSingleTesterCache = new SINGLETESTERCACHE(); if(file == "*.tst") // + { // + uchar Bytes2[]; // + // Why MTTESTER::GetLastTstCacheFileName() is private? // + // Print("Loading ", MTTESTER::GetLastTstCacheFileName()); // + if(MTTESTER::GetLastTstCache(Bytes2) != -1) // + { // + loaded = ptrSingleTesterCache.Load(Bytes2); // + } // + } else { loaded = ptrSingleTesterCache.Load(file); // * } if(!loaded) // * { delete ptrSingleTesterCache; ptrSingleTesterCache = NULL; return false; } size = generate(); Print("Tester cache import: ", size, " trades from ", ArraySize(ptrSingleTesterCache.Deals), " deals"); } return true; } }; TesterReportAdapter<RECORD_CLASS> _defaultTSTReportAdapter;
Хотел вывести имя последнего tst-файла в лог, но соотв. метод приватный.
В настройках нужно выбрать файл "*.tst" (пустое имя по-прежнему запускает анализ онлайн истории счета).
#include <fxsaber/MultiTester/MTTester.mqh> // + ... template<typename T> class OptCacheDataAdapter: public DataAdapter { private: TESTERCACHE<ExpTradeSummary> Cache; ... public: OptCacheDataAdapter() { reset(); } void load(const string optName) { bool loaded = true; // + if(optName == "") // + { // + uchar Bytes[]; // + // Why GetLastOptCacheFileName() is private? // + // Print("Loading ", MTTESTER::GetLastOptCacheFileName()); // + MTTESTER::GetLastOptCache(Bytes); // + loaded = Cache.Load(Bytes); // + } else { loaded = Cache.Load(optName); // * } if(loaded) // * { customize(); reset(); } else { cursor = -1; } } ...
Статья понравилась, впрочем, как и все статьи о технологии OLAP.
Лично мне не хватило полета мысли, - философских формулировок сути, дающих представление о всем подходе, - об охватываемом пространстве задач и потенциале. А потенциал огромный. Печалят множественные и малосущественные детали, на которые тратиться время читателя. Мол - там такая переменная, а тут такая... Понимаю, что это необходимо новичкам, копирующим решения, но все же... Никакая технология не является статичной и если сейчас применят Ваши классы и методы, то в будущем, кто то захочет все поменять под себя, и для него полезность статьи уменьшится пропорционально количеству частных сущностей. Больше пишите о подходе в целом, - о его настоящем и будущем. С такими походами нужно быть глобальнее.
Но, это мое субъективное мнение. Спасибо за статью.
Хотел вывести имя последнего tst-файла в лог, но соотв. метод приватный.
Не пришло в голову, что кому-нибудь это может понадобиться. Если есть еще замечания по private->public, скажите. Сделаю.
Публикую актуализированный исходник файла OLAPCommon.mqh, который перестал компилироваться из-за изменений в компиляторе. Также нужен TypeName.mqh - поведение typename тоже поменялось.
PS. Библиотеку от fxsaber TesterCache.mqh также нужно обновить.

- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Опубликована статья Применение OLAP в трейдинге (Часть 4): Количественный и визуальный анализ отчетов тестера:
Статья предлагает базовый инструментарий для OLAP-анализа отчетов тестера об одиночных проходах и результатах оптимизации в виде файлов стандартных форматов (tst и opt), а также интерактивный графический интерфейс к нему. Исходные коды MQL прилагаются.
Чтобы посмотреть общее распределение прибылей по уровням с шагом 100, выберем поле profit из статистики по оси X и агрегатор count.
Распределение прибылей всех проходов по диапазонам с шагом 100 уе
Наконец, оценить влияние количества трейдов на прибыль дает возможность агрегатор identity. В принципе, этот агрегатор позволяет наглядно увидеть многие другие закономерности.
Автор: Stanislav Korotky