//+------------------------------------------------------------------+ //| Скрипт создания и загрузки CSV файла с информацией по символам | //| с разделителем ",," | //| | //| Изменения: | //| - Используется разделитель ",," вместо "..". | //| - Формула расчёта pipValue остается: pipValueRaw = tradeContractSize * pointSize; | //| Таким образом, для EURUSDz, если в CSV: | //| tradeContractSize = 100000.00 | //| pointSize = 0.00010 (то есть pip) | //| результат: 100000 * 0.00010 = 10 (как и нужно) | //+------------------------------------------------------------------+ #property script_show_inputs #property strict #include // Подключаем, если нужна торговая функциональность // Входные параметры: input int FIELDS_COUNT = 7; // Количество полей после названия (фиксированное) input string InFileName = "SymbolSpecificationsOriginal.txt"; // Исходный файл (сырые данные) input string OutFileName = "SymbolSpecifications.csv"; // Выходной файл (исправленный CSV) // Определяем разделитель через макрос: #define DELIMITER ",," //--- Структура спецификаций struct InstrumentSpec { string symbol; string description; double tradeContractSize; double tradeTickSize; double pointSize; int digits; double volumeStep; double volumeMin; double volumeMax; string currencyProfit; double spreadFloat; }; InstrumentSpec Specs[]; // Функция удаления кавычек string TrimQuotes(string str) { StringReplace(str, "\"", ""); return(str); } //-------------------------------------------------------------- // Функция MyStringSplit // Разбивает строку source по подстроке delimiter и возвращает массив строк в result[] // Возвращает количество найденных элементов. //-------------------------------------------------------------- int MyStringSplit(const string source, const string delimiter, string &result[]) { ArrayResize(result, 0); int delimLen = StringLen(delimiter); int start = 0; int pos = StringFind(source, delimiter, start); while(pos != -1) { string token = StringSubstr(source, start, pos - start); int idx = ArraySize(result); ArrayResize(result, idx + 1); result[idx] = token; start = pos + delimLen; pos = StringFind(source, delimiter, start); } // Последний элемент string token = StringSubstr(source, start); int idx = ArraySize(result); ArrayResize(result, idx + 1); result[idx] = token; return(ArraySize(result)); } //-------------------------------------------------------------- // Функция загрузки CSV файла с разделителем DELIMITER (",,") //-------------------------------------------------------------- bool LoadInstrumentSpecs() { int fileHandle = FileOpen("SymbolSpecifications.csv", FILE_READ|FILE_ANSI); if(fileHandle == INVALID_HANDLE) { Print("Не удалось открыть файл SymbolSpecifications.csv"); return(false); } // Считываем заголовок и пропускаем его string header = FileReadString(fileHandle); ArrayResize(Specs, 0); while(!FileIsEnding(fileHandle)) { string line = FileReadString(fileHandle); if(StringLen(line) == 0) continue; // Разбиваем строку по разделителю DELIMITER (",,") string fields[]; int count = MyStringSplit(line, DELIMITER, fields); if(count < 11) { Print("Строка не содержит 11 полей: ", line); continue; } InstrumentSpec spec; spec.symbol = TrimQuotes(fields[0]); spec.description = TrimQuotes(fields[1]); spec.tradeContractSize = StringToDouble(TrimQuotes(fields[2])); spec.tradeTickSize = StringToDouble(TrimQuotes(fields[3])); spec.pointSize = StringToDouble(TrimQuotes(fields[4])); spec.digits = (int)StringToDouble(TrimQuotes(fields[5])); spec.volumeStep = StringToDouble(TrimQuotes(fields[6])); spec.volumeMin = StringToDouble(TrimQuotes(fields[7])); spec.volumeMax = StringToDouble(TrimQuotes(fields[8])); spec.currencyProfit = TrimQuotes(fields[9]); spec.spreadFloat = StringToDouble(TrimQuotes(fields[10])); int size = ArraySize(Specs); ArrayResize(Specs, size+1); Specs[size] = spec; } FileClose(fileHandle); Print("Загружено спецификаций инструментов: ", ArraySize(Specs)); return(true); } //-------------------------------------------------------------- // Функция поиска спецификации по символу //-------------------------------------------------------------- bool GetInstrumentSpec(string symbol, InstrumentSpec &spec) { for(int i = 0; i < ArraySize(Specs); i++) { if(Specs[i].symbol == symbol) { spec = Specs[i]; return(true); } } return(false); } //-------------------------------------------------------------- // Функция конвертации суммы из одной валюты в другую //-------------------------------------------------------------- double ConvertToDeposit(double amount, string fromCurrency, string toCurrency="USD") { if(fromCurrency == toCurrency) return(amount); string directSymbol = fromCurrency + toCurrency; if(SymbolSelect(directSymbol, true)) { double rate = SymbolInfoDouble(directSymbol, (ENUM_SYMBOL_INFO_DOUBLE)SYMBOL_BID); if(rate > 0.0) return(amount * rate); } string reverseSymbol = toCurrency + fromCurrency; if(SymbolSelect(reverseSymbol, true)) { double rate = SymbolInfoDouble(reverseSymbol, (ENUM_SYMBOL_INFO_DOUBLE)SYMBOL_BID); if(rate > 0.0) return(amount / rate); } Print("Не найдена котировка для конвертации ", fromCurrency, "->", toCurrency); return(amount); } //-------------------------------------------------------------- // Функция расчёта pipValue для инструмента //-------------------------------------------------------------- double GetInstrumentPipValue(string symbol, string &profitCurrency, bool &foundSpec) { InstrumentSpec spec; if(GetInstrumentSpec(symbol, spec)) { foundSpec = true; profitCurrency = spec.currencyProfit; // Формула: pipValueRaw = tradeContractSize * pointSize (pointSize трактуется как pip) double pipValueRaw = spec.tradeContractSize * spec.pointSize; double pipValueUSD = ConvertToDeposit(pipValueRaw, spec.currencyProfit, "USD"); return(pipValueUSD); } else { foundSpec = false; profitCurrency = ""; double tickValue = SymbolInfoDouble(symbol, (ENUM_SYMBOL_INFO_DOUBLE)SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(symbol, (ENUM_SYMBOL_INFO_DOUBLE)SYMBOL_TRADE_TICK_SIZE); if(tickValue > 0.0 && tickSize > 0.0) { double pip = (StringFind(symbol, "JPY") != -1) ? 0.01 : 0.0001; return((tickValue/tickSize) * pip); } return(10.0); } } //-------------------------------------------------------------- // Функция InsertSpacesBetweenLettersAndDigits с явными скобками //-------------------------------------------------------------- string InsertSpacesBetweenLettersAndDigits(string text) { string result; int len = StringLen(text); for(int i = 0; i < len; i++) { string cur = StringSubstr(text, i, 1); string prev = (i > 0) ? StringSubstr(text, i - 1, 1) : ""; bool prevIsLetter = false, prevIsDigitOrDot = false; bool curIsLetter = false, curIsDigitOrDot = false; if(prev != "") { if (((prev[0] >= 'A') && (prev[0] <= 'Z')) || ((prev[0] >= 'a') && (prev[0] <= 'z'))) prevIsLetter = true; if (((prev[0] >= '0') && (prev[0] <= '9')) || (prev == ".")) prevIsDigitOrDot = true; } if (((cur[0] >= 'A') && (cur[0] <= 'Z')) || ((cur[0] >= 'a') && (cur[0] <= 'z'))) curIsLetter = true; if (((cur[0] >= '0') && (cur[0] <= '9')) || (cur == ".")) curIsDigitOrDot = true; if ((prevIsLetter && curIsDigitOrDot)) result += " "; else if ((prevIsDigitOrDot && curIsLetter)) result += " "; result += cur; } return(result); } //-------------------------------------------------------------- // Функция CompressSpaces – сжимает повторяющиеся пробелы в один //-------------------------------------------------------------- string CompressSpaces(string text) { while(StringFind(text, " ") >= 0) StringReplace(text, " ", " "); return(text); } //-------------------------------------------------------------- // Функция FixLine – преобразует строку в формат: // "<Название>,,<Поле1>,,<Поле2>,,<Поле3>,,<Поле4>,,<Поле5>,,<Поле6>,,<Поле7>" // Название остаётся без изменений, даже если содержит пробелы, // а последние FIELDS_COUNT слов считаются отдельными полями. //-------------------------------------------------------------- string FixLine(string line) { line = InsertSpacesBetweenLettersAndDigits(line); line = CompressSpaces(line); string parts[]; int countAll = StringSplit(line, ' ', parts); if(countAll < (FIELDS_COUNT + 1)) return(line); string namePart = parts[0]; for(int i = 1; i < countAll - FIELDS_COUNT; i++) namePart += " " + parts[i]; string fieldPart[]; ArrayResize(fieldPart, FIELDS_COUNT); for(int i = 0; i < FIELDS_COUNT; i++) fieldPart[i] = parts[countAll - FIELDS_COUNT + i]; string fieldsJoined = fieldPart[0]; for(int i = 1; i < FIELDS_COUNT; i++) fieldsJoined += DELIMITER + fieldPart[i]; string finalLine = namePart + DELIMITER + fieldsJoined; return(finalLine); } //+------------------------------------------------------------------+ //| Основная функция скрипта OnStart() | //+------------------------------------------------------------------+ void OnStart() { // 1. Создаём CSV-файл с разделителем DELIMITER (",,") и записываем данные о символах int fileHandle = FileOpen(OutFileName, FILE_WRITE|FILE_ANSI); if(fileHandle == INVALID_HANDLE) { Print("Ошибка создания файла ", OutFileName); return; } string header = "symbol" + DELIMITER + "description" + DELIMITER + "tradeContractSize" + DELIMITER + "tradeTickSize" + DELIMITER + "pointSize" + DELIMITER + "digits" + DELIMITER + "volumeStep" + DELIMITER + "volumeMin" + DELIMITER + "volumeMax" + DELIMITER + "currencyProfit" + DELIMITER + "spreadFloat"; FileWriteString(fileHandle, header + "\n"); int total = SymbolsTotal(true); for(int i = 0; i < total; i++) { string symbol = SymbolName(i, true); if(symbol == "") continue; double tradeContractSize = SymbolInfoDouble(symbol, (ENUM_SYMBOL_INFO_DOUBLE)SYMBOL_TRADE_CONTRACT_SIZE); double tradeTickSize = SymbolInfoDouble(symbol, (ENUM_SYMBOL_INFO_DOUBLE)SYMBOL_TRADE_TICK_SIZE); double pointSize = SymbolInfoDouble(symbol, (ENUM_SYMBOL_INFO_DOUBLE)SYMBOL_POINT); int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); double volumeStep = SymbolInfoDouble(symbol, (ENUM_SYMBOL_INFO_DOUBLE)SYMBOL_VOLUME_STEP); double volumeMin = SymbolInfoDouble(symbol, (ENUM_SYMBOL_INFO_DOUBLE)SYMBOL_VOLUME_MIN); double volumeMax = SymbolInfoDouble(symbol, (ENUM_SYMBOL_INFO_DOUBLE)SYMBOL_VOLUME_MAX); string description = SymbolInfoString(symbol, SYMBOL_DESCRIPTION); string currencyProfit = SymbolInfoString(symbol, SYMBOL_CURRENCY_PROFIT); double spreadFloat = SymbolInfoDouble(symbol, (ENUM_SYMBOL_INFO_DOUBLE)SYMBOL_SPREAD); string line = symbol + DELIMITER + description + DELIMITER + DoubleToString(tradeContractSize, 2) + DELIMITER + DoubleToString(tradeTickSize, 5) + DELIMITER + DoubleToString(pointSize, 5) + DELIMITER + IntegerToString(digits) + DELIMITER + DoubleToString(volumeStep, 2) + DELIMITER + DoubleToString(volumeMin, 2) + DELIMITER + DoubleToString(volumeMax, 2) + DELIMITER + currencyProfit + DELIMITER + DoubleToString(spreadFloat, 2); FileWriteString(fileHandle, line + "\n"); } FileClose(fileHandle); Print("CSV-файл '", OutFileName, "' создан и заполнен. Всего символов: ", total); // 2. Загружаем данные из CSV-файла if(!LoadInstrumentSpecs()) { Print("Не удалось загрузить спецификации."); return; } // 3. Выводим рассчитанное pipValue для каждого символа Print("=== Список pipValue для каждого символа из CSV ==="); for(int i = 0; i < ArraySize(Specs); i++) { string sym = Specs[i].symbol; string ccy = ""; bool found = false; double pipVal = GetInstrumentPipValue(sym, ccy, found); Print("Symbol=", sym, ", foundInCSV=", (string)found, ", currencyProfit=", ccy, ", pipValue(1 lot, 1 pip)=", DoubleToString(pipVal, 6)); } Print("=== Конец списка ==="); }