Discussion of article "Applying OLAP in trading (part 4): Quantitative and visual analysis of tester reports"
When parsing the code from the article, I had a constant feeling that I would not be able to get even close to such a high level of skill. Unfortunately, I could not understand it because of my incompetence.
I ask the author to show (ready code), as an example, how to add this functionality to the presented toolkit?
Forum on trading, automated trading systems and testing trading strategies.
fxsaber, 2020.01.14 11:42 AM
uchar Bytes2[]; if (MTTESTER::GetLastTstCache(Bytes2) != -1) // If it was possible to read the last cache record of a single run { const SINGLETESTERCACHE SingleTesterCache(Bytes2); // Drive it into the corresponding object.
Forum on trading, automated trading systems and testing trading strategies
fxsaber, 2019.11.11 04:45 pm.
uchar Bytes[]; MTTESTER::GetLastOptCache(Bytes); // TESTERCACHE<TestCacheSymbolRecord> Cache; // Symbol Optimisation TESTERCACHE<ExpTradeSummary> Cache; // Standard optimisation if (Cache.Load(Bytes)) // Read the optimisation cache.
Please clarify the question. SingleTesterCache and TesterCache are already connected in the article.
I want to run EX5 and it will automatically pick up the last opt/tst-file (the last executed corresponding task in Tester) for analysis.
I want to run EX5 and it will automatically pick up the last opt/tst file (the last executed corresponding job in Tester) for analysis.
Here is a variant for a separate run.
//+------------------------------------------------------------------+ //|TSTcube.mqh | //|Copyright (c) 2020, Marketeer | //| https://www.mql5.com/en/users/marketeer | //| Online Analytical Processing of trading hypercubes | //| https://www.mql5.com/en/articles/6602 | //| https://www.mql5.com/en/articles/6603 | //| https://www.mql5.com/en/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;
I wanted to output the name of the last tst-file to the log, but the corresponding method is private.
In the settings you should select the file "*.tst" (an empty name still starts the analysis of the online account history).
For the results of the optimisation:
#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; } } ...
I liked the article, as well as all articles about OLAP technology.
Personally, I lacked the flight of thought, - philosophical formulations of the essence, giving an idea of the whole approach, - of the covered space of tasks and potential. And the potential is huge. I am saddened by the multiple and insignificant details that waste the reader's time. They say - there is such a variable there, and there is such a variable here..... I realise that this is necessary for beginners copying solutions, but still.... No technology is static, and if your classes and methods are used now, then in the future, someone will want to change everything for himself, and for him the usefulness of the article will decrease in proportion to the number of private entities. Write more about the approach as a whole - about its present and future. You need to be more global with such approaches.
But, this is my subjective opinion. Thank you for the article.
I wanted to output the name of the last tst file to the log, but the corresponding method is private.
It didn't occur to me that someone might need it. If you have more comments on private->public, tell me. Will do.
Hello Sir,
I tried to compile the file 'OLAPGUI_Opts.mq5' but there are 17 errors in some include files.
Kind Regards,
Ben

- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
New article Applying OLAP in trading (part 4): Quantitative and visual analysis of tester reports has been published:
The article offers basic tools for the OLAP analysis of tester reports relating to single passes and optimization results. The tool can work with standard format files (tst and opt), and it also provides a graphical interface. MQL source codes are attached below.
To view the general distribution of profits by levels in increments of 100, select the 'profit' field from the statistics along the X axis and the 'count' aggregator.
Distribution of all profits by ranges in increments of 100 units
By using the 'identity' aggregator, we can evaluate the influence of the number of trades to profit. Generally, this aggregator enables the visual evaluation of many other dependencies.
Profit vs number of trades
Author: Stanislav Korotky