
Управление капиталом по Винсу. Реализация в виде модуля Мастера MQL5
Введение
Работая на финансовых рынках, мы постоянно заняты поиском системы, которая приносила бы нам прибыль. При этом, конечно, хотелось бы, чтобы эта система была максимально стабильной и обладала минимальным риском. Чтобы этого достичь, разрабатываются торговые системы, основной упор в которых сделан на поиске оптимальных точек входа/выхода. Создаются технические индикаторы и торговые сигналы, указывающие, когда покупать/продавать. Разработана целая система ценовых моделей (фигур) для технического анализа. Вместе с тем, как показывает в своей работе "Математика управления капиталом" Ральф Винс, размер капитала, который используется для проведения сделок, не менее важен. Для оптимизации прибыли и, что не менее важно, сохранения депозита нужно определиться с размером лота, которым мы торгуем.
Попутно в работе Винса опровергаются расхожие "ложные концепции". Например, к таким концепциям можно отнести известное правило "чем больше риск, тем выше прибыль":
Потенциальная прибыль — линейная функция потенциального риска. Это неверно!
Следующая "ложная концепция" — диверсификация уменьшает убытки. Но и это не так. По Винсу:
Она может это сделать, но только в определенной степени — намного меньшей, чем считает большинство трейдеров.
Основные положения
Для наглядности рассмотрим основные идеи на примерах. Допустим, у нас есть некоторая условная система из двух сделок. Первая сделка выигрывает 50%, а вторая проигрывает 40%. Если мы не реинвестируем прибыль, то выигрываем 10%, а если реинвестируем — та же последовательность сделок дает проигрыш 10%. (P&L=Profit or Loss).
Номер сделки | P&L без реинвестирования | Полный капитал | P&L с реинвестированием | Полный капитал | |
---|---|---|---|---|---|
100 | 100 | ||||
1 | +50 | 150 | +50 | 150 | |
2 | -40 | 110 | -60 | 90 |
При реинвестировании прибыли выигрышная система превратилась в проигрышную. Нетрудно убедиться, что порядок сделок не имеет значения. Из этого примера видно, что при реинвестировании капитала мы не можем действовать точно так же, как и при торговле с фиксированным лотом. Собственно, поиск оптимального размера лота при реинвестировании и составляет основу метода Винса по управлению капиталом.
Пойдем от простого к сложному — начнём с подбрасывания монетки. Пусть в случае выигрыша мы получаем 2 доллара, а в случае проигрыша теряем 1 доллар. Вероятность проигрыша или выигрыша равна 1/2. Допустим, у нас есть 100 долларов. Тогда если мы поставим на кон все 100 долларов, наша потенциальная прибыль будет 200 долларов. Но в случае проигрыша мы потеряем сразу всю сумму и не сможем дальше продолжать игру. При бесконечной игре — а именно такая игра предполагается для оптимизации — мы гарантированно окажемся в проигрыше.
Если бы мы ставили не всю сумму сразу, а какую-то её часть — например, 20 долларов из 100 — то в случае проигрыша у нас оставались бы деньги для продолжения игры. Рассмотрим последовательность возможных сделок при разной доле капитала на одну сделку. Первоначальный капитал везде 100 долларов.
Сделка | P&L при К=0.1 | Капитал | P&L при К=0.2 | Капитал | P&L при К=0.5 | Капитал | P&L при К=0.7 | Капитал | P&L при К=1 | Капитал | ||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
100 | 100 | 100 | 100 | 100 | ||||||||||
+2 | 20 | 120 | 40 | 140 | 100 | 200 | 140 | 240 | 200 | 300 | ||||
-1 | -12 | 108 | -28 | 112 | -100 | 100 | -168 | 72 | -300 | 0 | ||||
+2 | 21.6 | 129.6 | 44.8 | 156.8 | 100 | 200 | 100.8 | 172.8 | 0 | 0 | ||||
-1 | -12.96 | 116.64 | -31.36 | 125.44 | -100 | 100 | -120.96 | 51.84 | 0 | 0 | ||||
+2 | 23.33 | 139.97 | 50.18 | 175.62 | 100 | 200 | 72.58 | 124.42 | 0 | 0 | ||||
-1 | -14 | 125.97 | -35.12 | 140.5 | -100 | 100 | -87.09 | 37.32 | 0 | 0 | ||||
Итого | 126 | 141 | 100 | 37 | 0 |
Как отмечалось выше, прибыль/убыток не зависит от последовательности сделок. Поэтому вполне корректно, что у нас прибыльные сделки чередуются с убыточными.
Очевидно, что существует некоторый оптимальный коэффициент (делитель) при котором прибыль максимальна. Для простых случаев, когда вероятность выигрыша и отношение прибыль/проигрыш постоянны, этот коэффициент находят по формуле Келли:
f=((B+1)*P-1)/B
f — оптимальная фиксированная доля, которую далее мы и будем искать;
P — вероятность выигрыша;
B — отношение выигрыш/проигрыш.
Далее для удобства f буду называть просто коэффициентом.
Понятно, что на практике размер и вероятность выигрыша постоянно меняются и формула Келли неприменима. Поэтому для эмпирических данных коэффициент f находится численными методами. Оптимизировать будем прибыльность системы по произвольному эмпирическому потоку сделок. Для прибыли по сделке Винс применяет термин HPR (holding period returns, или прибыль за период удержания позиции). Если сделка принесла прибыль 10%, то HPR =1+0.1=1.1. Следовательно, для одной сделки HPR =1+f*Прибыль/(Максимальный возможный проигрыш), где прибыль берётся со знаком плюс или минус, в зависимости от того получен проигрыш или выигрыш. Фактически коэффициент f — это коэффициент максимальной возможной просадки. Чтобы найти оптимальный f, нам надо найти максимум произведения по всем сделкам max(HPR1 * HPR2 * ... *HPRn).
Напишем программу нахождения f для произвольного массива данных.
Программа 1. Поиск оптимального f.
double PL[]={9,18,7,1,10,-5,-3,-17,-7}; // Массив прибылей/убытков из книги double Arr[]={2,-1}; void OnStart() { SearchMaxFactor(Arr); //Или PL и любой другой массив } void SearchMaxFactor(double &arr[]) { double MaxProfit=0,K=0; // Максимальная прибыль // и коэффициент, ей соответствующий for(int i=1;i<=100;i++) { double k,profit,min; min =MathAbs(arr[ArrayMinimum(arr)]); // находим максимальный убыток в массиве k =i*0.01; profit =1; // Находим доходность при заданном коэффициенте for(int j=0;j<ArraySize(arr);j++) { profit =profit*(1+k*arr[j]/min); } // Сравниваем с максимальной доходностью if(profit>MaxProfit) { MaxProfit =profit; K=k; } } Print("Optimal K ",K," Profit ",NormalizeDouble(MaxProfit,2)); }
Можно убедиться, что для случая +2,-1,+2,-1 и т.д. f будет тот же, что и полученный с помощью формулы Келли.
Имейте в виду, что оптимизация имеет смысл только для прибыльных систем — т.е. систем, для которых математическое ожидание (средняя прибыль) положительное. Для убыточных систем оптимальное f=0. Управление размером лота не делает убыточную систему прибыльной. Наоборот, если в потоке нет убытков, т.е. если все P&L>0, оптимизация также не имеет смысла, f=1, и нужно торговать максимальным лотом.
Мы можем, пользуясь графическими возможностями MQL5, найти не только максимальное значение f, но и посмотреть всю кривую распределения прибыли в зависимости от f. Ниже представлена программа, рисующая график прибыли в зависимости от коэффициента f.
Программа 2. График прибыли в зависимости от f.
//+------------------------------------------------------------------+ //| Graphic.mq5 | //| Orangetree | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Orangetree" #property link "https://www.mql5.com" #property version "1.00" #include<Graphics\Graphic.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ //double PL[]={9,18,7,1,10,-5,-3,-17,-7}; // Массив прибылей/убытков из книги double PL[]={2,-1}; void OnStart() { double X[100]={0}; for(int i=1;i<=100;i++) X[i-1]=i*0.01; double Y[100]; double min =PL[ArrayMinimum(PL)]; if(min>=0){Comment("f=1");return;} min =MathAbs(min); int n = ArraySize(X); double maxX[1]= {0}; double maxY[1] ={0}; for(int j=0;j<n;j++) { double k =X[j]; double profit =1; for(int i=0;i<ArraySize(PL);i++) { profit =profit*(1+k*PL[i]/min); } Y[j] =profit; if(maxY[0]<profit) { maxY[0] =profit; maxX[0] =k; } } CGraphic Graphic; Graphic.Create(0,"Graphic",0,30,30,630,330); CCurve *Curve=Graphic.CurveAdd(X,Y,ColorToARGB(clrBlue,255),CURVE_LINES,"Profit"); Curve.LinesStyle(STYLE_DOT); //При желании график можно сглаживать /*Curve.LinesSmooth(true); Curve.LinesSmoothTension(0.8); Curve.LinesSmoothStep(0.2);*/CCurve *MAX =Graphic.CurveAdd(maxX,maxY,ColorToARGB(clrBlue,255),CURVE_POINTS,"Maximum"); MAX.PointsSize(8); MAX.PointsFill(true); MAX.PointsColor(ColorToARGB(clrRed,255)); Graphic.XAxis().MaxLabels(100); Graphic.TextAdd(30,30,"Text",255); Graphic.CurvePlotAll(); Graphic.Update(); Print("Max factor f = ", maxX[0]); }
График для {+2,-1} имеет вид:
Из полученного графика видно, что правило "чем больше риск, тем больше прибыль" неверно. Во всех случаях, когда кривая лежит ниже 1 (f>0.5), мы в конечном итоге получим убыток, а при бесконечной игре — 0 на счету.
Здесь есть одно интересное противоречие. Чем выше математическое ожидание прибыли и стабильнее система, тем больше коэффициент f. Например, для потока {-1,1,1,1,1,1,1,1,1,1} коэффициент равен 0.8, и о такой системе, казалось бы, можно только мечтать. Но коэффициент 0.8 означает,что максимальный допустимый убыток равен 80%, и в один прекрасный день вы потеряете 80% счёта! Да, с точки зрения математической статистики это оптимальный размер лота для максимального увеличения баланса, но готовы ли вы к таким потерям?
Немного о диверсификации
Допустим, у нас есть две торговые стратегии А и Б с одинаковым распределением прибылей/убытков, например те же (+2,-1). Для этих систем оптимальное f равно 0.25. Рассмотрим случаи, когда системы имеют корреляцию 1,0 и -1. Баланс счёта будем просто делить поровну между этими системами.
Корреляция 1, f=0.25
Система А | Сделка P&L | Система Б | Сделка P&L | Комбинированный счёт | ||
---|---|---|---|---|---|---|
50 | 50 | 100 | ||||
2 | 25 | 2 | 25 | 150 | ||
-1 | -18.75 | -1 | -18.75 | 112.5 | ||
2 | 28.13 | 2 | 28.13 | 168.75 | ||
-1 | -21.09 | -1 | -21.09 | 126.56 | ||
прибыль 26.56 |
Как и можно было ожидать, этот вариант ничем не отличается от случая торговли всем капиталом по одной стратегии. Теперь возьмём случай, когда корреляция равна 0.
Корреляция 0, f=0.25
Система А | Сделка P&L | Система Б | Сделка P&L | Комбинированный счёт |
---|---|---|---|---|
50 | 50 | 100 | ||
2 | 25 | 2 | 25 | 150 |
2 | 37.5 | -1 | -18.75 | 168.75 |
-1 | -21.1 | 2 | 42.19 | 189.85 |
-1 | -23.73 | -1 | -23.73 | 142.39 |
Прибыль 42.39 |
Прибыль значительно больше. И, наконец, случай корреляции -1.
Корреляция -1, f=0.25
Система А | Сделка P&L | Система Б | Сделка P&L | Комбинированный счёт |
---|---|---|---|---|
50 | 50 | 100 | ||
2 | 25 | -1 | -12.5 | 112.5 |
-1 | -14.08 | 2 | 28.12 | 126.56 |
2 | 31.64 | -1 | -15 | 142.38 |
-1 | 17.8 | 2 | 35.59 | 160.18 |
Прибыль 60.18 |
В этом случае прибыль максимальна. На этих и подобных примерах можно увидеть, что в случае реинвестирования прибыли диверсификация даёт лучшие результаты. Но также легко заметить, что она не избавляет от наихудшего случая (в нашем примере наибольший убыток f=0.25 от размера баланса), за исключением варианта, когда системы имеют корреляцию -1. На практике таких систем с корреляцией ровно -1 не бывает. Это аналогично случаю открытия позиций по одному инструменту в разные стороны. На основании подобных рассуждений Винс приходит к следующему выводу. Приведу дословную цитату из книги.
Мораль такова: диверсификация, если она произведена правильно, является методом, который повышает прибыли. Она не обязательно уменьшает проигрыши худшего случая, что абсолютно противоречит популярному представлению.
Корреляция и другая статистика
Прежде чем перейти к параметрическим методам нахождения коэффициента f, рассмотрим еще несколько характеристик потока прибылей. Может случиться так, что мы получаем серию взаимосвязанных результатов. За прибыльными сделками следуют прибыльные, а за убыточными — убыточные. Для выявления подобных зависимостей рассмотрим два метода: нахождения автокорреляции ряда и серийный тест.
Серийный тест заключается в вычислении показателя, который называется счёт Z. По содержанию счёт Z — это число стандартных отклонений, на которое данные отстоят от среднего значения нормального распределения. Отрицательное значение Z говорит о том, что полос (непрерывных рядов прибыли/убытков) меньше, чем в нормальном распределении, а значит, за прибылью вероятнее следует убыток и наоборот. Формула для счёта Z:
Z=(N(R-0.5)-Х)/((Х(Х-N))/(N-1))^(1/2)
или
где:
- N — общее число сделок;
- R — общее число серий;
- X=2*W*L, где
- W = общее число выигрышных сделок в последовательности;
- L = общее число проигрышных сделок в последовательности.
Программа 3. Счёт Z.
double Z(double &arr[]) { int n =ArraySize(arr); int W,L,X,R=1; if(arr[0]>0) { W=1; L=0; } else { W=0; L=1; } for(int i=1;i<n;i++) { if(arr[i]>0) { W++; if(arr[i-1]<=0){R++;} } else { L++; if(arr[i-1]>0){R++;} } } X =2*W*L; double x=(n*(R-0.5)-X); double y =X*(X-n); y=y/(n-1); double Z=(n*(R-0.5)-X)/pow(y,0.5); Print(Z); return Z; }
Счет Z рассчитывается Тестером Стратегий, где в отчете (Бэктест) он так и называется "Z-Счёт".
Автокорреляция — статическая взаимосвязь между последовательностями величин одного ряда, взятыми со сдвигом. Для ряда {1,2,3,4,5,6,7,8,9,10}, это корреляция между рядами {1,2,3,4,5,6,7,8,9} и {2,3,4,5,6,7,8,9,10}. Ниже рассмотрена программа для нахождения автокорреляции.
Программа 4. Автокорреляция.
double AutoCorr(double &arr[]) { int n =ArraySize(arr); double avr0 =0; for(int i=0;i<n-1;i++) { avr0=avr0+arr[i]; } avr0=avr0/(n-1); double avr1 =0; for(int i=1;i<n;i++) { avr1=avr1+arr[i]; } avr1=avr1/(n-1); double D0 =0; double sum =0.0; for(int i=0;i<n-1;i++) { sum =sum+(arr[i]-avr0)*(arr[i]-avr0); } D0 =MathSqrt(sum); double D1 =0; sum =0.0; for(int i=1;i<n;i++) { sum =sum+(arr[i]-avr1)*(arr[i]-avr1); } D1 =MathSqrt(sum); sum =0.0; for(int i=0;i<n-1;i++) { sum =sum +(arr[i]-avr0)*(arr[i+1]-avr1); } if(D0==0||D1==0) return 1; double k=sum/(D0*D1); return k; }
Если результаты сделок взаимосвязаны, то торговую стратегию имеет смысл скорректировать. Лучшие результаты получатся, если мы будем использовать два разных коэффициента — f1 и f2 — для прибылей и убытков. Для этого случая будет написан отдельный модуль управления капиталом на MQL5.
Параметрические методы
Оптимизируя параметры системы, мы можем использовать два подхода. Первый — эмпирический, основанный непосредственно на экспериментальных данных, когда мы оптимизируем параметры под конкретные результаты. И второй — параметрический, основанный на функциональных или статических зависимостях. Пример параметрического метода — нахождение оптимального коэффициента из формулы Келли.
Для нахождения оптимального коэффициента Винс предлагает использовать распределения полученных прибылей. Сначала он рассматривает нормальное распределение, как наиболее изученное и распространенное, затем конструирует обобщенное распределение.
Задача формулируется так. Допустим, наши прибыли/убытки распределены в соответствии с нормальным (или — в общем случае — с любым другим) распределением. Найдем оптимальный коэффициент f для этого распределения. В случае нормального распределения нам из экспериментальных данных достаточно найти среднее значение потока PL(profit/loss) и стандартное отклонение. Эти два параметра полностью характеризуют нормальное распределение.
Напомню формулу плотности нормального распределения:
где
- σ — стандартное отклонение
- m — математическое ожидание (среднее значение).
Сама идея мне понравилась. С помощью эмпирических данных найти характер распределения прибылей/убытков. И уже по этой функции, к которой стремятся результаты, находить параметр f, тем самым избегая влияния случайных значений. К сожалению, на практике всё не так просто. Но всё по порядку. Сначала о самом методе.
На графике синим цветом построен график плотности нормального распределения, где среднее значение равно нулю, а стандартное отклонение — единице. Красным цветом отображается интеграл этой функции. Это кумулятивная вероятность, то есть вероятность того, что значение меньше или равно заданному Х. Её принято обозначать F(x). Оранжевый график — вероятность того, что значение меньше или равно х при х<0 и что значение больше или равно х при х>0 (F(x)' =1-F(x), при х>0. Все эти функции хорошо известны и их значения легко получить.
Нам требуется найти наибольшее среднее геометрическое сделок, распределённых по этому закону. Для этого Винс предлагает следующие действия.
Сначала находим характеристики распределения — среднее значение и стандартное отклонение. Затем выбираем "доверительный интервал" или ширину отсечения, который выражается в стандартных отклонениях. Обычно выбирают интервал 3σ. Значения больше 3σ отсекаются. Затем данный интервал разбивается на отрезки и находятся "ассоциированные значения" прибылей/убытков (PL). Например, для σ=1 и m=0 значение ассоциированных PL на краях интервала будут m +- 3σ = +3 и -3. Если мы разбили интервал на отрезки длиной 0.1σ, то ассоциированные PL будут -3, -2.9, -2.8 ... 0 ... 2.8, 2,9, 3. И именно для этого потока PL мы находим оптимальное f.
Так как различные значения PL имеют различную вероятность, то для каждого значения находится его "ассоциированная вероятность" P. После этих преобразований ищется максимум произведений:
HPR=(1+PL*f/maxLoss)^P, где maxLoss — максимальный убыток (по модулю).
Здесь Винс предлагает в качестве ассоциированной вероятности брать кумулятивную вероятность, которая у нас на графике показана оранжевым цветом F'(x).
Было бы логично, если бы кумулятивная вероятность бралась только для крайних значений, а для остальных значений P=F'(x)-F'(y), где х и у — значения F(x) на краях интервала.
Тогда множитель HPR=(1+PL*f/maxLoss)^P был бы своеобразным "взвешенным значением". Суммарная вероятность этих значений, как и положено, была бы равна единице. В книге Винс признаёт, что полученные таким образом результаты не совпадают с результатами, полученными на фактических данных. Он относит это к ограниченности выборки и отличию фактического распределения от нормального. Утверждается, что при увеличении числа элементов и распределении их по нормальному закону параметрические и фактические значения оптимального коэффициента f будут сходиться.
Интересно, что в разобранном по его методу примере суммарная вероятность оказывается равной 7.9. Ччтобы найти среднее геометрическое, он просто извлекает из результата корень 7.9-ой степени. По всей видимости, для такого подхода есть строгое математическое обоснование.
Мы же, имея в своём распоряжении такой инструмент, как MQL5, можем всё это легко проверить. Для этого у нас есть библиотека Normal.mqh, которая находится по адресу <Math\Stat\Normal.mqh>.
Для экспериментов я сделал два варианта: в точности как у Винса и описанный выше. Для нахождения "ассоциированных вероятностей" используется библиотечная функция MathCumulativeDistributionNormal(PL,mean,stand,ProbCum).
Программа 5. Поиск оптимального f по нормальному распределению (по Винсу).
//+------------------------------------------------------------------+ //| Vince.mq5 | //| Copyright 2017, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2017, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #include<Math\Stat\Math.mqh> #include<Math\Stat\Normal.mqh> input double N=3; // интервал отсечения в стандартных отклонениях input int M=60; // количество отрезков разбиения //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { double arr[10000]; bool ch =MathRandomNormal(1,8,10000,arr); double mean =MathMean(arr); double stand =MathStandardDeviation(arr); double PL[]; // массив "ассоциированных прибылей" ArrayResize(PL,M+1); // Заполнение массива for(int i=0;i<M+1;i++) { double nn =-N+2.0*i*N/M; PL[i] =stand*nn+mean; } //............................. массив "ассоциированных вероятностей" double ProbCum[]; ArrayResize(ProbCum,M+1); //............................. Заполнение массива .................. ch =MathCumulativeDistributionNormal(PL,mean,stand,ProbCum); //F'(x)= 1-F(x) при х>0 for(int i=0,j=0;i<M+1;i++) { if(i<=M/2)continue; else j=M-i; ProbCum[i] =ProbCum[j]; } double SumProb=0; for(int i=0;i<M+1;i++) { SumProb =SumProb+ProbCum[i]; } Print("SumProb ",SumProb); double MinPL =PL[ArrayMinimum(PL)]; double min =arr[ArrayMinimum(arr)]; double f=0.01,HPR=1,profit=1; double MaxProfit=1,MaxF=0; for(int k=0;k<1000;k++) { f=k*0.001; profit =1; for(int i=0;i<M+1;i++) { HPR=pow((1-PL[i]/MinPL*f),ProbCum[i]); profit =HPR*profit; } if(MaxProfit<profit) { MaxF =f; MaxProfit =profit; } } Print("Profit Vince"); Print(MaxF," ",pow(MaxProfit,1/SumProb)," ",Profit(MaxF,min,arr)); //... Для сравнения найдем максимальную прибыль по фактическим данным MaxF =0; MaxProfit =1; for(int k=0;k<1000;k++) { f=k*0.001; profit =Profit(f,min,arr); if(MaxProfit<profit) { MaxF =f; MaxProfit =profit; } } Print("------MaxProfit-------"); Print(MaxF," ",MaxProfit); } // Программа нахождения прибыли по фактическим данным // массива arr[] с минимальным значением min // и заданным значением f double Profit(double f,double min, double &arr[]) { if(min>=0) { return 1.0; Alert("min>=0"); } double profit =1; int n =ArraySize(arr); for(int i=0;i<n;i++) { profit =profit*(1-arr[i]*f/min); } return profit; }
Код программы находится в файле Vince.mq5
В этой программе находится коэффициент из нормального распределения и затем — для сравнения — по фактическим данным. Второй вариант отличается только массивом "ассоциированных" вероятностей и PL.
Программа 6.
............................................. double ProbDiff[]; ArrayResize(ProbDiff,M+2); double PLMean[]; ArrayResize(PLMean,M+2); ProbDiff[0]=ProbCum[0]; ProbDiff[M+1]=ProbCum[M]; PLMean[0]=PL[0]; PLMean[M+1]=PL[M]; for(int i=1;i<M+1;i++) { ProbDiff[i] =MathAbs(ProbCum[i]-ProbCum[i-1]); PLMean[i] =(PL[i]+PL[i-1])/2; } ..............................................
Код программы находится в файле Vince_2.mq5
Здесь PLMean[i] =(PL[i]+PL[i-1])/2; — среднее значение PL на отрезке разбиения, ProbDiff[] — значения вероятности того, что величина находится в заданном интервале. По краям значения отсекаются (возможно стоп-лоссом или тейк-профитом), поэтому вероятность на краях просто равна кумулятивной вероятности.
Обе программы работают примерно одинаково и выдают примерно одинаковые результаты. Оказалось, что ответ сильно зависит от N — ширины отсечения ("доверительного интервала"). Причём, что самое печальное, при увеличении N коэффициент f, полученный из нормального распределения, стремится к 1. Теоретически, чем шире интервал "отсечения", тем точнее должен получиться результат. На практике это не так.
Так может получаться из-за накапливающейся ошибки. Экспоненциальная функция довольно быстро убывает, и нам приходится иметь дело с довольно малыми величинами — HPR=pow((1-PL[i]/MinPL*f),ProbCum[i]). Возможно, что и сама методика где-то содержит ошибку. Но для практического применения это не принципиально. В любом случае, для корректной работы нам нужно каким-то образом "подгонять" параметр N, который сильно влияет на результат.
Понятно, что реальный поток PL отличается от нормального распределения. В связи с этим Винс создаёт обобщенное распределение с параметрами, имитирующими характеристики любого произвольного распределения. Добавляются параметры, задающие разные моменты распределения (среднее значение, эксцесс, ширину, асимметрию). Затем предполагается с помощью численных методов найти эти параметры для эмпирических данных и построить функцию распределения потока PL.
Так как результаты экспериментов с нормальным распределением мне не понравились, численные расчёты с обобщенным распределением я решил не проводить. Приведу еще аргумент в пользу своих сомнений.
Винс утверждает, что параметрические методы намного мощнее. Ведь с увеличением числа экспериментов данные будут стремиться к теоретическим результатам, так как коэффициент, полученный по выборке, неточен вследствие ограниченности выборки. Но параметры в случае с нормальным распределением (среднее значение и стандартное отклонение) мы получаем именно из этой ограниченной выборки. Неточность расчёта характеристик распределения точно такая же. Затем эта неточность только увеличивается из-за накапливающейся ошибки в увеличивающихся вычислениях. При этом, как выясняется, результаты в практической реализации ещё и зависят от ширины отсечения. Так как на практике распределение не является нормальным, мы добавляем ещё одно звено — поиск функции распределения, опять же основанный на тех же самых эмпирических конечных данных. Дополнительное звено влечет за собой дополнительную погрешность вычислений.
Позволю высказать своё скромное мнение. Параметрический подход представляет собой иллюстрацию того, что красивые в теории идеи не всегда так же красивы и на практике.
Краткий обзор книги Винса
Модуль мастера MQL5
В целом реализация модуля сходна с уже имеющемся стандартным модулем MoneyFixedRisk. Там размер лота находится через задаваемый стоп-лосс. Для наглядности оставим стоп-лосс независимым и зададим коэффициент f и максимальный убыток в явном виде через входные параметры.
Для начала создадим в директории Include/Expert новую папку для своих модулей — например, MyMoney. В ней создадим файл MoneyF1.mql.
Все торговые модули состоят из набора стандартных частей: класса торгового модуля и его специального описания (дескриптора класса).
Класс, как правило, содержит:
- конструктор;
- деструктор;
- функции установки входных параметров;
- функцию проверки введённых значений параметров ValidationSettings(void);
- методы определения объема позиции CheckOpenLong(double price,double sl) и CheckOpenShort(double price,double sl).
Назовем наш класс CMoneyFactor
class CMoneyFactor : public CExpertMoney { protected: //--- input parameters double m_factor; // коэффициент максимального проигрыша f double m_max_loss; // максимальный убыток в пунктах public: CMoneyFactor(void); ~CMoneyFactor(void); //--- void Factor(double f) { m_factor=f;} void MaxLoss(double point) { m_max_loss=point;} virtual bool ValidationSettings(void); //--- virtual double CheckOpenLong(double price,double sl); virtual double CheckOpenShort(double price,double sl); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ void CMoneyFactor::CMoneyFactor(void) : m_factor(0.1), m_max_loss(100) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ void CMoneyFactor::~CMoneyFactor(void) { }
Максимальный проигрыш в пунктах задан типом double для соответствия стандартным модулям. Связано это с тем, что в других модулях, поставляемых вместе с дистрибутивом, стоп-лосс и тейк-профит задаются в пунктах определяемыми в базовом классе ExpertBase.mqh
ExpertBase.mqh int digits_adjust=(m_symbol.Digits()==3 || m_symbol.Digits()==5) ? 10 : 1; m_adjusted_point=m_symbol.Point()*digits_adjust;
То есть для котировок с пятью и тремя знаками после запятой один пункт равен 10*Point(). 105 пунктов в смысле Point() равны 10.5 пунктам в стандартных модулях для MQL5.
Функции Factor(double f) и MaxLoss(double point) устанавливают входные параметры и должны называться так же, как затем будут описаны в дескрипторе модуля.
Функция проверки корректности введённых параметров:
bool CMoneyFactor::ValidationSettings(void) { if(!CExpertMoney::ValidationSettings()) return(false); //--- initial data checks if(m_factor<0||m_factor>1) { Print(__FUNCTION__+"Размер коэффициента должен быть в пределах от 0 до 1"); return false; } return true; }
Здесь мы проверяем, чтобы значение коэффициента было от 0 до 1.
И наконец, сами функции определения объёма позиции. Для открытия в "лонг":
double CMoneyFactor::CheckOpenLong(double price,double sl) { if(m_symbol==NULL) return(0.0); //--- Определение размера лота double lot; /* ExpertBase.mqh int digits_adjust=(m_symbol.Digits()==3 || m_symbol.Digits()==5) ? 10 : 1; m_adjusted_point=m_symbol.Point()*digits_adjust; */ double loss; if(price==0.0)price =m_symbol.Ask(); loss=-m_account.OrderProfitCheck(m_symbol.Name(),ORDER_TYPE_BUY,1.0,price,price - m_max_loss*m_adjusted_point); double stepvol=m_symbol.LotsStep(); lot=MathFloor(m_account.Balance()*m_factor/loss/stepvol)*stepvol; double minvol=m_symbol.LotsMin(); //---проверка минимального лота if(lot<minvol) lot=minvol; //---проверка максимального лота double maxvol=m_symbol.LotsMax(); if(lot>maxvol) lot=maxvol; //--- return trading volume return(lot); }
Здесь максимальный убыток находится с помощью библиотечного метода класса CAccountInf - OrderProfitCheck(). Затем добавлена проверка лота на его соответствия разрешенным предельным значениям — минимальному и максимальному.
В начале каждого модуля идёт его описание (дескриптор), необходимое компилятору для его распознавания.
// wizard description start //+------------------------------------------------------------------+ //| Description of the class | //| Title=Торговля с оптимальным коэффициентом f | //| Type=Money | //| Name=FixedPart | //| Class=CMoneyFactor | //| Page= ? | //| Parameter=Factor,double,0.1,Оптимальная фиксированная доля | //| Parameter=MaxLoss,double,50,Максимальный убыток в пунктах | //+------------------------------------------------------------------+ // wizard description end
Для экспериментов этот модуль можно скомпилировать с любым имеющимся модулем торговых сигналов. Предварительно можно выбранный модуль торговых сигналов скомпилировать с модулем управления капиталом с фиксированным лотом. Полученные результаты используем для нахождения максимального убытка и потока PL. Потом по этим результатам найдем оптимальный фактор f с помощь Программы 1. Таким образом по экспериментальным данным можно найти оптимальный f. Другой способ — найти оптимальный f непосредственно из полученного советника на основе нашего модуля путём его оптимизации. У меня результаты расходятся лишь на +/- 0.01. Это связано с погрешностью вычислений, например, из-за округлений.
Код модуля находится в файле MoneyF1.mqh.
Может случиться так, что поток наших прибылей/убытков имеет значимую автокорреляцию. Это можно выяснить с помощью приведённых ранее программ расчёта "счёта Z" и автокорреляции. Тогда имеет смысл задать два коэффициента — f1 и f2. Первый применяется после прибыльных сделок, второй — после убыточных. Напишем для данной стратегии второй модуль управления капиталом. Коэффициенты затем можно находить с помощью оптимизации, а можно — непосредственно по данным потока прибылей/убытков для той же стратегии с фиксированным лотом.
Программа 7. Определения оптимальных f1 и f2 по потоку прибыли/убытка.
void OptimumF1F2(double &arr[]) { double f1,f2; double profit=1; double MaxProfit =0; double MaxF1 =0,MaxF2 =0; double min =MathAbs(arr[ArrayMinimum(arr)]); for(int i=1;i<=100;i++) { f1 =i*0.01; for(int j=1;j<=100;i++) { f2 =j*0.01; profit =profit*(1+f1*arr[0]/min); for(int n=1;n<ArraySize(arr);n++) { if(arr[n-1]>0){profit =profit*(1+f1*arr[n]/min);} else{profit =profit*(1+f2*arr[n]/min);} } if(MaxProfit<profit) { MaxProfit=profit; MaxF1 =i;MaxF2 =j; } } }
Соответственно, для мастера MQL5 нужно переделать основные функции модуля управления капиталом. Во-первых, добавим еще один параметр — f2 — и его проверку. Во вторых, переделаем функции CheckOpenLong() и CheckOpenShort(). Для определения финансового результата предыдущей сделки добавим функцию CheckLoss().
//+------------------------------------------------------------------+ //| Проверяет результат предыдущей сделки | //+------------------------------------------------------------------+ double CMoneyTwoFact:: CheckLoss() { double lot=0.0; HistorySelect(0,TimeCurrent()); int deals=HistoryDealsTotal(); // количество сделок в истории CDealInfo deal; //--- поиск предущей сделки if(deals==1) return 1; for(int i=deals-1;i>=0;i--) { if(!deal.SelectByIndex(i)) { printf(__FUNCTION__+": ошибка выбора сделки по индексу"); break; } //--- отбор сделок по символу или любой другой if(deal.Symbol()!=m_symbol.Name()) continue; //--- возвращаем результат сделки lot=deal.Profit(); break; } return(lot); }
Функции CheckOpenLong() CheckOpenShort():
double CMoneyTwoFact::CheckOpenLong(double price,double sl) { double lot=0.0; double p=CheckLoss(); /* ExpertBase.mqh int digits_adjust=(m_symbol.Digits()==3 || m_symbol.Digits()==5) ? 10 : 1; m_adjusted_point=m_symbol.Point()*digits_adjust; */ double loss; if(price==0.0)price =m_symbol.Ask(); if(p>0) { loss=-m_account.OrderProfitCheck(m_symbol.Name(),ORDER_TYPE_BUY,1.0,price,price - m_max_loss*m_adjusted_point); double stepvol=m_symbol.LotsStep(); lot=MathFloor(m_account.Balance()*m_factor1/loss/stepvol)*stepvol; } if(p<0) { loss=-m_account.OrderProfitCheck(m_symbol.Name(),ORDER_TYPE_BUY,1.0,price,price - m_max_loss*m_adjusted_point); double stepvol=m_symbol.LotsStep(); lot=MathFloor(m_account.Balance()*m_factor2/loss/stepvol)*stepvol; } return(lot); } //+------------------------------------------------------------------+ double CMoneyTwoFact::CheckOpenShort(double price,double sl) { double lot=0.0; double p=CheckLoss(); /* int digits_adjust=(m_symbol.Digits()==3 || m_symbol.Digits()==5) ? 10 : 1; m_adjusted_point=m_symbol.Point()*digits_adjust;*/ double loss; if(price==0.0)price =m_symbol.Ask(); if(p>0) { loss=-m_account.OrderProfitCheck(m_symbol.Name(),ORDER_TYPE_SELL,1.0,price,price+m_max_loss*m_adjusted_point); double stepvol=m_symbol.LotsStep(); lot=MathFloor(m_account.Balance()*m_factor1/loss/stepvol)*stepvol; } if(p<0) { loss=-m_account.OrderProfitCheck(m_symbol.Name(),ORDER_TYPE_SELL,1.0,price,price+m_max_loss*m_adjusted_point); double stepvol=m_symbol.LotsStep(); lot=MathFloor(m_account.Balance()*m_factor2/loss/stepvol)*stepvol; } return(lot); }
Полный код модуля находится в файле MoneyF1F2.mqh.
Как упоминалось выше, в целом концепция управления капиталом по Винсу вертится вокруг оптимального коэффициента f. Поэтому для примера двумя модулями вполне можно ограничиться. Хотя можно изобрести и какие-либо дополнительные вариации. Например, добавить элементы мартингейла.
Прикреплённые файлы
В файле Programs.mq5 находятся коды программ, использованных в статье. Добавлена сюда и программа для чтения данных из файла void ReadFile(string file,double &arr[]). Она нужна для нахождения коэффициентов f по потоку прибылей/убытков из тестера стратегий. Можно, конечно, подойти к делу обстоятельно и написать целый класс для парсинга отчётов, как это сделано в статье "Раскладываем входы по индикаторам". Но это целая отдельная программа со своими классами.
На мой взгляд, проще сделать так. Прогоняем стратегию с фиксированным лотом в тестере стратегий. Сохраняем отчёт тестера в виде Open XML (MS Office Excel). К колонке "прибыль" добавляем "своп" и "комиссия" — получаем поток прибыли PL. Отдельно эту колонку сохраняем в текстовый файл или файл csv. Получаем набор строк, состоящих из отдельных результатов каждой сделки. Эти результаты добавленная функция ReadFile() читает в массив arr[]. Таким несложным путем мы можем найти оптимальный f по данным любой стратегии с фиксированным лотом.
В файлах Vince.mq5 и Vince_2.mq5 находятся исходники параметрических методов нахождения оптимальных коэффициентов, рассмотренные в статье.
Файлы MoneyF1.mqh и MoneyF1F2.mqh исходники торговых модули управления капиталом.
Архивированный файл содержит все эти файлы, структурированные в соответствии с их расположением в MetaEditor.





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Восхищён, невероятно хорошо проделанная работа. Благодарю за труды.
Опубликована статья Управление капиталом по Винсу. Реализация в виде модуля Мастера MQL5:
Автор: Dmitrii Troshin
Спасибо! Хорошая проработка одной из лучших книг!
Основные положения
Для наглядности рассмотрим основные идеи на примерах. Допустим, у нас есть некоторая условная система из двух сделок. Первая сделка выигрывает 50%, а вторая проигрывает 40%. Если мы не реинвестируем прибыль, то выигрываем 10%, а если реинвестируем — та же последовательность сделок дает проигрыш 10%. (P&L=Profit or Loss).
При реинвестировании прибыли выигрышная система превратилась в проигрышную.
Не возможно превратить минусовую систему в плюсовую с помощью ММ. Но верно и обратное, плюсовую систему в минусовую нельзя превратить с помощью ММ .
В этом примере автор не учитывает ещё два варианта:
1. Обе сделки идут в плюс. т.е прибыль равна ( 100*1.5*1.5 - 100 ) = 125.
2. Обе сделки идут в минус т.е прибыль равна ( 100*0,6*0,6-100)=-64.
В общем плюсовая система так и осталось плюсовой.