Доброго времени суток!
Как мы знаем, правильное соблюдение правил мани менеджмента являемся одном из аспектов прибыльной торговли. Мы не можем просто взять и назначить сделке объем в 0.5 лота, просто потому, что нам кажется, что этого будет достаточно. Все должно быть рассчитано и просчитано. Представим ситуацию, что у вас открыт ПАММ счет и его баланс постоянно изменяется. Можно просто сойти с ума от ручной корректировки торгового лота вашего советника каждый раз, когда счет пополнит крупный инвестор. Чтобы автоматизировать данный процесс, мы создадим отдельную библиотеку со статическим лотом и тремя вариантами расчета АвтоММ, прикрепим ее к простенькой сове, показав примеры применения.
Создаем библиотеку для автоматического расчета риска
Почему отдельная библиотека? Да просто для удобства. Один раз создав небольшой кусок кода в отдельном файле мы сможем прикреплять его к любому советнику и индикатору, а не ломать клавиши Ctrl+C, Ctrl+V каждый раз, когда нам будет нужен данный код.
Итак, в редакторе Meta Editor жмем Файл -> Создать. Выбираем пункт «Библиотека», жмем Далее. Задаем ей имя (в моем случае DaVinci AutoMM) и жмем Готово. Внутри удаляем ненужный код в комментариях.
О том, что данный файл является библиотекой сигнализирует строчка #property library.
Мы будем использовать переключатель в виде перечисления enum. В итоге у нас будет четыре варианта выбора из списка:
- Фиксированный лот (Fixed lot)
- Расчет лота по заданному пользователем балансу на указанный лот (By balance)
- Расчет лота по заданному пользователем значению свободной маржи на указанный лот (By free margin)
- Расчет лота по заданному проценту риска от эквити счета (Risk % per trade)
0 1 2 3 4 5 6 7 8 9 10 11 |
#property library #property copyright "Copyright (c) DaVinci FX Group" #property link "https://www.davinci-fx.com/" #property version "1.00" #property strict enum MM_TYPE { MM_FIXED_LOT = 1, //Fixed lot MM_BY_BALANCE = 2, //By balance MM_BY_FREE_MARGIN =3, //By free margin MM_AUTO = 4 //Risk % per trade }; |
0 1 2 |
extern MM_TYPE LotType = MM_FIXED_LOT; extern double Lot = 0.01; extern double AmountDivider = 500; |
0 1 2 3 4 5 6 7 8 9 10 11 |
double CalculateLots(double _StopLoss) { double result = 0.0; double lotStep = MarketInfo(Symbol(),MODE_LOTSTEP); double minLot = MarketInfo(Symbol(),MODE_MINLOT); double maxLot = MarketInfo(Symbol(),MODE_MAXLOT); //...дальнейший код return(result); } |
0 1 2 |
if(LotType == MM_FIXED_LOT) { //если лот фиксированный result = NormalizeDouble(Lot,2); } |
0 1 2 3 4 5 |
if(LotType == MM_FIXED_LOT) { //если лот фиксированный result = NormalizeDouble(Lot,2); } else if(LotType == MM_BY_BALANCE) { //расчет по балансу и заданному значению для указанного лота result = NormalizeDouble(MathFloor(AccountBalance() / AmountDivider) * Lot,2); } |
Но что делать, если сейчас на счете открыто уже много ордеров и они оказывают большую нагрузку на маржу? В таком случае нам не целесообразно выставлять риск по балансу. В данной ситуации поможет следующий вариант расчета лота по свободной марже. Формула расчета идентичная предыдущей с единственным отличием — мы используем значение функции AccountFreeMargin().
0 1 2 3 4 5 6 7 8 |
if(LotType == MM_FIXED_LOT) { //если лот фиксированный result = NormalizeDouble(Lot,2); } else if(LotType == MM_BY_BALANCE) { //расчет по балансу и заданному значению для указанного лота result = NormalizeDouble(MathFloor(AccountBalance() / AmountDivider) * Lot,2); } else if(LotType == MM_BY_FREE_MARGIN) { //расчет по свободной марже и заданному значению для указанного лота result = NormalizeDouble(MathFloor(AccountFreeMargin() / AmountDivider) * Lot,2); } |
Четвертый метод расчета является самым муторным, но наиболее предпочтительным для наших советников. Нам необходимо произвести расчет лота по указанному в настройках риска на сделку и значению Стоп Лосса. Сделав корректно этот расчет мы будем четко представлять каким процентом от депозита мы готовы рисковать.
Данный расчет происходит по следующей формуле:
(Эквити счета * Процент риска) / Стоп Лосс / Цена пункта
Начнем с определения цены пункта. Для каждой минорной валютной пары оно свое. К примеру у пар с долларом размер изменения цены всегда равен единице. Высчитать это значение можно, воспользовавшись идентификатором запроса MODE_TICKVALUE.
Так как на реальном счете я много раз сталкивался с ситуацией, что брокер не выдавал корректно значение цены пункта, мы подстрахуемся и используем оператор цикла Do While. В нем зададим 10 попыток на определение значения TickValue и в случае неудачи вернем минимальный лот, с которым брокер разрешает открывать сделку.
Не забудьте скачать наши скрипты и индикаторы.
В расчете торгового лота целесообразно использовать значение эквити, а не баланса, поскольку в момент подсчета просадка может быть внушительной. Но при реальной торговле также может возникать ситуация, когда суммарный профит открытых сделок положительный и эквити, соответственно больше, чем баланс счета. В связи с этим мы добавим проверку на минимальное из двух значений баланса и эквити.
Не стоит забывать, что если вы используете бонус брокера, который начисляется в отдельной строке Bonus, то этот фантик нельзя использовать в нашем расчете, поскольку брокер может его вычесть в любой, подходящий для него момент и увеличить нашу просадку. Из полученного значения баланса мы вычитаем значение функции AccountCredit().
Получается следующая конструкция:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
else if(LotType == MM_AUTO) { //расчет по риску на сделку double TickValue = 0; int a=0; do { //узнаем значение цены пункта в цикле из 10 попыток TickValue = MarketInfo(Symbol(), MODE_TICKVALUE); a++; if (TickValue == 0) Sleep(1000); } while (TickValue == 0 && a < 10); if(TickValue == 0) { //если не удалось получить значение цены пункта Print(__FUNCTION__ +": TickValue Error"); return(minLot); //возвращаем минимальный лот } double Balance = (AccountEquity() > AccountBalance() ? AccountBalance() : AccountEquity()); result = ((Balance - AccountCredit()) * (AmountDivider / 100)) / _StopLoss / TickValue; } |
На этом подсчет значения лота практически закончен. Неважно, какой из методов расчета мы использовали, нам все равно нужно округлить вниз параметр result до 2 знаков после запятой, используя значение lotStep, а также сделать проверку, что полученный лот был не выше/ниже максимально/минимально возможного у данного брокера.
0 1 |
if (lotStep != 0) result = MathFloor(result/lotStep)* lotStep; //округление полученного лота вниз result = MathMin(MathMax(result, minLot), maxLot); //сравнение полученного лота с минимальным/максимальным. |
В итоге вся эта конструкция будет иметь вид:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
double CalculateLots(double _StopLoss) { double result = 0.0; double lotStep = MarketInfo(Symbol(),MODE_LOTSTEP); double minLot = MarketInfo(Symbol(),MODE_MINLOT); double maxLot = MarketInfo(Symbol(),MODE_MAXLOT); if(LotType == MM_FIXED_LOT) { //если лот фиксированный result = NormalizeDouble(Lot,2); } else if(LotType == MM_BY_BALANCE) { //расчет по балансу и заданному значению для указанного лота result = NormalizeDouble(MathFloor(AccountBalance() / AmountDivider) * Lot,2); } else if(LotType == MM_BY_FREE_MARGIN) { //расчет по свободной марже и заданному значению для указанного лота result = NormalizeDouble(MathFloor(AccountFreeMargin() / AmountDivider) * Lot,2); } else if(LotType == MM_AUTO) { //расчет по риску на сделку double TickValue = 0; int a=0; do { //узнаем значение цены пункта в цикле из 10 попыток TickValue = MarketInfo(Symbol(), MODE_TICKVALUE); a++; if (TickValue == 0) Sleep(1000); } while (TickValue == 0 && a < 10); if(TickValue == 0) { //если не удалось получить значение цены пункта Print(__FUNCTION__ +": TickValue Error"); return(MarketInfo(Symbol(), MODE_MINLOT)); } double Balance = (AccountEquity() > AccountBalance() ? AccountBalance() : AccountEquity()); result = ((Balance - AccountCredit()) * (AmountDivider / 100)) / _StopLoss / TickValue; } if (lotStep != 0) result = MathFloor(result/lotStep)* lotStep; //округление полученного лота вниз result = MathMin(MathMax(result, minLot), maxLot); //сравнение полученного лота с минимальным/максимальным. return(result); } |
Добавление библиотеки в код советника
Естественно в данном примере мы не будем писать советник с нуля. Мы возьмем готовый код из урока 3.1 Создаем советник, торгующий по сигналу двух скользящих средних. Нам необходимо прикрепить данную библиотеку к советнику, для этого удаляем в его коде переменную Lot (ведь она уже есть в библиотеке) и вместо нее вставляем импорт через препроцессор #include.
0 1 |
extern string s0 = "<== General Settings ==>"; //> > > #include <..\Libraries\DaVinci AutoMM.mq4> //библиотека для расчета АвтоММ |
Переходим к функции OpenTrade(). В нее нужно добавить всего одну строчку, которая будет обращаться к пользовательской функции библиотеки CalculateLots() и передать ей значение Стоп Лосса советника в пунктах. Также заменить переменную Lot на Lots в функции OrderSend().
0 1 2 3 4 5 6 7 8 9 |
bool OpenTrade(int OP_Type) { //функция для открытия рыночного ордера double price = (OP_Type == OP_BUY ? Ask : Bid); //определение цены для открытия рыночного ордера color col_type = (OP_Type == OP_BUY ? clrBlue : clrRed); //определение цвета стрелки ордера string op_str = (OP_Type == OP_BUY ? "на покупку" : "на продажу"); //определение текста для принта double Lots = CalculateLots(StopLoss); int ticket = OrderSend(Symbol(), OP_Type, Lots, price, Slippage, 0, 0, Comments, MagicNumber, 0, col_type); ... |
Всего несколько движений и наш советник уже умеет автоматически рассчитывать торговый лот.
Давайте теперь проверим написанный код на корректность. Пропустим открытие с фиксированным лотом и начнем с варианта By balance. Зададим в тестере баланс 3000$, значение AmountDivider = 500$ и Lot = 0.01. Запустим тест:
Мы видим, что первые два ордера открылись с лотом 0.06 (3000/500*0.01), а вот третий ордер уже имеет лот 0.05, потому что перед ним было закрытие в минус и баланс счета уменьшился до 2967,78$. Этого значения уже не хватает на 0.06 лот.
Далее переходим к варианту By free margin. Тут уже используется значение свободной маржи, а не баланса счета. К примеру на скриншоте ниже она равна 465,88$.
Запускаем тестер, все настройки оставляем такими же:
На скриншоте выше видно, что первый ордер открылся с лотом 0.06, а уже второй имеет лот 0.05. Это связано с тем, что свободной маржи стало меньше после выставления первого ордера и ее значения не хватило на открытие с таким же лотом, как у первоначального.
Если же вы поставите в настройках значение Lot = 0.02, то начальный лот уже будет равен 0.12 (3000/500*0,02). Это нужно для более гибкого управления вашим расчетом АвтоММ.
Переходим к определению лота по проценту риска Risk % per trade. Зададим риск LotType = Risk % per trade и AmountDivider = 10%.
После первого же Стоп Лосса мы видим убыток -300$, что как раз является 10% от 3000$.
Исходя из полученных результатов мы делаем вывод, что код нами был написан корректно и он готов для боя на реальном счете.
Заключение
В заключении хотелось бы заметить, что такой небольшой кусок кода является незаменимой основной для любого вашего советника. Эти 65 строчки помогают мне получать более ровную кривую доходности и принести прибыль как нашей команде, так и инвесторам. Надеюсь, расчет АвтоММ будет полезен и вам.
Стоит заметить, что вариант с использованием риска на сделку не подходит для советников, основанных на системе Мартингейл из-за отсутствия Стоп Лосс в них, как такового. Тем не менее, никто не мешает указать значение баланса для минимального лота, используя значения By balance или By free margin. Для нашего советника Night Hawk мы выставляем риск 1500-3000 на 0.01 лот в зависимости от валютной пары.
Не забудьте ознакомиться с нашим разделом статьей на тему Форекс, торговли и всякого такого. Мы действительно стараемся предоставить вам свежий и интересный материал.
На этом все, всем профитов!
[download url=»http://www.davinci-fx.com/wp-content/uploads/2021/06/4.1-AutoMM.rar» title=»Скачать пример урока»]
4 комментария. Оставить новый
… тук-тук! Здесь кто-то есть? А возможно ли Вас попросить добавить эту функцию в готовый советник?
Добрый. За дополнительную плату это возможно. Напишите на почту rever273(собака)gmail.com
Доброго дня, хотел бы узнать будет ли урок посвящён функции WebRequest? Например с пояснением как можно сделать удалённую таблицу с номерами счетов, что бы советник при установке проверял что на данном счету он может работать.
Доброго дня!
Да, урок который будет посвящён функции WebRequest запланирован. Следите за новостями сайта. 😉