#property copyright "Scriptong" #property link "http://advancetools.net" #property description "English: Rating of the trade system by htm-report or by account history. Description is available at AdvanceTools.net in the section \"Tools\".\nRussian: Оценка торговой системы по htm-отчету или по истории счета. Описание доступно на сайте AdvanceTools.net в разделе \"Инструменты\"." #property version "2.00" #property strict #property show_inputs enum ENUM_RATING_TYPE { RATING_TYPE_HISTORY, // By account history/По истории счета RATING_TYPE_STATEMENT // By html-report file/По html-отчету }; enum ENUM_REPORT_TYPE { REPORT_TYPE_NONE, // Report type could not be defined REPORT_TYPE_ACCOUNT, // The report contains information about online trade REPORT_TYPE_TESTER // The report file created by the strategy tester }; enum ENUM_TAG_FIND_STATE { TAG_STATE_NONE, // Tag is not finding TAG_STATE_START_OPEN, // An opening bracket of start tag found () TAG_STATE_TAGCONTENT_COMPLETE // Tag content has formed }; enum ENUM_MESSAGE_CODE { MESSAGE_CODE_ENOUGHT_MEMORY, MESSAGE_CODE_FILE_OPEN_ERROR, MESSAGE_CODE_INVALID_FILE_FORMAT, MESSAGE_CODE_FILE_FORMAT_UNDEFINED, MESSAGE_CODE_TABLE_HEADER_ABSENT, MESSAGE_CODE_QUALITY, MESSAGE_CODE_EXPECTATION, MESSAGE_CODE_DEVIATION, MESSAGE_CODE_DEALS, MESSAGE_CODE_VERY_POOR, MESSAGE_CODE_POOR, MESSAGE_CODE_AVERAGE, MESSAGE_CODE_GOOD, MESSAGE_CODE_EXCELLENT, MESSAGE_CODE_SUPERIOR, MESSAGE_CODE_GRAIL }; enum ENUM_RATE_WAY { RATE_WAY_VAN_THARP, // Van Tharp method / Метод Ван Тарпа RATE_WAY_SORTINO // Sortino factor / Коэффициент Сортино }; input ENUM_RATING_TYPE i_ratingType = RATING_TYPE_HISTORY; // To rate/Оценивать input ENUM_RATE_WAY i_ratingWay = RATE_WAY_VAN_THARP; // Rating way / Способ оценки input string i_fileName = "Statement.htm"; // File name of htm-report/Имя файла htm-отчета #define FILE_FORMAT_STRING "T / P" //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Script program start function | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ void OnStart() { int dealsCnt = 0; double dealsProfit[]; if (i_ratingType == RATING_TYPE_HISTORY) { if (!FormDealsListByHistory(dealsProfit, dealsCnt)) return; } else if (!FormDealsListByReport(dealsProfit, dealsCnt)) return; double expectationValue, stdDev; double quality = CalculateQuality(dealsProfit, dealsCnt, expectationValue, stdDev); Alert(GetStringByMessageCode(MESSAGE_CODE_QUALITY), GetStringCharacteristic(quality), " (", DoubleToString(quality, 2), "), ", GetStringByMessageCode(MESSAGE_CODE_EXPECTATION), DoubleToString(expectationValue, 2), ", ", GetStringByMessageCode(MESSAGE_CODE_DEVIATION), DoubleToString(stdDev, 2), ", ", GetStringByMessageCode(MESSAGE_CODE_DEALS), dealsCnt); } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Calculate data by account history | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ bool FormDealsListByHistory(double &dealsProfit[], int &dealsCnt) { for (int i = OrdersHistoryTotal() - 1; i >= 0; i--) { if (!OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) continue; if (OrderType() != OP_BUY && OrderType() != OP_SELL) continue; if (ArrayResize(dealsProfit, dealsCnt + 1, 1000) < 0) { Alert(WindowExpertName(), GetStringByMessageCode(MESSAGE_CODE_ENOUGHT_MEMORY)); return false; } dealsProfit[dealsCnt] = OrderProfit() + OrderCommission() + OrderSwap(); dealsCnt++; } return true; } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Calculate data by htm-report | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ bool FormDealsListByReport(double &dealsProfit[], int &dealsCnt) { int hFile; if (!OpenFile(i_fileName, hFile)) return false; if (!IsFileFormatCorrect(hFile)) return false; return IsReadSuccess(hFile, dealsProfit, dealsCnt); } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Try to open the report file | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ bool OpenFile(string fileName, int &hFile) { hFile = FileOpen(fileName, FILE_READ | FILE_SHARE_READ | FILE_TXT); if (hFile != INVALID_HANDLE) return true; int error = GetLastError(); Alert(WindowExpertName(), GetStringByMessageCode(MESSAGE_CODE_FILE_OPEN_ERROR), fileName, " (", error, ")."); return false; } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Checking the file format | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ bool IsFileFormatCorrect(int hFile) { string firstString = FileReadString(hFile); if (StringFind(firstString, FILE_FORMAT_STRING) == 0) return true; Alert(WindowExpertName(), GetStringByMessageCode(MESSAGE_CODE_INVALID_FILE_FORMAT)); FileClose(hFile); return false; } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Reading the file | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ bool IsReadSuccess(int hFile, double &dealsProfit[], int &dealsCnt) { ENUM_REPORT_TYPE reportType = DefineReportType(hFile); if (reportType == REPORT_TYPE_NONE) { Alert(WindowExpertName(), GetStringByMessageCode(MESSAGE_CODE_FILE_FORMAT_UNDEFINED)); FileClose(hFile); return false; } if (!FindTableHeader(hFile)) { Alert(WindowExpertName(), GetStringByMessageCode(MESSAGE_CODE_TABLE_HEADER_ABSENT)); FileClose(hFile); return false; } return ReadByRows(hFile, reportType, dealsProfit, dealsCnt); } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Defining type of the report file | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ENUM_REPORT_TYPE DefineReportType(int hFile) { string tagTitleContent = GetTagContent(hFile, "title"); if (StringFind(tagTitleContent, TITLE_STATEMENT) == 0) return REPORT_TYPE_ACCOUNT; if (StringFind(tagTitleContent, TITLE_TESTER) == 0) return REPORT_TYPE_TESTER; return REPORT_TYPE_NONE; } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Reading of the specified file until reading the specified tag | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ string GetTagContent(int hFile, string tag) { ENUM_TAG_FIND_STATE tagState = TAG_STATE_NONE; string tagContent = ""; string tagStart = "<" + tag; string tagEnd = "", startPos); if (tagEndChar < 0) return 0; tagState = TAG_STATE_START_COMPLETE; return FindClosingTagStart(tagEnd, str, tagContent, tagState, tagEndChar + 1); } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Finding the start of closing tag | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ int FindClosingTagStart(string &tagEnd, string &str, string &tagContent, ENUM_TAG_FIND_STATE &tagState, int startPos = 0) { int tagBeginChar = StringFind(str, tagEnd, startPos); if (tagBeginChar < 0) { tagContent = tagContent + StringSubstr(str, startPos); return 0; } tagState = TAG_STATE_TAGCONTENT_COMPLETE; tagContent = tagContent + StringSubstr(str, startPos, tagBeginChar - startPos); return tagBeginChar + 1; } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Finding the tag and its content in the string | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ ENUM_TAG_FIND_STATE GetTagContentFromString(string &tag, string &str, string &tagContent, int &startPos) { tagContent = ""; string tagStart = "<" + tag; string tagEnd = "= 0) return true; } return false; } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Read the table row by row | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ bool ReadByRows(int hFile, ENUM_REPORT_TYPE reportType, double &dealsProfit[], int &dealsCnt) { while(!FileIsEnding(hFile)) { string trContent = GetTagContent(hFile, "tr"); double dealResult = GetDealResult(trContent, reportType); if (dealResult == DBL_MAX) continue; if (dealResult == DBL_MAX - 1) break; if (ArrayResize(dealsProfit, dealsCnt + 1, 1000) < 0) { Alert(WindowExpertName(), GetStringByMessageCode(MESSAGE_CODE_ENOUGHT_MEMORY)); return false; } dealsProfit[dealsCnt] = dealResult; dealsCnt++; } FileClose(hFile); return true; } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Parse one row of the table for deal profit | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ double GetDealResult(string &trContent, ENUM_REPORT_TYPE reportType) { if (reportType == REPORT_TYPE_ACCOUNT) return GetResultByAccount(trContent); return GetResultByStrategyTester(trContent); } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Parse the string formed by the history of the account | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ double GetResultByAccount(string &trContent) { string tagContent; string tag = "td"; int startPos = 0; double result = 0.0; for (int i = 0; i < 14; i++) { ENUM_TAG_FIND_STATE tagState = GetTagContentFromString(tag, trContent, tagContent, startPos); if (tagState != TAG_STATE_TAGCONTENT_COMPLETE) return DBL_MAX; if (tagContent == " ") return DBL_MAX - 1; if (i == 2 && tagContent != "buy" && tagContent != "sell") return DBL_MAX; if (i < 10) continue; result += StringToDouble(tagContent); } return result; } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Parse the string formed by the strategy tester | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ double GetResultByStrategyTester(string &trContent) { string tagContent; string tag = "td"; int startPos = 0; for (int i = 0; i < 9; i++) { ENUM_TAG_FIND_STATE tagState = GetTagContentFromString(tag, trContent, tagContent, startPos); if (tagState != TAG_STATE_TAGCONTENT_COMPLETE) return DBL_MAX; if (tagContent == " ") return DBL_MAX - 1; } double result = StringToDouble(tagContent); if (result == 0.0 && tagContent != "0.00") return DBL_MAX; return result; } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Calculate quality by data | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ double CalculateQuality(double &dealsProfit[], int dealsCnt, double &expectationValue, double &stdDev) { if (dealsCnt < 2) return 0.0; // Calculating of expectation value double expectationX2; expectationValue = GetExpectation(dealsProfit, dealsCnt, expectationX2); // Calculating of standart deviation stdDev = MathSqrt(expectationX2 - expectationValue * expectationValue); if (stdDev == 0.0) return 0.0; // Calculating of outcome switch (i_ratingWay) { case RATE_WAY_VAN_THARP: return expectationValue / stdDev; case RATE_WAY_SORTINO: return CalculateBySortino(dealsProfit, dealsCnt, expectationValue); } return 0.0; } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Calculate of expectation value | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ double GetExpectation(double &dealsProfit[], int dealsCnt, double &expectationX2) { double summ = 0.0, summX2 = 0.0; for (int i = 0; i < dealsCnt; i++) { summ += dealsProfit[i]; summX2 += dealsProfit[i] * dealsProfit[i]; } expectationX2 = summX2 / dealsCnt; return summ / dealsCnt; } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Calculate of Sortino factor | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ double CalculateBySortino(double &dealsProfit[], int dealsCnt, double expectationValue) { if (dealsCnt <= 0) return 0.0; // Getting the sum of square loss deals double sum = 0.0; for (int i = 0; i < dealsCnt; i++) if (dealsProfit[i] < 0.0) sum += dealsProfit[i] * dealsProfit[i]; // Square root from average value double sqrtValue = MathSqrt(sum / dealsCnt); if (sqrtValue <= 0.0) return 0.0; // Result return expectationValue / sqrtValue; } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| String quality characteristic | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ string GetStringCharacteristic(double quality) { switch (i_ratingWay) { case RATE_WAY_VAN_THARP: return GetStringCharacteristicByVanTharp(quality); case RATE_WAY_SORTINO: return GetStringCharacteristicBySortino(quality); } return "N/D"; } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| String quality characteristic by Van Tharp method | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ string GetStringCharacteristicByVanTharp(double quality) { if (quality < 0.16) return GetStringByMessageCode(MESSAGE_CODE_VERY_POOR); if (quality < 0.2) return GetStringByMessageCode(MESSAGE_CODE_POOR); if (quality < 0.25) return GetStringByMessageCode(MESSAGE_CODE_AVERAGE); if (quality < 0.3) return GetStringByMessageCode(MESSAGE_CODE_GOOD); if (quality < 0.5) return GetStringByMessageCode(MESSAGE_CODE_EXCELLENT); if (quality < 0.7) return GetStringByMessageCode(MESSAGE_CODE_SUPERIOR); return GetStringByMessageCode(MESSAGE_CODE_GRAIL); } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| String quality characteristic by Sortino factor | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ string GetStringCharacteristicBySortino(double quality) { if (quality < 0.24) return GetStringByMessageCode(MESSAGE_CODE_VERY_POOR); if (quality < 0.3) return GetStringByMessageCode(MESSAGE_CODE_POOR); if (quality < 0.38) return GetStringByMessageCode(MESSAGE_CODE_AVERAGE); if (quality < 0.45) return GetStringByMessageCode(MESSAGE_CODE_GOOD); if (quality < 0.75) return GetStringByMessageCode(MESSAGE_CODE_EXCELLENT); if (quality < 1.0) return GetStringByMessageCode(MESSAGE_CODE_SUPERIOR); return GetStringByMessageCode(MESSAGE_CODE_GRAIL); } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Getting string by code of message and terminal language | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ string GetStringByMessageCode(ENUM_MESSAGE_CODE messageCode) { string language = TerminalInfoString(TERMINAL_LANGUAGE); if (language == "Russian") return GetRussianMessage(messageCode); return GetEnglishMessage(messageCode); } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Getting string by code of message for russian language | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ string GetRussianMessage(ENUM_MESSAGE_CODE messageCode) { switch (messageCode) { case MESSAGE_CODE_ENOUGHT_MEMORY: return ": недостаточно памяти для обработки данных."; case MESSAGE_CODE_FILE_OPEN_ERROR: return ": ошибка открытия файла "; case MESSAGE_CODE_INVALID_FILE_FORMAT: return ": неверный формат файла."; case MESSAGE_CODE_FILE_FORMAT_UNDEFINED: return ": не удалось определить тип файла отчета."; case MESSAGE_CODE_TABLE_HEADER_ABSENT: return ": не удалось найти шапку таблицы закрытых сделок."; case MESSAGE_CODE_QUALITY: return "Качество: "; case MESSAGE_CODE_EXPECTATION: return "матожидание: "; case MESSAGE_CODE_DEVIATION: return "отклонение: "; case MESSAGE_CODE_DEALS: return "сделок: "; case MESSAGE_CODE_VERY_POOR: return "очень низкое, торговать нельзя"; case MESSAGE_CODE_POOR: return "низкое, но торговать можно"; case MESSAGE_CODE_AVERAGE: return "среднее"; case MESSAGE_CODE_GOOD: return "хорошее"; case MESSAGE_CODE_EXCELLENT: return "отличное"; case MESSAGE_CODE_SUPERIOR: return "превосходное"; case MESSAGE_CODE_GRAIL: return "священный Грааль))"; } return ""; } //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ //| Getting string by code of message for english language | //+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ string GetEnglishMessage(ENUM_MESSAGE_CODE messageCode) { switch (messageCode) { case MESSAGE_CODE_ENOUGHT_MEMORY: return ": data processing is not enough memory."; case MESSAGE_CODE_FILE_OPEN_ERROR: return ": error opening file "; case MESSAGE_CODE_INVALID_FILE_FORMAT: return ": format of report file is incorrect."; case MESSAGE_CODE_FILE_FORMAT_UNDEFINED: return ": could not be defined the report file type."; case MESSAGE_CODE_TABLE_HEADER_ABSENT: return ": could not be found the header of table of closed transactions."; case MESSAGE_CODE_QUALITY: return "Quality: "; case MESSAGE_CODE_EXPECTATION: return "expectation: "; case MESSAGE_CODE_DEVIATION: return "deviation: "; case MESSAGE_CODE_DEALS: return "deals: "; case MESSAGE_CODE_VERY_POOR: return "very poor, trade not allowed"; case MESSAGE_CODE_POOR: return "poor, but trade is possible"; case MESSAGE_CODE_AVERAGE: return "average"; case MESSAGE_CODE_GOOD: return "good"; case MESSAGE_CODE_EXCELLENT: return "excellent"; case MESSAGE_CODE_SUPERIOR: return "superior"; case MESSAGE_CODE_GRAIL: return "Sangrail))"; } return ""; }