Вся статистика, что есть в Отчете одиночного прогона, доступна. Раньше многое из этого было никак не получить даже при наличии исходника советника.
double ghpr; // среднее геометрическое сделки double ghprpercent; // среднее геометрическое сделки в процентах double ahpr; // среднее арифметическое сделки double ahprpercent; // среднее арифметическое сделки в процентах double zscore; // серийный тест double zscorepercent; // серийный тест в процентах double lrcorr; // коэффициент корреляции линейной регрессии double lrstderror; // стандартная ошибка отклонения баланса от линейной регрессии double corr_prf_mfe; // корреляция между mfe и profit double corr_prf_mae; // корреляция между mae и profit double corr_mfe_mae; // корреляция между mae и mfe double mfe_a; // корреляция между mfe и profit, коэф. для линии линейной регрессии double mfe_b; // корреляция между mfe и profit, коэф. для линии линейной регрессии double mae_a; // корреляция между mae и profit, коэф. для линии линейной регрессии double mae_b; // корреляция между mae и profit, коэф. для линии линейной регрессии UINT in_per_hours[24]; // распределение входов по часам UINT in_per_week_days[7]; // распределение входов по дням недели UINT in_per_months[12]; // распределение входов по месяцам double out_per_hours[24][2]; // распределение входов по часам double out_per_week_days[7][2]; // распределение входов по дням недели double out_per_months[12][2]; // распределение входов по месяцам INT64 holding_time_min; // минимальное время удержания позиции INT64 holding_time_max; // максимальное время удержания позиции INT64 holding_time_avr; // среднее время удержания позиции
В текущей версии tst-формата нет следующих данных
- Время в миллисекундах.
- PositionID.
- MagicNumber.
Воспроизведение нескольких багов. Запускаем советник в Тестере на хедж-счете.
#include <MT4Orders.mqh> // https://www.mql5.com/ru/code/16006 #define Ask SymbolInfoDouble(_Symbol, SYMBOL_ASK) #define PAUSE 100000 void OnTick() { static bool FirstRun = true; if (FirstRun) { OrderSend(_Symbol, OP_BUY, 1, Ask, 0, 0, 0); Sleep(PAUSE); OrderSend(_Symbol, OP_BUY, 2, Ask, 0, 0, 0); Sleep(PAUSE); if (OrderSelect(0, SELECT_BY_POS)) OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0); Sleep(PAUSE * 2); if (OrderSelect(0, SELECT_BY_POS)) OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0); Sleep(PAUSE * 2); TesterWithdrawal(100); FirstRun = false; } } void OnDeinit( const int ) { const int Total = OrdersHistoryTotal(); for (int i = 0; i < Total; i++) if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) { OrderPrint(); Print(OrderTicketID()); // MT5-PositionID } }
Получаем такое
2020.01.08 23:59:58 #1 2020.01.01 00:00:00 balance 0.00 0.00000 0.00000 0.00000 2020.01.01 00:00:00 0.00000 0.00 0.00 100000.00 0 2020.01.08 23:59:58 0 2020.01.08 23:59:58 #4 2020.01.02 06:00:00 buy 1.00 EURUSD 1.12137 0.00000 0.00000 2020.01.02 06:03:20 1.12132 -3.56 0.00 -4.46 0 2020.01.08 23:59:58 2 2020.01.08 23:59:58 #5 2020.01.02 06:01:40 buy 2.00 EURUSD 1.12137 0.00000 0.00000 2020.01.02 06:06:40 1.12129 -7.14 0.00 -14.27 0 2020.01.08 23:59:58 3 2020.01.08 23:59:58 #6 2020.01.02 06:10:00 balance 0.00 0.00000 0.00000 0.00000 2020.01.02 06:10:00 0.00000 0.00 0.00 -100.00 withdrawal 0 2020.01.08 23:59:58 0
Далее считываем соответствующий tst-файл скриптом.
#include <fxsaber\SingleTesterCache\SingleTesterCache.mqh> // https://www.mql5.com/ru/code/27611 #include <fxsaber\MultiTester\MTTester.mqh> // https://www.mql5.com/ru/code/26132 void OnStart() { uchar Bytes2[]; if (MTTESTER::GetLastTstCache(Bytes2) != -1) // Если получилось прочитать последнюю кеш-запись одиночного прогона { const SINGLETESTERCACHE SingleTesterCache(Bytes2); // Загоняем ее в соответствующий объект. for (int i = 0; i < ArraySize(SingleTesterCache.Positions); i++) Print(SingleTesterCache.Positions[i].ToString()); } }
Он выведет данные по позициям
id = 0 mfe = 0.0 mae = -8.029999999999999 profit = -4.46 lifetime = 00:03:20 id = 0 mfe = 0.0 mae = -21.4 profit = -14.27 lifetime = 00:05:00 id = 0 mfe = 0.0 mae = 0.0 profit = 0.0 lifetime = 00:00:00
Если сопоставить все в этом посте, то формулируются следующие баги.
- Нулевые id, вместо правильных.
- Комиссия и своп не учитываются при вычислении профита.
- Withdrawal-сделка ошибочно попадает в число закрытых торговых позиций.
Вместо сет-файлов использую теперь tst-файлы. Можно очень быстро переключаться между ними, имея не только входные параметры, но и полные бэктесты.
Жаль, что полноценно объединять в портфель разные ТС сейчас нельзя из-за отсутствия миллисекунд-данных в tst.
Надеюсь, разработчики существующие поля начнут использовать по полной
INT64 TradeDeal::time_create; // время создания записи INT64 TradeOrder::time_setup; // время приёма ордера от клиента в систему INT64 TradeOrder::time_done; // время снятия завки
записывая туда значение времени не в секундах, а в миллисекундах.
Вообще, на практике продемонстрировать всю крутость использования tst не дают совсем не большие недоработки tst. Исправить бы.
// Интерактивный график баланса одиночного прохода. #include <fxsaber\SingleTesterCache\SingleTesterCache.mqh> // https://www.mql5.com/ru/code/27611 #include <fxsaber\MultiTester\MTTester.mqh> // https://www.mql5.com/ru/code/26132 #include <..\Files\Graph.mqh> // https://www.mql5.com/ru/code/18801 #import "shell32.dll" int ShellExecuteW( int, string, string, string, string, int ); #import #define BASEPATH (::TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Files\\") bool CreateBalanceData( const SINGLETESTERCACHE &SingleTesterCache, const string FileName = "exdat.txt" ) { const int handle = FileOpen(FileName, FILE_WRITE | FILE_TXT | FILE_ANSI); const bool Res = (handle != INVALID_HANDLE); if (Res) { FileWriteString(handle, "var dat1=[\n"); for (uint i = 0; i < SingleTesterCache.Header.equities_total; i++) FileWriteString(handle, "[" + (string)((long)SingleTesterCache.TradeState[i].time * 1000) + "," + ::DoubleToString(SingleTesterCache.TradeState[i].balance, 2) + "],\n"); FileWriteString(handle, "];\n"); FileWriteString(handle, "var T1=dat1[0][0];\n"); FileWriteString(handle, "var T2=dat1[dat1.length-1][0];\n"); FileWriteString(handle, "var nTrades=dat1.length;\n"); FileWriteString(handle, "var Balance=" + ::DoubleToString(SingleTesterCache.TradeState[SingleTesterCache.Header.equities_total - 1].balance, 2) + ";\n"); FileWriteString(handle, "var Currency=\"" + (SingleTesterCache.Header.trade_pips ? "Pips" : SingleTesterCache.Header.trade_currency[]) + "\";\n"); FileClose(handle); } return(Res); } void OnStart() { uchar Bytes2[]; if (MTTESTER::GetLastTstCache(Bytes2) != -1) // Если получилось прочитать последнюю кеш-запись одиночного прогона { const SINGLETESTERCACHE SingleTesterCache(Bytes2); // Загоняем ее в соответствующий объект. const string FileName = "Report.htm"; uchar Array[]; if ((StringToCharArray(StrMQH, Array) > 0) && FileSave(FileName, Array) && CreateBalanceData(SingleTesterCache)) ShellExecuteW(0, "Open", BASEPATH + FileName, NULL, NULL, 3); } }
Если заменить
TradeState[i].balance -> TradeState[i].equity
будет график эквити.
В tst неправильно пишется объем сделок/ордеров. Он всегда вычисляется так, будто SYMBOL_TRADE_CONTRACT_SIZE = 100 000.
Если выставлено другое значение, то это никак не влияет на значение объема в tst.
на практике продемонстрировать всю крутость использования tst не дают совсем не большие недоработки tst. Исправить бы.
- www.mql5.com
- 2020.01.16
- www.mql5.com
Ответил там.
Не знаю, как происходит в блогах, появление нового комментария (если это не ответ) как-то сигнализируется? Или лучше писать в одной из тем форума, где видно появление новых комментариев?
Почему не опубликовали в КБ? Было бы удобнее.
Так где писать для оперативного реагирования?
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
SingleTesterCache:
Данные одиночного прохода Тестера.
Автор: fxsaber