Практика трейдинга Основные практические аспекты торговли на Форекс.

Ответить
11.11.2011, 14:12
Регистрация: 18.08.2008 / Сообщений: 8,856
Поблагодарили 2,792 раз(а) / Репутация: 2826

По умолчанию Валютный треугольник

Валютный треугольник

Причина ценовых движений на рынке Форекс стара как мир - изменение количества валюты, находящейся во владении покупателей и продавцов, тем более что вчерашний покупатель очень часто становится продавцом, или наоборот - продавец становится покупателем. Здесь работает еще одно старое правило - если в определенной местности количество какого-либо товара пользуется высоким спросом (товара мало, а желающих купить - много), то цена на товар растет, если же спрос невелик, то товар теряет в цене.
Казалось бы, в эпоху глобализации фактор "местности" должен потерять значение, т.к. любой трейдер, находясь в Австралии, может достаточно быстро купить валюту, продающуюся в Европе. То есть проблема дефицита или профицита возникать не должна, но она все равно возникает. Напрашивается простой вывод: часть средств искусственно забирается с рынка, на некоторое время оседая в "карманах" определенного круга неизвестных лиц. В результате изначально закрытая система, состоящая из конечного количества мировых валют, оказывается открытой. Возникает следующий эффект: количество одной валюты на рынке уменьшается, а другой - растет. Хотя в глобальном масштабе (если посчитать осевшие в "карманах" средства) ничего никуда не исчезает. Такая формулировка сродни физическому принципу сохранения энергии: "Энергия не появляется из неоткуда и не исчезает в никуда, она лишь преобразуется из одной формы в другую". Другая аналогия - сообщающиеся сосуды, в которых уровень жидкости всегда стремится к равновесию.
На этом основании, хоть и с большой натяжкой, можно предположить, что количество всех валют мира, если привести их к единому знаменателю, постоянно. Существует лишь перетекание одного вида валюты в другой. Безапелляционно это утверждать нельзя, т.к. денежные эмиссии отдельных стран все же имеют место. Для простоты в дальнейших рассуждениях будем полагать, что количество денежных средств в мире неизменно, а изменение стоимости одной валюты по отношению к другой является отражением факта искусственного вывода какой-то валюты с рынка. То есть, если бы в мире было две валюты, и имелась возможность в любой момент времени посчитать количество каждой из них на рынке, то мы наблюдали бы пропорциональное увеличение количества одной валюты и соответствующее уменьшение количества другой (см. рис. 1).


Рис. 1. Изменение количества валют на рынке.

Рассмотренный механизм перетекания одной валюты в другую справедлив для любого количества валют. Когда валют две, то по соотношению их цен сразу же можно сказать, какой валюты становится меньше, а какой больше. Когда валют больше, чем две, то простого взгляда на цены уже недостаточно. Потребуется скрупулезный анализ, на основании которого производятся подобные выводы.
К примеру, возьмем три распространенные валюты: евро (EUR), доллар США (USD) и японскую йену (JPY). Увидев на графике (см. рис. 2) EURUSD понижение цены, мы сделаем вывод о том, что количество евро на рынке увеличивается (т.к. его цена падает), а количество долларов США - уменьшается (т.к. цена доллара растет). Соответственно, это означает, что в "кармане" стало больше долларов США, а евро - меньше. Если же посмотреть на второй график - EURJPY, то видим обратный процесс - покупают евро за йену. Этим в какой-то степени компенсируется недостаток евро в "кармане" за счет уменьшения количества йены. На третьем графике (USDJPY) подтверждается процесс уменьшения количества йены в "кармане" (увеличение на рынке) и увеличение количества долларов США (уменьшение на рынке).


Рис. 2. Увеличение количества USD в "кармане" за счет уменьшения количества JPY.

Чтобы узнать точно, в достаточной ли мере компенсирована потеря евро из "кармана" при падении EURUSD ростом EURJPY, необходимо соотнести курсы валютных пар EURUSD, EURJPY и USDJPY. Располагая только ценовыми графиками, мы не можем говорить об объемах произведенных сделок (объемы в МТ4 означают количество тиков, а не реальный объем). Поэтому необходимо принять еще одно упрощение: изменение цены на один пункт приравнивается к потере (или получению) одной условной единицы валюты (это может быть что угодно: 1 евро, 10 евро или 100 000 евро). При этом учитываются текущие цены. Например, при изменении цены пары EURUSD с 1.4250 (один евро стоит 1.425 доллара США) до 1.4249, считаем, что в "кармане" количество евро стало на одну единицу меньше, а долларов США - на 1.4249 единицы больше. Если цена уменьшилась еще на один пункт, то количество евро уменьшилось на две единицы, а долларов США - на 1.4249 + 1.4248 = 2.8497 единицы. Аналогично производим расчеты для других валютных пар треугольника валют. Для примера возьмем свечу 2011.08.04 02:00. Начальная цена - это цена открытия свечи, а конечная - цены закрытия. Исходные данные следующие:
1. EURUSD. Цена открытия 1.4353, цена закрытия 1.4310 (уменьшение на 43 пункта).
2. EURJPY. Цена открытия 110.73, цена закрытия 112.08 (увеличение на 135 пунктов).
3. USDJPY. Цена открытия 77.15, цена закрытия 78.33 (увеличение на 118 пунктов).
Мы не располагаем сведениями, какое состояние валют было на момент открытия свечи. Найти начальную точку, при которой началось абсолютное изменение количества валют друг относительно друга, невозможно. Поэтому мы можем лишь рассчитать относительное изменение в валютном раскладе, взяв момент открытия рассматриваемой свечи за точку отсчета, т.е. за 0. Произведя расчеты, мы увидим, какие изменения произошли в "кармане" за один час. Итак, EUR = 0, USD = 0, JPY = 0.
1. EURUSD. EUR = EUR - 43 = 0 - 43 = -43.
USD = USD + Σ(1.4310; 1.4352) = 0 + 63.0586 = 61.6233.
2. EURJPY. EUR = EUR + 135 = -43 + 135 = 92.
JPY = JPY - Σ(110.74; 112.08) = 0 - 15040.35 = -15040.35.
3. USDJPY. USD = USD + 118 = 61.6233 + 118 = 179.6233.
JPY = JPY - Σ(77.16; 78.33) = -15040.35 - 9173.91 = -24214.26
В каждом из трех случаев начальная цена принималась большей или меньшей (в зависимости от направления движения), чем заявленная цена открытия. Связано это с тем, что цену открытия считать не требуется, т.к. нас интересует именно изменение цены. Первое слагаемое при подсчете сумм чисел от цены открытия до цены закрытия - это момент изменения на один пункт. Например, цена EURUSD двигалась от 1.4250 до 1.4248 вниз. То есть произошло уменьшение на 2 пункта. Соответственно, подсчет сумм для валюты USD должен включать только два слагаемых: 1.4249 и 1.4248.
Итогом произведенных подсчетов стал факт: в "кармане" уменьшилось количество йены (ее стало больше на рынке), увеличилось количество евро и долларов США. Чтобы произвести адекватное сравнение произошедших изменений, необходимо привести все полученные результаты к одной системе измерения, т.е. выразить их в стоимости одной из валют. К примеру, выразим все в йенах. Для этого значение EUR нужно разделить на последнюю стоимость евро, выраженную в йенах. Это цена закрытия - 112.08. То есть 92*112.08 = 10311.36. Для USD расчеты такие же: 179.6233*78.33 = 14069.89. Выходит, что йен в "кармане" стало меньше на 60% за счет доллара США и на 40% за счет евро.
Нетрудно заметить, что если сложить йеновые эквиваленты EUR и USD, то не получим значение для йены в абсолютном выражении (10311.36 + 14069.89 = 24381.25). Объяснение здесь простое: котировки валютного треугольника не соответствуют друг другу со 100%-ой точностью. Дело в том, что время возникновения котировки по каждой валютной паре происходит не одновременно. Да и связаны валютные пары не напрямую друг с другом, а через огромное количество других соотношений (EURUSD, USDJPY и EURJPY - не замкнутая система). Для приведения котировок в абсолютное соответствие друг с другом рынку требуется некоторое время, по прошествии которого происходит следующее изменение. И так до бесконечности: рынок пытается обрести равновесие и не успевает. Используя идею инертности рынка, некоторые трейдеры пытаются построить свои системы (арбитраж), но ничего толкового из этого не выходит, т.к. суммарный диссонанс между валютными парами не перекрывает даже размера спреда.
Все вышесказанное можно реализовать в виде индикатора. На данный момент ограничимся расчетами только для треугольника валют, т.к. охват всех доступных комбинаций валютных пар одной валюты (для евро необходимо учесть пары: EURUSD, EURJPY, EURGBP, EURCHF, EURCAD, EURAUD, EURCHF) является достаточно сложным и путанным делом.
Указание индикатору трех необходимых валют производится просто: первые две валюты индикатор определяет автоматически, используя названия валют, которые составляют текущую валютную пару. Третья валюта указывается в настроечном параметре индикатора RaterValute. С их помощью индикатор находит и составляет три доступные валютные пары так, чтобы образовался валютный треугольник. Следующие исходные данные для индикатора - начальное и конечное дата/время, в диапазоне которых производится расчет. Начальному времени соответствует момент, когда все валюты получают нулевое значение (точка отсчета), а по конечному времени производится расчет изменений.
Наиболее удобный способ указания диапазона - графический. Пользователю предоставляется две вертикальные линии для выбора начального и конечного времени. После перемещения одной или обеих линий и прихода нового тика (или нажать "Обновить" в контекстном меню) индикатор принимает дату/время линии, находящейся слева по графику, за начало диапазона, а дату/время второй линии - за его окончание. Цвет линий устанавливается во втором настроечном параметре LinesColor.
11.11.2011, 14:18
Регистрация: 18.08.2008 / Сообщений: 8,856
Поблагодарили 2,792 раз(а) / Репутация: 2826
Подготовка к работе индикатора осуществляется в первых пяти блоках функции init:

// - 1 - == Проверка принадлежности текущей валютной пары к списку рассматриваемых ======
MasterValute = StringSubstr(Symbol(), 0, 3);
// Формируем имя первой валюты
SlaveValute = StringSubstr(Symbol(), 3, 3); // Формируем имя второй валюты
CorrSign[, 0] = 1; // Валюта MasterValute всегда в..
// ..числителе ВП Pair[0]
CorrSign[, 1] = -1; // Валюта SlaveValute всегда в..
// ..знаменателе ВП Pair[0]
CorrSign[, 2] = 0; // Валюта RaterValute никогда не..
// ..присутствует в ВП Pair[0]
if (!IsInList(MasterValute)) // Проверим "числитель" текущей ВП
return();
if (!IsInList(SlaveValute)) // А затем "знаменатель"
return();
if (!IsInList(RaterValute)) // Также проверим наименование третьей
return(>(); // ..валюты
// - 1 - == Окончание блока ================================================== ==========

// - 2 - == Формирование списка трех используемых валютных пар ==========================
Pair[] = Symbol(); // Главная пара - текущая
Pair[1] = GeneratePair(MasterValute, RaterValute);// Генерируем имя второй ВП
if (Pair[1] == "") // Если пара не найдена, то это..
return(); // ..фатальная ошибка
Pair[2] = GeneratePair(SlaveValute, RaterValute);// Генерируем имя третьей ВП
if (Pair[2] == "") // Если пара не найдена, то это..
return(>(); // ..фатальная ошибка
// - 2 - == Окончание блока ================================================== ==========

// - 3 - == Определение минимальной глубины истории из трех инструментов ================
MinDepth = Time[Bars-1]; // Начнем с текущей ВП
datetime temp = GetDepth(Pair[1]); // Получим глубину по второй ВП
if (temp == 0) // Если получен 0, то произошла ошибка
return();
MinDepth = MathMax(MinDepth, temp); // Применим наибольшее значение
temp = GetDepth(Pair[2]); // Получим глубину по третьей ВП
if (temp == 0) // Если получен 0, то произошла ошибка
return();
MinDepth = MathMax(MinDepth, temp); // Применим наибольшее значение
// - 3 - == Окончание блока ================================================== ==========

// - 4 - == Начальное отображение линий выбора исторического диапазона ==================
Line1Time = 1;
Line2Time = 1;
ShowVLine(line_1_name, MathMax(MinDepth, Time[100]));
ShowVLine(line_2_name, Time[1]);
// - 4 - == Окончание блока ================================================== ==========

// - 5 - == Получим величину пунктов и разрядность для каждой ВП ========================
for (int i = 0; i < 3; i++)
{
_Point[i] = MarketInfo(Pair[i], MODE_POINT);
_Digits[i] = MarketInfo(Pair[i], MODE_DIGITS);
}
// - 5 - == Окончание блока ================================================== ==========


Названия валют, составляющих треугольник, записываются в трех переменных: MasterValute, SlaveValute и RaterValute. И, если значение RaterValute указывает пользователь, то MasterValute и SlaveValute вычисляются из названия текущего инструмента. "Основной" валютой считается та, которая идет в названии первой (числитель пары), а "вспомогательной" - та, которая идет второй (знаменатель пары).
Далее в первом блоке происходит начальное заполнение массива CorrSign. Этот массив необходим для определения нахождения каждой из трех валют в соответствующей валютной паре. Индексам первого измерения массива соответствуют валютные пары: 0 - текущая, 1 - пара, составленная из MasterValute и RaterValute, 2 - пара, составленная из SlaveValute и RaterValute. Индексам второго измерения массива соответствуют валюты: 0 - MasterValute, 1 - SlaveValute, 2 - RaterValute. Значения, записываемые в соответствующие элементы, определяют положение валюты в валютной паре: 1 - числитель, -1 - знаменатель, 0 - не присутствует. Для индекса 0 первого измерения массива значения элементов всегда постоянны: [0, 0] = 1 (валюта MasterValute всегда в числителе текущей пары), [0, 1] = -1 (SlaveValute всегда в знаменателе текущей пары), [0, 2] = 0 (RaterValute никогда не присутствует в паре).
Последнее действие первого блока - определение принадлежности валют к списку рассматриваемых валют. В этот список входит всего восемь валют: USD, GBP, EUR, CAD, JPY, AUD, NZD, CHF. При желании, список можно дополнить. Роль списка - отсеивание явно несуществующих валют типа "DFGHKJOI". Также он является защитой от использования индикатора на инструментах, не являющихся валютной парой (например, GOLD).
Задачей второго блока является формирование списка трех валютных пар. Список записывается в массив Pair, первому элементу которого (индекс 0) всегда соответствует текущая валютная пара. Вторая и третья валютная пары формируются при помощи вызова пользовательской функции GeneratePair. Алгоритм функции сделан по образу и подобию блоков 3.1 и 3.2 функции init индикатора PairsDependence (Зависимость в движении валютных пар. Часть 1). В упомянутой статье подробно описан алгоритм синтеза валютной пары по двум валютам. Поэтому в данном материале не будем рассматривать код функции GeneratePair.
Блок 3 определяет наименьшую глубину истории, которой обладает каждая из трех рассматриваемых валютных пар. Для этого используется функция GetDepth, которая получает количество доступных баров для соответствующей валютной пары, и по индексу последнего бара определяет дату и время начала истории. Полученные три значения начала истории сравниваются, из них определяется минимальное, которое и записывается в переменную MinDepth.
Блок 4 отображает две вертикальные прямые, служащие для указания начала и окончания расчетного диапазона, в начальном положении. Этому положению соответствуют бары с индексами 1 и 100 (можно изменить в коде). Переменные Line1Time и Line2Time соответствуют дате/времени предыдущего положения линий. Сравнивая текущее положение линий и предыдущее, индикатор определяет момент для произведения перерасчета своих значений. Для того чтобы расчет произошел при первом подключении индикатора, переменным присваивается значение, которое не соответствует текущему положению линий.
В пятом блоке собираются сведения о величине одного пункта в валюте котировки для каждой из трех валютных пар (массив в _Point). Попутно определяется разрядность котировок пар (массив _Digits).
Основной алгоритм работы индикатора собран в блоках 2-6 функции start:

// - 2 - == Ожидание изменения положения одной из вертикальных линий ====================
if (GetVLineBarIndex(line_1_nameme, Line1Time) ||// При изменении положения одной из..
GetVLineBarIndex(line_2_name, Line2Time)) // ..двух линий - перерасчет
// - 2 - == Окончание блока ================================================== ===========
{
// - 3 - == Проверка выхода линии за границы доступной истории ==========================
if (Line1Time < MinDepth || Line2Time < MinDepth)// Если одна из линий имеет дату..
{ // ..меньше, чем начало истории по..
// ..одной из ВП, то выдадим сообщение
Alert("Минимально возможная дата для трех используемых валютных пар: ",
TimeToStr(MinDepth, TIME_DATE));
Line1Time = MathMax(Line1Time, MinDepth); // ..и передвинем линии на минимально
Line2Time = MathMax(Line2Time, MinDepth); // ..возможную дату
ShowVLine(line_1_name, Line1Time); // Отображение линий
ShowVLine(line_2_name, Line2Time);
}
// - 3 - == Окончание блока ================================================== ===========

// - 4 - == Вычисление начальной и конечной дат =========================================
if (Line1Time > Line2Time) // При вычислениях важно, чтобы..
{ // ..конечная дата была больше..
datetime begin_time = Line2Time; // ..начальной
datetime end_time = Line1Time;
}
else
{
begin_time = Line1Time;
end_time =>= Line2Time;
}
// - 4 - == Окончание блока ================================================== ===========

// - 5 - == Очистка всех буферов индикатора =============================================
ArrayInitialize(Master, EMPTY_VALUE);
ArrayInitialize(Slave, EMPTY_VALUE);
ArrayInitialize(Rater, EMPTY_VALUE);
// - 5 - == Окончание блока ================================================== ===========

// - 6 - == Расчет значений индикатора на всем выбранном диапазоне ======================
double koef[3]; // Коэффициенты умножения
int k = iBarShift(NULL, 0, begin_time); // Начальный бар отображения
int end = iBarShift(NULL, 0, end_time); // Конечный бар отображения индикатора
for (; k >= end; k--) // По всему выбранному диапазону дат
{
ArrayInitialize(Changes, 0); // Обнуляем суммы каждой валюты
for (int i = 0; i < 3; i++) // По трем ВП
{
koef[i] = CalcChanges(i, begin_time, Time[k]);// Расчет сумм
if (koef[i] == 0) return(); // Если получена ошибка, то выходим
}
if (CorrSign[1, 0] == -1) // Если основная валюта находится
koef[1] = 1/koef[1]; // ..в знаменателе второй пары,
// ..то коэффициент принимает..
// ..обратное значение
Master[k] = Changes[]*koef[1]; // Расчет значения для первой валюты
if (CorrSign[2, 1] == -1) // То же самое для второй валюты
koef[2] = 1/koef[2];
Slave[k] = Changes[1]*koef[2]; // Расчет значения для второй валюты
Rater[k] = Changes[2]; // Расчет значения для третьей валюты
}
// - 6 - == Окончание блока ================================================== ===========
}
11.11.2011, 14:18
Регистрация: 18.08.2008 / Сообщений: 8,856
Поблагодарили 2,792 раз(а) / Репутация: 2826
Запуск процесса расчета происходит при выполнении условия, содержащегося в блоке 2. Условие простое - перемещение одной из двух вертикальных линий. Определение факта перемещения происходит при помощи вызова функции GetVLineBarIndex, которой передается имя первой или второй линии, а также время, соответствующее предыдущему положению. Если новое положение линии не соответствует старому положению, то функция вернет значение true.
В третьем блоке проверяется выход одной из линий за минимально возможную дату MinDepth. В такой ситуации вышедшая за пределы линия принудительно перемещается на дату, равную значению MinDepth, а на экране появляется соответствующее сообщение.
В четвертом блоке определяется, какая из линий соответствует началу диапазона, а какая его окончанию. Линия, расположенная левее по графику, считается начальной (begin_time).
Пятый блок подготавливает индикаторные буфера к заполнению, очищая их перед использованием. Использованный здесь метод, согласно справке по MQL4, применять не рекомендуется. Такая рекомендация, очевидно, касается использования функции ArrayInitialize в теле функции и init. В данном же случае (тело функции start) применение подобной инициализации видится уместным.
В шестом блоке производятся все необходимые расчеты и заполнение индикаторных буферов от бара с индексом k до бара с индексом end. Значения индексов рассчитываются, исходя из времени начала (begin_time) и окончания (end_time) расчетного диапазона. Результат изменений для каждой из трех валют вычисляется и записывается в массиве Changes. Каждому элементу массива соответствует своя валюта: 0 - MasterValute, 1 - SlaveValute, 2 - RaterValute. В цикле i при помощи вызова функции CalcChanges вычисляются изменения для каждой валюты, произошедшие между временем begin_time и очередным баром с индексом k. Алгоритм расчетов полностью соответствует расчетам, приведенным выше. Функция CalcChanges возвращает цену закрытия свечи, соответствующую окончанию расчетного диапазона (время Time[k]) для каждой из валютных пар. Впоследствии эта цена, как мы помним, используется для приведения всех численных изменений к единому знаменателю (коэффициент koef).
В качестве эталонной валюты в индикаторе всегда принимается третья валюта RaterValute, для которой никакой коэффициент не нужен. Поэтому из элементов массива koef используются только последние два элемента (индексы 1 и 2). Значение для MasterValute (находится в Changes[0]) умножается на коэффициент koef, если валюта находится в числителе пары Pair[1] (вторая пара в списке) и делится, если валюта расположена в знаменателе. Аналогичным образом определяется метод (умножение или деление) приведения значения SlaveValute (находится в Changes[1]) к величине RaterValute.
Рассмотрим алгоритм расчета изменений для одной валютной пары, реализованный в функции CalcChanges:

//+-------------------------------------------------------------------------------------+
//| Расчет изменений по текущей ВП для составляющих ее валют |
//+-------------------------------------------------------------------------------------+
double CalcChanges(int i, datetime b_time, datetime e_time)
{
int b_index = GetBarNum(i, b_time); // Индекс начального бара
if (b_index < 0) // Если не удалось получить сведения..
{ // ..по нужной ВП, то это фатальная..
Alert("Фатальная ошибка при получении данных по инструменту ", // ..ошибка
Pair[i]);
Activate = false; // Выключаем индикатор
return(>();
} int e_index = iBarShift(Pair[i], 0, e_time); // Индекс конечного бара
double close = iClose(Pair[i], 0, e_index); // Значение цены закрытия
double open = iOpen(Pair[i], 0, b_index); // Значение цены открытия
for (int k = 0; k < 3; k++) // Пройдем по валютам
{
if (CorrSign[i, k] == 1) // Если валюта присутствует в..
// ..числителе валютной пары, то..
Changes[k] += (close - open)/_Point[i]; // ..занесем в изменения то количество
// ..пунктов, на которое изменилась..
// ..цена. При росте - плюс, при..
// ..падении - минус
if (CorrSign[i, k] == -1) // Если валюта присутствует в..
// ..знаменателе валютной пары, то..
Changes[k] += RowSumm(open, close, // ..занесем в изменения сумму ряда..
_Digits[i], _Point[i>i]);// ..от начального до конечного..
// ..значений
}
return(close); // Вернем значение цены закрытия
}


Первый аргумент функции i - это порядковый номер (начинается с 0) пары в списке рассматриваемых валютных пар, который соответствует индексу элемента массива Pair. Второй и третий аргументы - начальное и конечное время расчетного диапазона, которые сразу же преобразуются в номера соответствующих баров, т.к. получить ценовые данные бара можно только по их индексам.
Получение индекса начального бара производится путем обращения к функции GetBarNum. Эта функция полностью идентична функции GetTime индикатора ThreeRegressionLines (статья Что там, на другом таймфрейме?). Единственное отличие - функция iTime заменена функцией iBarShift. Смысл использования функции GetBarNum вместо прямого обращения к iBarShift - существование риска отсутствия исторических данных. Функция GetBarNum возвращает запрошенное значение или -1, если данные так и не были получены. В момент получения индекса конечного бара e_index обращение к функции iBarShift происходит напрямую, т.к. исторические данные к этому моменту уже точно получены (сам факт продолжения исполнения функции CalcChanges).
На основании индексов начального и конечного баров производится получение цен открытия и закрытия расчетного диапазона, после чего в цикле k происходит поочередное обращение к трем валютам. Две из трех валют присутствуют в рассматриваемой валютной паре. Отсутствующая валюта определяется по нулевому значению соответствующего элемента массива CorrSign. Элемент массива Changes, имеющий индекс отсутствующей валюты, в результате исполнения текущей функции остается неизменным. Валюта, находящаяся в числителе текущей валютной пары (CorrSign = 1), получает значение изменения, соответствующее разности цен закрытия и открытия расчетного диапазона, выраженное в пунктах. Для валюты, находящейся в знаменателе валютной пары (CorrSign = -1), значение изменения будет равно сумме значений ряда, начинающегося от цены открытия диапазона и заканчивающегося ценой закрытия. Эта сумма рассчитывается при помощи функции RowSumm:

//+-------------------------------------------------------------------------------------+
//| Вычисление суммы чисел от row_beg до row_end включительно с шагом point |
//+-------------------------------------------------------------------------------------+
double RowSumm(double row_beg, double row_end, double row_dig, double point)
{
if (MathAbs(row_beg - row_end) <= point) // Если ряд состоит из одного числа,..
return(); // ..то его сумма равна нулю
int mul = -1; // Множитель для правильной установки
// ..знака
if (row_beg > row_end) // Если ряд задан неправильно (от..
{ // ..большего к меньшему), то меняем..
double temp = row_end; // ..последовательность
row_end = row_beg - point; // Цену open уменьшаем
row_beg = temp;
mul = 1; // Знак меняем на противоположный
}
if (row_beg < row_end) // Если ряд задан правильно, то..
row_beg += point; // ..цену open увеличиваем
int row_length = MathRound((row_end - row_beg)/point);// Длина ряда в пунктах
double row_average = (row_end + row_beg)/2; // Среднее число ряда
double res1 = (row_beg + row_end)* // Первая часть результата
MathRound(row_length/2.0);


if (MathMod(row_length, 2) == 0) // Если длина ряда - четное число,..
res1 = res1 + row_average; // ..то не всем числам ряда будет..
//..соответствовать пара. К результату
// ..нужно добавить среднее значение..
// ..ряда
return(res1*mul); // Если длина ряда - нечетное число,..
// ..то ничего добавлять не нужно
}

Самый простой способ вычисления суммы ряда чисел заключается в их последовательном суммировании. Он хорошо работает, когда ряд чисел достаточно короткий (до 1000 элементов). При большой длине ряда (например, 1 млн. чисел) этот метод вычисления может оказаться слишком продолжительным. К счастью, существует более простой способ подсчета суммы ряда чисел, продолжительность выполнения которого не зависит от длины ряда. Заключается он в разбиении ряда на пары чисел таким образом, чтобы сумма каждой пары была равна одному и тому же значению. Например, для подсчета суммы ряда натуральных чисел от 1 до 100 достаточно вычислить одну сумму: 1 и 100. Все последующие пары чисел в направлении схождения ряда будут давать то же самое число - 101 (2 и 99, 3 и 98, 4 и 97 и т.д.). Таких пар в ряду будет 50 ((100 - 1 + 1)/ 2). Значит, 101*50 = 5050. В итоге получаем формулу для вычисления суммы ряда с четным количеством элементов:


где a - первый элемент ряда,
b - последний элемент ряда.
Для рядов с нечетным количеством элементов не для всех чисел найдется пара. Без пары останется средний элемент ряда, значение которого следует прибавить к сумме имеющихся пар:

11.11.2011, 14:21
Регистрация: 18.08.2008 / Сообщений: 8,856
Поблагодарили 2,792 раз(а) / Репутация: 2826
В функции RowSumm приведенные формулы реализованы немного по-другому, т.к. мы имеем дело не с натуральными числами, а с вещественными, шаг приращения для которых указывается в аргументе point. Соответственно, разрядность чисел указывается в параметре row_dig, а начальное и конечное значения - в row_beg и row_end соответственно.
В первом условии функции проверяется длина ряда. Если ряд состоит из одного числа, то сумма ряда считается нулевой, т.к. не было изменения цены. Далее определяется направление ряда: растущий или нисходящий. Для расчета суммы чисел растущего ряда (row_beg < row_end) применяется коэффициент умножения -1 (изменяется знак суммы ряда), а начальное значение ряда увеличивается на один пункт. Для подсчета суммы чисел нисходящего ряда конечное и начальное значения меняются местами, начальное значение уменьшается на один пункт, а коэффициент умножения становится равным 1 (знак суммы ряда не изменяется).
В переменной row_length сохраняется длина ряда в пунктах без учета четности или нечетности (b - a в формулах). Среднее значение ряда row_average рассчитывается только для применения к рядам нечетной длины. Поэтому никакого округления здесь не производится, т.к. в случае применения значения оно точно будет кратно величине пункта. Далее рассчитывается непосредственно сумма ряда res1, которая вычисляется по первой формуле. Вместо добавления единицы используется математическое округление.
Решение о добавлении среднего значения к полученной сумме принимается также немного по-другому. Так как длина ряда вычислена без прибавления единицы, то проверяется не нечетность, а четность длины ряда.
Возвращает функция значение res1, умноженное на коэффициент mul.
Вид индикатора в момент присоединения к графику валютной пары EURUSD будет следующим (см. рис. 3). Напомним, что для успешного проведения расчетов индикатору необходимо иметь доступ к данным еще двух валютных пар (с тем же таймфреймом), кроме текущей: пары, составленной из числителя текущей пары и валюты, указанной в параметре RaterValute, а также пары, составленной из знаменателя текущей валюты и валюты, указанной в параметре RaterValute. В противном случае будет отображено сообщение об ошибке, что приведет к неработоспособности индикатора.


Рис. 3. Индикатор CurrencyTriangle.
В нижнем окне графика показано последовательное изменение состояния "кармана" по сравнению с его состоянием на 2011.08.06 16:00. Зеленой линией отображено изменение количества йены, красной - евро, а синей - доллара США. Эти данные не претендуют на абсолютную достоверность, т.к. не принимают во внимание изменения, происходящие на других валютных парах, составленных из валют EUR, USD и JPY. Тем не менее, основные процессы распределения валют в "кармане" (или на рынке, если отразить график относительно нуля), индикатор отражает.

Игорь Герасько
19.03.2014, 11:44
Аватар для Novikov
Novikov Novikov на форуме Гуру форума
Регистрация: 02.08.2012 / Адрес: Днепр / Сообщений: 3,154
Поблагодарили 2,676 раз(а) / Репутация: 2664
Выдавал ошибку в новом билде. По моей просьбе его подправили здесь.
Кому нужен, можете скачать: CurrencyTriangle[1].mq4

Но почему то у меня отображается совершенно не так, как на скрине вверху.
Пожалуйста, разъясните, в чем может быть проблема?

07.03.2017, 00:33
Аватар для kalinych
kalinych kalinych вне форума Интересующийся
Регистрация: 21.04.2011 / Сообщений: 4
Поблагодарили 0 раз(а) / Репутация: 1
Выдавал ошибку в новом билде. По моей просьбе его подправили здесь.
Кому нужен, можете скачать: CurrencyTriangle[1].mq4

Но почему то у меня отображается совершенно не так, как на скрине вверху.
Пожалуйста, разъясните, в чем может быть проблема?

Интересная идея, но проверить не получается.
Индюк компилируется правильно, но картинка нечитабельна.
Кто нибудь, подскажите, как довести до ума
10.03.2017, 08:33
Аватар для Crosh
Crosh Crosh на форуме Местный житель
Регистрация: 29.03.2013 / Сообщений: 1,458
Поблагодарили 259 раз(а) / Репутация: 260
Интересная идея, но проверить не получается.
Индюк компилируется правильно, но картинка нечитабельна.
Кто нибудь, подскажите, как довести до ума
Так новикову в личку напиши, может поможет. Как по мне сложная система. Слишком от индюка зависима.
10.03.2017, 12:43
Аватар для Novikov
Novikov Novikov на форуме Гуру форума
Регистрация: 02.08.2012 / Адрес: Днепр / Сообщений: 3,154
Поблагодарили 2,676 раз(а) / Репутация: 2664
Вертикальные линии задают границы.
Измени дату этих вертикальных линий и увидишь изменения!
10.03.2017, 13:36
Аватар для kalinych
kalinych kalinych вне форума Интересующийся
Регистрация: 21.04.2011 / Сообщений: 4
Поблагодарили 0 раз(а) / Репутация: 1
Вертикальные линии задают границы.
Измени дату этих вертикальных линий и увидишь изменения!
Благодарю, всё заработало.
В конце статьи на рис.3 эти временнЫе границы автор наглядно демонстрирует , а я прошляпил.

После смене ТФ индюк надо перезапускать (через вызов свойства) и начальную дату двигать ближе к конечной,
--- эта суета напрягает, но есть исходник, может там по-лёгкому что подправить мона
10.03.2017, 16:26
Аватар для Novikov
Novikov Novikov на форуме Гуру форума
Регистрация: 02.08.2012 / Адрес: Днепр / Сообщений: 3,154
Поблагодарили 2,676 раз(а) / Репутация: 2664
может кто нибудь поможет с этим вопросом в теме Доработка ботов (советников, индикаторов) vol. 2
Ответить


Опции темы

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Trackbacks are Выкл.
Pingbacks are Выкл.
Refbacks are Выкл.



Текущее время: 11:37. Часовой пояс GMT.


Перевод: zCarot
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
SEO by vBSEO