Optimal F Service
- Утилиты
- Версия: 1.0
- Активации: 10
Optimal F Service
- тип приложения: сервис
- функции приложения: расчёт оптимальной фракции и объёма торговли для достижения максимального роста кривой баланса, основываясь на результатах предыдущих сделок
О приложении
Управление капиталом -- это важнейшая и недооценённая часть любой торговой системы. Правильное управление капиталом позволяет улучшить, а иногда и сильно улучшить результаты вашего торгового алгоритма.
Данное приложение в автоматическом режиме расчитывает оптимальную фракцию и объём по алгоритму предложенному Ральфом Винсом в книге "Математика управления капиталом" для достижения максимального геометрического роста депозита. Эта точка существует и единственная для любой торговой системы, по этому её необходимо знать. В своих торговых системах вы должны использовать объём не больше, чем оптимальный!
Алгоритмы управления капиталом НЕ предназначены для убытчных в математическом смысле систем на основе усреднений, мартингейла и подобных. Данные системы будут офильтровываться приложением до расчётов, так как оптимальная фракция таких систем и оптимальный объём всегда = 0. Алгоритмы управления капиталом способны улучшить результат ТОЛЬКО прибыльных в математическом смысле торговых систем (положительное математическое ожидание). По этому данный сервис рекомендуется ТОЛЬКО для профессионалов, которые понимают, что делают.
Так же данный алгоритм не учитывает корреляцию (зависимость) между одновременно работающими системами, по этому для эффективной работы алгоритма, необходимо хорошо двиверсифицированое множество торговых систем.
Как использовать
- LOG_LEVEL - уровень логирования в разделе Experts терминала. DEBUG - самый подробный. ERROR - минимум информации
- MAGIC_LIST - список идентификаторов систем (Magic Number) через ',' которые работают одновременно, и для которых требуется делать расчёт
- TRADE_FILES_PATH - путь к каталогу с файлами результатов предыдущих сделок (относительно <Data folder>/MQL5/Files/)
- OUTPUT_FILE_PATH - путь к файлу результатов расчётов (относительно <Data folder>/MQL5/Files/)
- WORK_PERIOD - частота запуска перерасчёта в секундах
- BALANCE_MATRIX_PERIOD - период, за который результаты суммируются и расчёты ведутся как результат за этот период, а не для каждой отдельной сделки
Перед первым запуском необходимо протестировать каждую торговую систему в тестере за период до настоящего момента. Рекомендуется выбирать интервал, чтобы было не меньше 100 торговых сделок. Далее используя скрипт Test Trade Saver Script , следуя инструкции получаем файлы результатов из файлов тестирования (*.tst) в нужном формате.
Если торговая система уже использовалась в терминале и в истории уже есть позиции с указанным MAGIC, следует в параметре скрипта указать отличный от ранее используемого CUSTOM_MAGIC_NUMBER !!!
Затем для того, чтобы файлы данных регулярно пополнялись и были актуальными, необходимо запустить сервис Trade Saver Service , следуя инструкции.
Таким образом после первой выгрузки данных из тестов используя Trade Saver Script , Trade Saver Service регулярно пополняет файлы новыми данными, если они появляются, а Optimal F Service регулярно расчитывает и записывает новые значения в файл результата.
Алгоритм
- Из MAGIC_LIST получаем список систем для которых требуется провести расчёт
- Используя текстовые файлы с именем <MAGIC>.csv в формате <MAGIC>,<POSITION_CLOSE_TIME>,<LOTS>,<RESULT_$> с результатами предыдущих сделок из директории TRADE_FILES_PATH составляем матрицу для функции кривой баланса, где каждое значение a[i][j] - результат торговой системы i за период j
- Проверяем каждую систему на наличие хотя бы одного отрицательного периода. Если такого нет, то для системы расчёты производиться не будут. (такие системы необходимо убрать)
- Проверяем систему на наличие положительного оценочного математического ожидания. Если нет, то для системы расчёты производиться не будут. (такие системы необходимо убрать)
- Для оставшихся систем расчитываем погрешность вычислений для получения торгового объёма с точностью до 0.01
- Расчитываем оптимальную фракцию для каждой системы
- Делим текущий баланс на равные части для оставшихся систем, и для каждой такой системы и части баланса расчитывается объём в лотах для торговли, соответствующий оптимальной фракции
- Результаты записываются в текстовый файл OUTPUT_FILE_PATH в формате <MAGIC>,<BIGGEST_LOSS>,<OPTIMAL_F>,<OPTIMAL_LOTS>
Ссылки и зависимости
- Ralph Vince - The Mathematics of Money Management: Risk Analysis Techniques for Traders (ISBN-13 978-0471547389)
Для разработчиков
Для использования результатов в своих торговых системах можно использовать следующий класс
#include "OptimalFResultsLoader.mqh" // create loader COptimalFResultsLoader* optimalFResultsLoader = new COptimalFResultsLoader("/SRProject/results.csv"); // print all fields for magic = '1111' Print(optimalFResultsLoader.getOptimalFFor(1111), " ", optimalFResultsLoader.getBiggestLossFor(1111), " ", optimalFResultsLoader.getOptimalLotsFor(1111)); // delete loader from memory delete(optimalFResultsLoader);
//+------------------------------------------------------------------+ //| OptimalFResultsLoader.mqh | //| Semyon Racheev | //| | //+------------------------------------------------------------------+ #property copyright "Semyon Racheev" #property link "" #property version "1.00" #include <Files\FileTxt.mqh> class COptimalFResultsLoader { private: const string name_; const uchar delimiter_; const ushort separator_; const string srcFilePath_; bool checkStringForOptimalFResultsDeserializing(string &inputStr[]) const; ushort calculateCharCode(const uchar separator) const; public: COptimalFResultsLoader(const string srcFilePath, uchar separator); ~COptimalFResultsLoader(); double getOptimalLotsFor(const ulong magicNumber) const; double getOptimalFFor(const ulong magicNumber) const; double getBiggestLossFor(const ulong magicNumber) const; }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ COptimalFResultsLoader::COptimalFResultsLoader(const string srcFilePath = "/SRProject/results.csv", uchar separator = ','):name_("OptimalFResultsLoader"), srcFilePath_(srcFilePath), delimiter_(separator), separator_(calculateCharCode(separator)) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ COptimalFResultsLoader::~COptimalFResultsLoader() { } //+------------------public------------------------------------------+ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double COptimalFResultsLoader::getOptimalLotsFor(const ulong inputMagicNumber) const { double rsl = 0.0; CFileTxt* file = new CFileTxt(); int fileHandle = file.Open(srcFilePath_,FILE_READ|FILE_UNICODE|FILE_CSV); while (!FileIsEnding(fileHandle)) { string readString = file.ReadString(); string str[]; StringSplit(readString, separator_, str); if (checkStringForOptimalFResultsDeserializing(str)) { if (inputMagicNumber == (ulong)StringToInteger(str[0])) { rsl = StringToDouble(str[3]); } } } file.Close(); delete(file); return(rsl); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double COptimalFResultsLoader::getOptimalFFor(const ulong inputMagicNumber) const { double rsl = 0.0; CFileTxt* file = new CFileTxt(); int fileHandle = file.Open(srcFilePath_,FILE_READ|FILE_UNICODE|FILE_CSV); while (!FileIsEnding(fileHandle)) { string readString = file.ReadString(); string str[]; StringSplit(readString, separator_, str); if (checkStringForOptimalFResultsDeserializing(str)) { if (inputMagicNumber == (ulong)StringToInteger(str[0])) { rsl = StringToDouble(str[2]); } } } file.Close(); delete(file); return(rsl); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double COptimalFResultsLoader::getBiggestLossFor(const ulong inputMagicNumber) const { double rsl = 0.0; CFileTxt* file = new CFileTxt(); int fileHandle = file.Open(srcFilePath_,FILE_READ|FILE_UNICODE|FILE_CSV); while (!FileIsEnding(fileHandle)) { string readString = file.ReadString(); string str[]; StringSplit(readString, separator_, str); if (checkStringForOptimalFResultsDeserializing(str)) { if (inputMagicNumber == (ulong)StringToInteger(str[0])) { rsl = StringToDouble(str[1]); } } } file.Close(); delete(file); return(rsl); } //+---------------------private--------------------------------------+ //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool COptimalFResultsLoader::checkStringForOptimalFResultsDeserializing(string &inputStr[]) const { if (ArraySize(inputStr) < 4) { return(false); } return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ ushort COptimalFResultsLoader::calculateCharCode(const uchar separator) const { string str = CharToString(separator); return(StringGetCharacter(str,0)); } //+------------------------------------------------------------------+
