Список работ

Оценка эффективности индикаторов технического анализа

Содержание

Постановка задачи

Выбор индикатора Технического анализа (далее ТА) для последующего употребления будет более аргументирован, если известна эффективность индикаторов, из числа которых выбор осуществляется.
Для оценки эффективности индикатора ТА или группы одновременно употребляемых индикаторов необходимо иметь графики цены актива, например акции, стратегию применения индикатора и знать формулу или алгоритм вычисления значений индикатора.
Так, Осциллятор Ускорение/Замедление (Acceleration/Deceleration Oscillator, AC) можно оценить, применяя следующую стратегию:

График цены актива можно выгрузить, например, с finam.ru или иного источника.
Формула (алгоритм) вычисления значений индикатора берется из приводимых ниже источников. Из них же заимствуются и описания подаваемых индикатором сигналов.
Таким образом, остается выбрать подходящую стратегию употребления индикатора, применить ее на некотором временном интервале и подсчитать полученный доход или понесенный убыток.

Чтобы реализовать эту схему, создается приложение, обеспечивающее

При создании приложения употреблена СУБД Microsoft Visual FoxPro. Примеры программирования на Microsoft Visual FoxPro можно найти в [4].

Некоторые понятия ТА

Трейдеры и инвесторы

Трейдеры (Traders) – биржевые спекулянты (игроки), рассчитывающие наварить на постоянной купле-продаже ФИ. В качестве трейдеров могут выступать как физические, так и юридические лица, например, банки или инвестиционные компании. Физические лица, как правило, относятся к разряду мелких игроков, а банки – к разряду крупных. Доли мелких, средних и крупных игроков в общем объеме биржевых торгов неизвестны.
Быки (Bulls) – трейдеры, играющие на повышение рынка.
Медведи (Bears) – трейдеры, играющие на понижение рынка.
Сила быков и медведей измеряется, в частности, индикатором EFI.
Приведенные понятия относятся к разряду образных, поскольку трейдер в общем случае придерживается не одной стратегии (бычьей, медвежьей или иной), а тех, которые сулят наибольшую выручку.
Биржевые инвесторы – физические или юридические лица, на длительный срок покупающие ценные бумаги. С экономической точки зрения инвестиции в ценные бумаги оправданы, если получаемые дивиденды или процентные выплаты (за вычетом налогов) превышают инфляцию.
Долгосрочное инвестирование в подавляющее большинство продаваемых на биржах ценных бумаг является практически бессмысленным мероприятием, поскольку биржевые спекулянты (трейдеры) делают рынок непредсказуемым.

Эмитенты и ценные бумаги

Эмитент – любой орган или организация (государственный банк, финансово-кредитное учреждение, предприятие, компания), выпускающие в обращение деньги и ценные бумаги, производящие эмиссию. Например, Сбербанк, помимо прочих активов, поставляет на биржу обычные и привилегированные акции.
Эмиссия ценных бумаг – выпуск в обращение акций, сертификатов, облигаций и других ценных бумаг любыми эмитентами, включая государство, кредитные учреждения, акционерные компании.
Рыночная капитализация компании – это величина, равная количеству акций компании (общему количеству выпущенных ценных бумаг), умноженная на текущую рыночную стоимость одной акции. Если компания выпустила несколько видов ценных бумаг, то все они учитываются при расчете капитализации по вышеприведенной схеме.
Акции ранжируются по эшелонам.
Акции первого эшелона – это так называемые голубые фишки (в покере голубые фишки, или blue chips считаются самыми дорогими). Эмитенты голубых фишек – это крупные компании, имеющие большую капитализацию.
Российские компаний с голубыми фишками (в скобках указан код акции):

Голубые фишки нередко показывают хорошую динамику роста. Но она может уступать некоторым акциям второго эшелона.
Голубые фишки обладают высокой степенью ликвидности.
Ликвидность ценной бумаги оценивают по количеству совершаемых сделок (объему торгов) и величине спреда – разницы между максимальными ценами заявок на покупку и минимальными ценами заявок на продажу. Чем больше сделок и меньше разница, тем больше ликвидность.
Второй эшелон – это акции, эмитенты которых вне списка компаний с наиболее ликвидными ценными бумагами, то есть не являются представителями голубых фишек на фондовом рынке. Акции второго эшелона уступают голубым фишкам в отношении ликвидности и капитализации.
Классическим представителем второго эшелона может выступать компания, имеющая средние годовые объемы продаж около 150-400 миллионов долларов и рыночную капитализацию 25-500 миллионов долларов.
Второй эшелон может оказаться более привлекательным, чем голубые фишки: его акции могут подорожать из-за их рыночной переоценки. Сравнительно небольшая компания с большей вероятностью прибавит в росте на 50 и более процентов, нежели известные лидеры.
Третий эшелон включает акции компаний-новичков, которые только начали свое обращение на рынке и не имеют собственной истории торгов. Уровень ликвидности третьего эшелона наиболее низок. Акции третьего эшелона подвержены наибольшим рискам. Однако, как известно, чем выше риск, тем выше может быть и доходность.
IPO (Initial Public Offering) – первичное размещение эмитентом ценных бумаг (акций). Цель IPO – получение дополнительных средств для развития бизнеса.

Анализ рынка

Макроэкономические индикаторы (МА) и индексы деловой активности (ИДА)– числовые показатели, отражающие экономическое состояние отраслей промышленности, сферы услуг, стран и регионов.
Примеры МА:

Примеры ИДА:

Технический анализ – совокупность методов прогнозирования динамики цены актива на основе прошлых цен. При прогнозировании широко употребляются индикаторы ТА.
Фундаментальный анализ (Fundamental analysis) – совокупность методов прогнозирования рыночной (биржевой) стоимости компаний, основанных на анализе финансовых и производственных показателей её деятельности. Фундаментальный анализ исходит из предпосылки, что мировая экономика, макроэкономические тенденции влияют на котировки акций конкретных компаний, а также на иные инструменты рынка ценных бумаг. Оценка экономики и макроэкономических тенденций опирается на макроэкономические индикаторы и индексы деловой активности.

Торги

Бар (Bar) – вертикальная полоса (столбец) на графике цены актива, выводимая в каждом интервале графика от минимального значения цены интервала к максимальному значению цены интервала.
Бычье расхождение (дивергенция) – цена достигает нового максимума, а индикатор не поднимается выше предыдущих максимумов.
Бычье схождение (конвергенция) – цена не достигла нового максимума, а индикатор показал новый максимум.
Медвежье расхождение (дивергенция) – цена не достигла нового минимума, а индикатор показал новый минимум.
Медвежье схождение (конвергенция) – цена достигает нового минимума, а индикатор не опускается ниже предыдущих минимумов.
Сигналы расхождения и схождения предупреждают о возможном изменении направления движения цены.
Волатильность ФИ (Volatility) – это разница, вычисляемая на заданном временном промежутке, между максимальной и минимальной ценами ФИ. Используется, в частности, индикатором ChV.
Длинная позиция (Long position) – покупка акций с целью их последующей реализации после роста цены (бычья тактика).
Короткая позиция (Short position) – продажа акций, взятых взаймы у брокера, в ожидании снижения их цены. После снижения цены трейдер покупает акции и возвращает их брокеру (медвежья тактика).
Коррекция (откат рынка – это движение цены в противоположную сторону по отношению к действующему в данный момент тренду. Различают восходящую, нисходящую и боковую коррекции. Последняя также называется консолидацией рынка. Консолидация рынка возникает, когда спрос и предложение на ФИ примерно равны. Продолжительную коррекцию можно рассматривать как тренд.
Кредитное плечо – это доля средств трейдера в общей сумме сделки. Например, кредитное плечо 1:20 при сумме сделки 5000 руб. означает, что 250 руб. этой суммы взято со счета трейдера, а оставшиеся 4750 руб. добавлены инвестиционной компанией (предоставлены в кредит).
Отскок от трендовой линии – кратковременное движение цены вблизи линии тренда, в направлении, противоположном тренду.
Пипс (Pips) – минимальная единица изменения цены актива. Например, если акция котируется с точностью до третьего десятичного знака, то 1 пипс = 0.001 руб.
Спрэд (Spread) – разница между ценами покупки и продажи актива.
Стоп-приказ на покупку (Order Buy Stop) – приказ на покупку ценной бумаги в случае, когда рыночная цена достигает или преодолевает заданную цену (стоп-цену).
Стоп-приказ на продажу (Order Sell Stop) – приказ на продажу ценной бумаги, когда рыночная цена достигает или оказывается ниже стоп-цены.
Стоп-приказ исполняется по наилучшей доступной цене.
Финансовые инструменты (ФИ) – продаваемые на бирже активы, например, акции, валюта, сырьевые ресурсы и др.
Флэт (Flat) – временной промежуток, в котором цена актива практически не изменяется.

Индикаторы ТА, используемые приложением

Индикатор ТА – это показатель, вычисляемый в каждой точке заданного временного диапазона, количественно характеризующий динамику биржевого актива.
Описание индикаторов ТА широко представлено в Интернете (см. например список источников). По этой причине индикаторы ТА рассматриваются бегло, в объеме, необходимом для построения приложения.
Список используемых приложением индикаторов ТА приведен в алфавитном порядке:

Категории индикаторов ТА

В разных источниках индикаторы делят на три или пять категорий.
В первом случае выделяют следующие категории:

  1. Индикаторы тренда. Указывают направление движения цены, синхронно или с небольшим опозданием определяют силу, продолжение или смену тренда. Примеры: ADX, BB, CCI, +/–DM, MA, MACD, PSAR, PCU и SD.
  2. Осцилляторы: AC, ChO, EFI, MACD, Momentum, RSI, OSC, OsMA, ROC, SO и %R.
  3. Индикаторы, характеризующие объем: A/D, ChV, MnFI, OBV, VA и VPT.

Список из пяти категорий разбиения следующий:

  1. Индикаторы тренда. Примеры: ADX, MA, MACD, +/–DM, PSAR.
  2. Индикаторы изменчивости. Показывают меры изменчивости цены базисного актива. Важны на смене тренда или при боковом тренде. Сигналы индикатора с небольшим временным окном позволяют принять решение о входе в рынок или о выходе из него. Примеры: BB, ChV, RSI, SD.
  3. Индикаторы момента. Измеряют скорость изменения цены за определенный промежуток времени. Используются и для определения тренда, и для прогнозирования момента его окончания. Примеры: Momentum, MACD (при определенных условиях), ROC, RSI.
  4. Индикаторы цикла. Выявляют циклические составляющие и их длины. Хорошо работают только на боковых трендах. Важны при работе с фьючерсами на товарных рынках с высокой циклической составляющей, например на рынках зерна, сахара или нефти. Примеры: FTZ, MESASWI.
  5. Индикаторы силы рынка. Подают сигналы о силе тренда. Используют в качестве одной из независимых переменных либо объем сделок, либо число открытых позиций. Примеры: OBV, VA.

В ряде источников выделяется еще одна категория – психологические индикаторы, определяющие настроения участников рынка.

Проект приложения

План работ

Приложение реализуется поэтапно:

  1. Разработка модели данных и подсистемы импорта данных.
  2. Подбор индикаторов ТА.
  3. Реализация подсистемы вычисления линий тренда, скользящих средних и иных сопутствующих данных.
  4. Реализация подсистемы вычисления значений индикаторов ТА.
  5. Реализация подсистемы оценки эффективности индикаторов ТА.
  6. Оценка эффективности индикаторов ТА.

Модель данных

Приводится сокращенная версия модели данных, отражающей ход биржевых торгов. В модели имеются следующие таблицы:

Первые две таблицы имеют следующие поля:

Таблица Активы имеет следующие поля (тип всех полей Integer):

Таблица Биржевые Торги имеет следующие поля:

Загрузка данных

Данные в таблицу Биржевые Торги загружаются из файла, выгружаемого с finam.ru или иного источника.
Файл с данными выгружается без заголовка и несет следующие сведения: Date, Time, Open, High, Low, Close и Volume.
Разделителем полей является запятая.
Интервал предоставления данных 5 минут.
Для загрузки выбирается актив и файл с выгруженными данными. Форма, обеспечивающая загрузку, приведена на рис. 1.

Данные для расчета индикаторов технического анализа

Рис. 1. Форма загрузки актива

Кнопка Загрузить D зарезервирована для загрузки данных с интервалом в 1 день.
Загрузку и вывод сведений о результатах загрузки обеспечивает следующий код:

LOCAL fn, IDSVl
WITH thisForm
 IDSVl = .IDSlct
 nmVl = .TxtNm.Value
ENDWITH
fn = GETFILE('TXT', RTRIM(nmVl), 'Get data', 0, 'Stock prices')
IF NOT EMPTY(fn)
 IF RIGHT(GETWORDNUM(fn, 1, '.'), 1) # 'F'
  MESSAGEBOX("5 minutes file should be selected")
  RETURN
 ENDIF
 ali = 'stcksTrdngTmp'
 closeAli(ali)
 tbl = _tmpPth + ali
 * DATE, TIME, OPEN, HIGH, LOW, CLOSE, VOL
 CREATE TABLE (tbl) FREE (dtD D, tm C(6), pn N(7, 2), hgh N(7, 2), lw N(7, 2), cls N(7, 2), vlm N(9, 0))
 APPEND FROM (fn) DELIMITED WITH ,
 rCVl = RECCOUNT( )
 IF rCVl = 0
  MESSAGEBOX('File is empty')
 ELSE
  ALTER TABLE (tbl) ADD dt T ADD IDS I
  REPLACE dt WITH DATETIME(YEAR(dtD), MONTH(dtD), DAY(dtD), VAL(LEFT(tm, 2)), VAL(SUBSTR(tm, 3, 2)), 0);
   IDS WITH IDSVl ALL
  DELETE stcksTrdngTmp FROM stcksTrdngF;
   WHERE stcksTrdngTmp.IDS = stcksTrdngF.IDS AND stcksTrdngTmp.Dt = stcksTrdngF.Dt
  SELECT stcksTrdngTmp
  PACK
  ali2 = 'stcksTrdngF'
  rc = RECCOUNT(ali2)
  IF RECCOUNT(ali) > 0
   INSERT INTO (ali2) (ids, dt, pn, hgh, lw, cls, vlm);
    SELECT ids, dt, pn, hgh, lw, cls, vlm FROM (ali)
   nDdVl = _TALLY
  ELSE
   nDdVl = 0
  ENDIF
  fn2 = _tmpPth + 'rslts.txt'
  SET SAFETY OFF
  SET ALTERNATE TO (fn2)
  SET ALTERNATE ON
  SET CONSOLE OFF
  ? 'File ' + fn
  ? nmVl + '. Added records: ' + TRANSFORM(nDdVl)
  IF rCVl - nDdVl # 0
   ? nmVl + '. Missed records: ' + TRANSFORM(rCVl - nDdVl)
  ENDIF
  ? ''
  SET ALTERNATE TO
  MODIFY FILE (fn2)
 ENDIF
ENDIF

Загруженные данные используются, в частности, для вычисления значений индикаторов ТА.

Выгрузка графика цены, линии тренда, скользящих средних, объемов и индикаторов ТА

График цены

Выгрузка данных об активе для последующего построения графика цены, линии тренда, скользящих средних, объемов продаж и индикаторов ТА обеспечивается формой, показанной на рис. 2.

Данные для графиков цены, линий тренда, скользящих средних, объемов и и индикаторов технического анализа

Рис. 2. Форма выгрузки актива

Визуализация биржевых данных используется только на этапе отладки системы. Сама же система вырабатывает рекомендации по табличным данным.
Изменение цены актива, 0.5 * (high + low) или close, во времени отображается на графике (рис. 3).

Диаграмма цены и график линии тренда

Рис. 3. ОАО Газпром АО. Черный четверг 22.09.2011

График строится по данным, выгружаемым посредством формы, приведенной на рис. 2. Выгружается один биржевой день. Реальное значение цены актива получается путем прибавления к цене значения base, указанного в заголовке графика.

Линии тренда

Одновременно с графиком цены, если установлен флажок Тренд, рассчитываются и выгружаются начальные и конечные координаты линии тренда. Поиск линии тренда выполняется в указанном временном диапазоне.
Линия тренда – это прямая линия, которая соединяет на графике цены два характерных пика ценовых максимумов (минимумов). Все промежуточные точки лежат ниже (выше) линии тренда.
Линия нисходящего тренда показана на рис. 3, а восходящего – на рис. 4.

Восходящий тренд

Рис. 4. ОАО Газпром АО. Линия восходящего тренда

В пределах основного тренда могут наблюдаться вторичные (малые) тренды.
Выбор временного диапазона при определении линий тренда имеет принципиальное значение.
Тренд, характеризуемый пологой линией (цена практически не меняется), называется боковым (Sideways Trend).
Крутая линия, соединяющая ценовые максимумы, образует нисходящий тренд (Down Trend). При таком тренде тон задают медведи. Такую линию можно рассматривать как нисходящий уровень сопротивления.
Линия, соединяющая ценовые минимумы, образует восходящий тренд (Up Trend). При таком тренде тон задают быки. Такую линию можно рассматривать как восходящий уровень поддержки.
При нисходящем или восходящем тренде можно говорить о существовании на рынке доминирующей группы. Сила этой группы зависит от следующих факторов:

Чем выше значения этих факторов, тем больше сила доминирующей группы.
Продолжительность ярко выраженные трендовых изменений обычно не превышает 15-30 % общей продолжительности биржевых торгов.

Вычисление скользящих средних, стандартного отклонения и середины цены

При вычислении многих индикаторов ТА употребляются сглаженные данные, называемые скользящими средними (Moving Average, MA). Преимущественно используются три следующих типа MA:

Использованы следующие обозначения:
j – номер временного интервала, в котором рассчитывается значение MA;
Pi – значение цены в i-м интервале;
Vi – значение объема продаж в i-м интервале;
n – число интервалов (период) сглаживания (по умолчанию n = 7).

В общем случае при вычислении MA или индикатора ТА в качестве цены актива может быть взято одно из следующих значений:

Выгрузка MA выполняется, если в форме (см. рис. 2) установлен соответствующий флажок.
Вычисление SMA и VMA обеспечивает функция clcSMA, а EMA – функция clcEMA.

*
* Returns SMA or VMA value in record rNd
FUNCTION clcSMA(vl, rStrt, rNd)
 LOCAL aVl, k2
 aVl = 0.0
 FOR k2 = rStrt TO rNd
  GO k2
  aVl = aVl + &vl
 NEXT
 RETURN aVl / (rNd - rStrt + 1)
ENDFUNC
*
* Returns EMA value in record rCrrnt
FUNCTION clcEMA(vl, aVl, rCrrnt, n)
 GO rCrrnt
 RETURN (aVl * (n - 1) + 2 * &vl) / (n + 1)
ENDFUNC

На рис. 5 график SMA отображен красным, EMA – зеленым, а VMA – синим цветом.

Простая скользящая средняя, экспоненциальная скользящая средняя и объемно-зависимая скользящая средняя

Рис. 5. Скользящие средние (график цены показан черным цветом)

Индикатор RSI использует скользящие средние повышения и понижения цены. Их расчет обеспечивает функция clcUDMA.

*
* Returns UMA or DMA value in record rNd
FUNCTION clcUDMA(vl, rStrt, rNd, sUVl)
 LOCAL mVVl, aVl, k2, n2
 aVl = 0.0
 n2 = 0
 FOR k2 = rStrt TO rNd
  GO k2 - 1
  mVVl = &vl
  GO k2
  IF sUVl
   IF &vl > mVVl
    n2 = n2 + 1
    aVl = aVl + (&vl - mVVl)
   ENDIF
  ELSE
   IF &vl < mVVl
    n2 = n2 + 1
    aVl = aVl + (mVVl - &vl)
   ENDIF
  ENDIF
 NEXT
 RETURN IIF(n2 = 0, 0, aVl / n2)
ENDFUNC

Ряд индикаторов, например BB, использует стандартное отклонение цены SD:

SDj = SQRT(sum(Pi – MAi(P, n))^2) / n), где i = j – n + 1, j

Значение SDj в интервалеj возвращается функцией clcSD. Перед вызовом функции выполняется расчет MA (SMA, EMA или VMA).

*
* Returns Standard deviation (SD) value in record rNd
FUNCTION clcSD(p, vl, rStrt, rNd)
 LOCAL sdVl, k2, dVl
 sdVl = 0.0
 FOR k2 = rStrt TO rNd
  GO k2
  dVl = &p - &vl
  sdVl = sdVl + dVl * dVl
 NEXT
 RETURN SQRT(sdVl) / (rNd - rStrt + 1)
ENDFUNC

Индикатор IKH рассчитывает в указанном временном промежутке из n интервалов средину цены:

PM = (max(H, n) + min(L, n)) / 2

Значение PM в позиции rCrrnt для временного промежутка из mntVl интервалов возвращает функция clcPrcMddl.

*
* Returns price middle value for a record rCrrnt
FUNCTION clcPrcMddl(rCrrnt, mntVl)
 LOCAL rStrtVl, pXVl, pMVl, rNdVl, k2
 rStrtVl = rCrrnt - mntVl + 1
 GO rStrtVl
 pXVl = hgh
 pMVl = lw
 rStrtVl = rStrtVl + 1
 FOR k2 = rStrtVl TO rCrrnt
  GO k2
  pXVl = MAX(pXVl, hgh)
  pMVl = MIN(pMVl, lw)
 NEXT
 RETURN 0.5 * (pXVl + pMVl)
ENDFUNC

Вычисление денежного потока, ценовых экстремумов и суммы изменений цены

Индикатор MnFI использует значения положительного и отрицательного денежных потоков, возвращаемых функцией clcMF.

*
* Returns sum for positive or negative money flow for a record rCrrnt
FUNCTION clcMF(rCrrnt, mntVl, fPVl)
 LOCAL rStrtVl, mFVl, k2, mVVl, mVVl2
 rStrtVl = rCrrnt - mntVl + 1
 mFVl = 0.0
 FOR k2 = rStrtVl TO rCrrnt
  GO k2 - 1
  mVVl = (hgh + lw + cls) / 3.0 * vlm
  SKIP
  mVVl2 = (hgh + lw + cls) / 3.0 * vlm
  IF fPVL
   IF mVVl2 > mVVl
    mFVl = mFVl + mVVl2
   ENDIF
  ELSE
   IF mVVl2 < mVVl
    mFVl = mFVl + mVVl2
   ENDIF
  ENDIF
 NEXT
 RETURN mFVl
ENDFUNC

В индикаторах SO, %R и VHF берутся минимальное и максимальное значения цены в заданном временном промежутке. Эти значения возвращаются функциями clcMinP и clcMaxP.

*
* Возвращает минимум цены за mntVl интервалов
FUNCTION clcMinP(rCrrnt, mntVl)
 LOCAL rStrtVl, mnPVl, k2
 rStrtVl = rCrrnt - mntVl + 1
 GO rStrtVl
 mnPVl = lw
 FOR k2 = rStrtVl + 1 TO rCrrnt
  GO k2
  mnPVl = MIN(mnPVl, lw)
 NEXT
 RETURN mnPVl
ENDFUNC
*
* Возвращает максимум цены за mntVl интервалов
FUNCTION clcMaxP(rCrrnt, mntVl)
 LOCAL rStrtVl, mxPVl, k2
 rStrtVl = rCrrnt - mntVl + 1
 GO rStrtVl
 mxPVl = hgh
 FOR k2 = rStrtVl + 1 TO rCrrnt
  GO k2
  mxPVl = MAX(mxPVl, hgh)
 NEXT
 RETURN mxPVl
ENDFUNC

Индикатору VHF нужна сумма изменений цены в заданном промежутке. Эту сумму возвращает функция clcSPCH.

*
* Возвращает сумму абсолютных значений изменений цены за mntVl интервалов
FUNCTION clcSPCH(mV, rCrrnt, mntVl)
 LOCAL rStrtVl, mVVl, sPCHVl, k2
 rStrtVl = rCrrnt - mntVl + 1
 sPCHVl = 0
 GO rStrtVl
 mVVl = &mV
 FOR k2 = rStrtVl + 1 TO rCrrnt
  GO k2
  sPCHVl = sPCHVl + ABS(&mV - mVVl)
  mVVl = &mV
 NEXT
 RETURN sPCHVl
ENDFUNC

Объемы продаж

Объемы продаж отображаются в виде столбчатой диаграммы. Перед выводом график объема масштабируются так, чтобы его можно было показать одновременно с графиком цены (рис. 6).

Добавлена диаграмма объемов продаж актива

Рис. 6. Объемы продаж

Код выгрузки

Выгрузку данных обеспечивает следующий код (функции выгрузки SMA, VMA и EMA см. выше):

* Выгрузка графиков цены, линии тренда, скользящей средней и объема
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
* See someprocs.prg
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, 1)
 RETURN
ENDIF
WITH thisForm
 chTrnd = .CheckTrnd.Value
 chMA = .CheckMA.Value
 chVlms = .CheckVlms.Value
 prdVl = .SpinnerPrd.Value
ENDWITH
nFldsVl = 6
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'mV'
arrFlds[2] = 'trnd'
arrFlds[3] = 'sma'
arrFlds[4] = 'ema'
arrFlds[5] = 'vma'
arrFlds[6] = 'vlms'
arrGTps[1] = 'L 0.0 0.0 0.0 0.0'
arrGTps[2] = IIF(chTrnd, 'T', 'N') + ' 1.0 0.0 1.0 0.0'
arrGTps[3] = IIF(chMA, 'L', 'N') + ' 1.0 0.0 0.0 0.0'
arrGTps[4] = IIF(chMA, 'L', 'N') + ' 0.0 1.0 0.0 0.0'
arrGTps[5] = IIF(chMA, 'L', 'N') + ' 0.0 0.0 1.0 0.0'
arrGTps[6] = IIF(chVlms, 'C', 'N') + ' 0.0 1.0 1.0 0.0'
prcKndVl = LEFT(prcKndVl, RAT('_', prcKndVl) - 1)
prcKndVl = prcKndVl + IIF(chTrnd, '_Trend', '')
prcKndVl = prcKndVl + IIF(chMA, '_MA(' + TRANSFORM(prdVl) +')', '')
prcKndVl = prcKndVl + IIF(chVlms, '_Volume', '')
* Kind of files
mVDtVl = arrFlds[1]
flNmVl = _prjPth + mVDtVl + '.txt'
kFFlsWrdVl = mVDtVl + _NEXTLINE + arrGTps[1]
mvBsVl = nldMv(ali, IDSVl, pDtVl, 'ttl.txt', 'Prices', prcKndVl, mVDtVl, .T., 0, flNmVl, .F.)
FOR k = 2 TO nFldsVl
 kFFlsWrdVl = kFFlsWrdVl + _NEXTLINE + arrFlds[k] + _NEXTLINE + arrGTps[k]
 * Trend
 IF chTrnd
  chTrnd = nldTrnd(thisForm, slctVl, mvBsVl)
 ENDIF
 IF chMA
  chMA = nldMFls(ali, DAY(pDtVl), prdVl, mvBsVl)
 ENDIF
 IF chVlms
  chVlms = nldVlms(ali, DAY(pDtVl), mvBsVl)
 ENDIF
NEXT
* Create file kFFls.txt with types of unloaded files
STRTOFILE(kfFlsWrdVl, _prjPth + 'kFFls.txt')
oVl = chTrnd OR chMA OR chVlms
msgVl = 'File' + IIF(oVl, 's ', '');
 + ' prices' + IIF(chTrnd, ', trend', '') +;
 IIF(chMA, ', moving average', '') +;
 IIF(chVlms, ', volumes', '') +;
 IIF(oVl, ' are', ' is') + ' created'
MESSAGEBOX(msgVl)

*
* Определяет начальные и конечные координаты тренда указанного вида
* (нисходящего или восходящего)
FUNCTION trnd(tk, dtVl, mvMVl, dtVl2, mvMVl2)
 sgn = IIF(tk = 'd', '<', '>')
 sgn2 = IIF(tk = 'd', '>=', '<=')
 sgn3 = IIF(tk = 'd', '>', '<')
 FOR k = 2 TO rCnt - 1
  GO k - 1
  mvPVl = mv
  SKIP
  mvCVl = mv
  SKIP
  IF mvCVl &sgn2 mvPVl AND mvCVl &sgn3 mv
   SKIP -1
   REPLACE tpnts WITH 'm'
   SKIP
  ENDIF
 NEXT
 IF tk = 'd'
  CALCULATE MAX(mv) FOR tpnts = 'm' TO mvMVl
 ELSE
  CALCULATE MIN(mv) FOR tpnts = 'm' TO mvMVl
 ENDIF
 LOCATE FOR mv = mvMVl
 REPLACE tpnts WITH 't'
 dtVl = tmx
 REPLACE tpnts WITH '' FOR tmx < dtVl
 STORE 0 TO dtVl2, mvMVl2
 tgVl = IIF(tk = 'd', -1, 1) * 10000.0
 k = 0
 SCAN FOR tpnts = 'm'
  tgVl2 = (mv - mvMVl) / (tmx - dtVl)
  IF tgVl2 &sgn3 tgVl
   k = k + 1
   mvMVl2 = mv
   dtVl2 = tmx
   tgVl = tgVl2
  ENDIF
 ENDSCAN
 RETURN IIF(k = 0, 0, dtVl2 - dtVl)
ENDFUNC && trnd
*
* Масштабирование по временной оси
FUNCTION fndTmxVl(tmxVl)
 r2Vl = RIGHT(tmxVl, 2)
 DO CASE
 CASE r2Vl = '05'
  r2Vl = '08'
 CASE r2Vl = '10'
  r2Vl = '17'
 CASE r2Vl = '15'
  r2Vl = '25'
 CASE r2Vl = '20'
  r2Vl = '34'
 CASE r2Vl = '25'
  r2Vl = '42'
 CASE r2Vl = '30'
  r2Vl = '51'
 CASE r2Vl = '35'
  r2Vl = '59'
 CASE r2Vl = '40'
  r2Vl = '67'
 CASE r2Vl = '45'
  r2Vl = '75'
 CASE r2Vl = '50'
  r2Vl = '84'
 CASE r2Vl = '55'
  r2Vl = '92'
 ENDCASE
 RETURN LEFT(tmxVl, 2) + r2Vl
ENDFUNC && fndTmxVl
*
* Выгружает в файл fn график цены или индикатора ТА (поле mV) из таблицы ali
FUNCTION nldMv(ali, IDSVl, pDtVl, ttlFnVl, ttlVl, prcKndVl, mV, sBsVl, mVBs, fn, llVl)
 LOCAL f, dVl
 dVl = DAY(pDtVl)
 IF sBsVl
  CALCULATE MIN(&mV) TO mVNVl FOR dy = dVl AND (&mV > 0 OR llVl)
  mVBs = INT(mVNVl / 10) * 10
 ENDIF
 IF NOT EMPTY(ttlFnVl)
  ALTER table (ali) ADD tmx C(4)
  REPLACE tmx WITH CHRTRAN(tm, ':', '') ALL
  SEEK IDSVl IN stcks ORDER IDN
  SEEK stcks.IDC IN cmpns ORDER IDN
  SEEK stcks.IDK IN stcksKnds ORDER IDN
  pDtVl = CHRTRAN(TRANSFORM(PDtVl), '.', '')
  stckVl = TRIM(cmpns.NmShrt) + '_' + TRIM(stcksKnds.NmShrt2)
  SELECT (ali)
  GO TOP
  tmxNVl = tmx
  GO BOTTOM
  tmxXVl = tmx
  * Title-file
  ttlWrd = stckVl + '_' + pDtVl + '_' + ttlVl + '_' + prcKndVl +;
   _NEXTLINE + TRANSFORM(mvBs) + _NEXTLINE + tmxNVl + _NEXTLINE + tmxXVl
  STRTOFILE(ttlWrd, _prjPth + ttlFnVl)
 ENDIF
 f = FCREATE(fn)
 SCAN FOR dy = dVl AND (&mV > 0 OR llVl)
  FPUTS(f, fndTmxVl(tmx) + ' ' + TRANSFORM(&mV - mVBs))
 ENDSCAN
 FCLOSE(f)
 RETURN mVBs
ENDFUNC && nldMv
*
* Находит и выгружает начальные и конечные координаты линии тренда
FUNCTION nldTrnd(thsFrm, slctVl, mvBs)
 LOCAL fn, f, ali0, ali, tbl
 fn = _prjPth + 'trnd.txt'
 WITH thsFrm
  IDSVl = .IDSlct
  sDtVl2 = .SDt2.Value
  nDtVl2 = .NDt2.Value
 ENDWITH
 nDtVl2 = IIF(EMPTY(nDtVl2), DATETIME( ) + 19 * 3600, nDtVl2)
 ali0 = 'stcksTrdngF'
 ali = 'stcksTL'
 tbl = _tmpPth + ali
 SELECT &slctVl AS mV,;
   TRANSFORM(HOUR(dt)) + ":" + IIF(MINUTE(dt) > 5, TRANSFORM(MINUTE(dt)),;
   '0' + TRANSFORM(MINUTE(dt))) AS tm;
  FROM (ali0);
  WHERE IDS = IDSVl;
   AND dt BETWEEN sDtVl2 AND nDtVl2;
  ORDER BY dt;
  INTO TABLE (tbl)
 rCnt = RECCOUNT()
 IF rCnt < 3
  MESSAGEBOX("Trend: three or less records are selected")
  RETURN .F.
 ENDIF
 ALTER table (ali) ADD tmx I ADD tpnts C(2)
 REPLACE tmx WITH VAL(CHRTRAN(tm, ':', '')) ALL
 * Try to understand a kind of trend (D, U or S)
 * Try D trend first
 STORE 0 TO dtVlD, mvMVlD, dtVlD2, mvMVlD2, dtVlU, mvMVlU, dtVlU2, mvMVlU2
 tLD = trnd('d', @dtVlD, @mvMVlD, @dtVlD2, @mvMVlD2)
 * Try U trend next
 REPLACE tpnts WITH '' ALL
 tLU = trnd('u', @dtVlU, @mvMVlU, @dtVlU2, @mvMVlU2)
 IF tLU > tLD
*!*   tk = 'U'
  dtVlT = dtVlU
  mvMVlT = mvMVlU
  dtVlT2 = dtVlU2
  mvMVlT2 = mvMVlU2
 ELSE
*!*   tk = 'D'
  dtVlT = dtVlD
  mvMVlT = mvMVlD
  dtVlT2 = dtVlD2
  mvMVlT2 = mvMVlD2
 ENDIF
 f = FCREATE(fn)
 FPUTS(f, fndTmxVl(TRANSFORM(dtVlT)))
 FPUTS(f, TRANSFORM(mvMVlT - mvBs))
 FPUTS(f, fndTmxVl(TRANSFORM(dtVlT2)))
 FPUTS(f, TRANSFORM(mvMVlT2 - mvBs))
 FCLOSE(f)
 RETURN .T.
ENDFUNC && nldTrnd
*
* Выгрузка скользящих средних
FUNCTION nldMFls(ali, dVl, n, mvBs)
 LOCAL ali2, tbl2, k2
 ali2 = 'nldMF'
 tbl2 = _tmpPth + ali2
 SELECT mV, vlm, tmx;
  FROM (ali);
  WHERE dy = dVl AND mV > 0;
  INTO TABLE (tbl2)
 rCnt = _TALLY
 IF rCnt < n + 1
  RETURN .F.
 ENDIF
 fn3 = _prjPth + 'sma.txt'
 fn4 = _prjPth + 'ema.txt'
 fn5 = _prjPth + 'vma.txt'
 f3 = FCREATE(fn3)
 f4 = FCREATE(fn4)
 f5 = FCREATE(fn5)
 emaVl = mV
 FOR k = 1 TO n - 1
  GO k
  tmxVl = fndTmxVl(tmx)
  FPUTS(f3, tmxVl + ' ' + TRANSFORM(mv - mvBs))
  FPUTS(f4, tmxVl + ' ' + TRANSFORM(mv - mvBs))
  FPUTS(f5, tmxVl + ' ' + TRANSFORM(mv - mvBs))
 NEXT
 FOR k = n TO rCnt
  k2 = k - n + 1
  smaVl = clcSMA('mV', k2, k)
  emaVl = clcEMA('mV', emaVl, k, n)
  vmaVl = clcSMA('mV * vlm', k2, k) / clcSMA('vlm', k2, k)
  GO k
  tmxVl = fndTmxVl(tmx)
  FPUTS(f3, tmxVl + ' ' + TRANSFORM(smaVl - mvBs))
  FPUTS(f4, tmxVl + ' ' + TRANSFORM(emaVl - mvBs))
  FPUTS(f5, tmxVl + ' ' + TRANSFORM(vmaVl - mvBs))
 NEXT
 FCLOSE(f3)
 FCLOSE(f4)
 FCLOSE(f5)
 RETURN .T.
ENDFUNC && nldMFls
*
* Выгрузка объемом продаж
FUNCTION nldVlms(ali, dVl, mVBs)
 LOCAL fn, f
 SELECT (ali)
 CALCULATE MAX(vlm) TO vlmXVl FOR dy = dVl AND mV > 0
 CALCULATE MAX(mV - mVBs) TO mVXVl FOR dy = dVl AND mV > 0
 dvdVl = vlmXVl / mVXVl
 fn = _prjPth + 'vlms.txt'
 f = FCREATE(fn)
 SCAN FOR dy = dVl AND mV > 0
  FPUTS(f, fndTmxVl(tmx) + ' ' + TRANSFORM(vlm / dvdVl))
 ENDSCAN
 FCLOSE(f)
 RETURN .T.
ENDFUNC && nldVlms

Файл ttl.txt может иметь следующее наполнение:

gasp_O_23092011_Prices_0.5*(high+low)_ma(7)
140
1000
1840
6.44
12.85

Начальную часть файла prcs.txt с ценами актива иллюстрирует следующий пример:

1000 10.710
1005 10.330
1010 9.825

Линия тренда восстанавливается по файлу trnd.txt, содержащему описатель вида тренда (буква U, D или S соответственно для восходящего, нисходящего или бокового тренда) и начальные и конечные координаты линии.

U
1505
1.95
1725
2.880

Визуализация данных

Графический вывод обеспечивается средствами OpenGL и языка программирования Fortran.

!
! Модуль, задающий цифры, точку, знаки + и - и буквы x, y, z и e
module figures
  use opengl
  integer(4), parameter :: mli = 19, nli = 8
  ! mli - число символов (битовых образов) в массиве rasterfont
  ! nli - размер знакоместа
  integer(1), dimension(nli) :: letter  ! Массив для одного символа
  integer(1) :: pointplace = 12  ! Позиция точки в массиве
  ! Точка выводится более компактно. Для ее поиска применяется pointplace
  ! rasterfont - массив символов с ASCII-кодами цифр и букв x, y, z и e
  integer(1), dimension(mli, nli) :: rasterfont = reshape((/  &
   #3c, #66, #c3, #c3, #c3, #c3, #66, #3c,  &  ! 0  (48)
   #7e, #18, #18, #18, #18, #78, #38, #18,  &  ! 1  (49)
   #ff, #60, #30, #18, #0c, #06, #e7, #7e,  &  ! 2  ...
   #7e, #e7, #03, #03, #3e, #03, #e7, #7e,  &  ! 3
   #0c, #0c, #0c, #ff, #cc, #6c, #3c, #1c,  &  ! 4
   #7e, #c3, #03, #03, #fe, #c0, #c0, #ff,  &  ! 5
   #7e, #c3, #c3, #c3, #fe, #c0, #c3, #7e,  &  ! 6
   #30, #30, #30, #18, #0c, #06, #03, #ff,  &  ! 7
   #7e, #c3, #c3, #c3, #3c, #c3, #c3, #7e,  &  ! 8
   #7e, #e7, #03, #03, #7f, #c3, #c3, #7e,  &  ! 9  (57)
   #00, #00, #00, #3f, #3f, #00, #00, #00,  &  ! -  (45)
   #00, #38, #38, #00, #00, #00, #00, #00,  &  ! .  (46)
   #7c, #c2, #c0, #fc, #c6, #c6, #7c, #00,  &  ! e  (101 = 69 + 32)
   #00, #18, #18, #ff, #ff, #18, #18, #00,  &  ! +  (43)
   #c6, #6c, #38, #38, #6c, #c6, #00, #00,  &  ! x  (120)
   #60, #30, #18, #1c, #36, #63, #c3, #00,  &  ! y  (121)
   #fe, #60, #30, #18, #0c, #fe, #00, #00,  &  ! z  (122)
   #00, #00, #00, #00, #00, #00, #00, #00,  &  ! ' '  (32)
   #00, #38, #38, #00, #00, #38, #38, #00  &  ! : (58)
   /), shape = (/ mli, nli /), order = (/ 2, 1 /))
  real(4) :: spx = 8.0, spy = 8.0  ! x и y-размеры знакоместа
contains
  ! Модуль figures включает процедуры:
  ! makeFigures - формирует списки команд вывода текста
  ! printString - выводит строку текста
  ! Создаем списки команд, используемые затем при выводе наборов символов
  ! Выводимый набор символов задается в виде строки,
  ! например: '0123456789xyz+-.e'
  subroutine makeFigures()
   integer(4) :: i, sg
   ! Начало координат битового образа
   real(4) :: xorigx, yorigx, xorigy, yorigy
   ! Выравнивание по одному байту
   call fglPixelStorei(gl_unpack_alignment, 1)
   ! Генерируем 3*m списков команд - по 3 списка на каждый заданный массивом
   ! rasterfont символ. Первый список для x-координат, второй - для y-координат
   xorigx = 14.0; yorigx = 15.0
   xorigy = 14.0; yorigy = 1.0
   sg = 1
   do i = 1, mli
    letter = rasterfont(i, :)
    call details(i, sg, xorigx, yorigx)
    call details(i + mli, -sg, xorigy, yorigy)
   end do
  contains
   ! Внутренняя процедура подпрограммы makeFigures
   ! Детализирует различия в способах вывода x- и y-координат
   subroutine details(k, sg, xorig, yorig)
    integer(4) :: k, sg  ! k - номер списка; sg = +1 или -1
    real(4) :: xorig, yorig  ! Начало координат битового образа
    call fglNewList(k, gl_compile)
    if(i == pointplace) then
      ! Способ вывода точки
      call fglBitmap(spx, spy, xorig + sg, yorig, sg * (spx - 1.0), 0.0, loc(letter))
     else
      ! Способ вывода x-координат, если sg = +1, и y-координат, если sg = -1
      call fglBitmap(spx, spy, xorig, yorig, sg * (spx + 1.0), 0.0, loc(letter))
     end if
   call fglEndList( )
   end subroutine details
  end subroutine makeFigures
  subroutine printString(s, gt)  ! Вывод строки текста
   character(*) s
   character(1) gt  ! gt = 'x', 'y'
   integer(2), dimension(len_trim(s)) :: ars
   integer(2) :: lens, i, k
   lens = len_trim(s)
   if(gt == 'x') then
    if(s(1:1) > '1') then
     s = s(1:1) // ':' // s(2:3)
    else
     s = s(1:2) // ':' // s(3:4)
    endif
   endif
   do i = 1, lens  ! Формируем массив номеров команд, которые
    k = int2(ichar(s(i:i)))  ! нужно выполнить для вывода строки с текстом
    if(k > 47 .and. k < 58) then  ! Цифры 0, 1, ..., 9
      ars(i) = k - 47
    else if(k == 45 .or. k == 46) then
      ars(i) = k - 34  ! Знаки - и .
    else if(k == 58) then
      ars(i) = 19  ! Двоеточие
    else if(k == 69) then  ! Буква e
      ars(i) = 13
    else if(k == 43) then
      ars(i) = 14  ! Знак +
    else if(k > 119 .and. k < 123) then  ! Буквы x, y и z
      ars(i) = k - 105
    else
      ars(i) = 18  ! Пробел
    end if
   end do
   if(gt == 'y') ars = ars + mli
   call fglPushAttrib(gl_list_bit)  ! Сохраняем атрибуты изображения
   ! Номер выполняемой команды будет равен ars(i)
   call fglCallLists(lens, gl_short, loc(ars))
   call fglPopAttrib( )  ! Восстанавливаем атрибуты изображения
  end subroutine printString
end module figures
!
! Модуль points содержит процедуры:
! initGL - выполняет инициализацию окна OpenGL
! setVars - вычисляет протяженность осей координат и шаги координатной сетки
! fgrid - находит шаг координатной сетки и область ее отображения по оси x или y
! gridt - подпрограмма, выводящая линии координатной сетки и их координаты
module points
  use figures  ! Содержит ссылку на модуль opengl.mod
  ! Число выводимых графиков
  integer(4) :: kF
  character(1), allocatable, dimension(:) :: ifun
  ! Число используемых для вывода графика точек
  integer(4) :: npox
  ! gridval = .TRUE., если сетка отображается
  ! coordval = .TRUE., если выводятся координаты
  logical(4) :: gridval, coordval
  ! Шаги координатной сетки по осям x и y
  real(4) :: gridx, gridy
  ! Число шагов координатной сетки по осям x и y
  integer(4), parameter :: ngx = 5, ngy = ngx
  ! px, py - массивы, задающие выводимые кривые
  real(4), allocatable, dimension(:, :) :: px, py
  real(4) :: xl, xr, yb, yt  ! Протяженность осей координат
  ! Массив цветов для графиков функций
  real(4), allocatable, dimension(:, :) :: cols
  real(4) :: xmi1, xma1, ymi1, yma1
  logical(4) :: fzer = .true.  ! fzer = .false., когда 0.0 не выводится по оси y
  ! Образец для вывода координатной сетки (пунктир)
  integer(2) :: pattern = 2#0011001100110011
contains
  subroutine initGL(cmpn)  ! Инициализация окна OpenGL
   character(*) :: cmpn
   ! w, h - начальные размеры окна вывода
   integer(4) :: result = 0, w = 629, h = 191
   character(100) :: ttl
   ttl = trim(cmpn) // char(0)
   call fauxInitDisplayMode(aux_single .or. aux_rgb)
   call fauxInitPosition(100, 50, w, h)
   result = fauxInitWindow(ttl)
  end subroutine initGL
  subroutine setVars(xmi, xma, ymi, yma)
   real(4) :: xmi, xma, ymi, yma
   real(4) :: addx, addy, rk
   ! Вычисляем gridx и gridy - шаги координатной сетки по осям x и y
   xmi1 = xmi; xma1 = xma; ymi1 = ymi; yma1 = yma
   gridx = fgrid(xmi1, xma1, ngx)  ! gridx - шаг координатной сетки по оси x
   ! xmi1, xma1 - область отображения координатной сетки по оси x
   gridy = fgrid(ymi1, yma1, ngy)
   ! Координаты xmi1, xma1, ymi1, yma1 задают границы координатной сетки
   ! Координаты xl, xr, yb, yt используем для задания области вывода
   rk = 0.1
   addx = rk * (xma1 - xmi1)  ! Увеличиваем протяженность осей координат
   addy = rk * (yma1 - ymi1)  ! x и y соответственно на 2 * addx и 2 * addy
   xl = xmi1 - 1.4 * addx; xr = xma1 + 0.6 * addx
   yb = ymi1 - 1.2 * addy; yt = yma1 + 0.8 * addy
  end subroutine setVars
  ! fgrid - функция модуля points; содержит функцию tita, возвращающую
  ! границу области отображения координатной сетки
  ! Находит шаг координатной сетки и область ее отображения по оси x или y
  function fgrid(ti, ta, ng)
   real(4) :: fgrid, ti, ta
   integer(4) ng, k
   fgrid = (ta - ti) / float(ng)
   k = 0
   if(fgrid < 1.0) then
    do while(fgrid <= 1.0)
     k = k + 1
     fgrid = fgrid * 10.0
    end do
    fgrid = aint(fgrid) / float(10 ** k)
   else if(fgrid > 10.0) then
    do while(fgrid > 10.0)
     k = k + 1
     fgrid = fgrid / 10.0
    end do
    fgrid = aint(fgrid) * float(10 ** k)
   else
    fgrid = aint(fgrid)
   end if
   ! ti, ta - границы области отображения координатной сетки
   ti = tita(ti, 1)
   ta = tita(ta, 2)
   contains  ! tita - процедура функции fgrid
    function tita(t, k)  ! ti и ta должны быть кратны fgrid
     real(4) :: tita, t  ! t - граница области отображения сетки
     integer(4) :: k  ! k = 1, если рассматривается ti, k = 2, если - ta
     tita = 0.0
     if(t < 0.0) then
      do while(tita > t)
        tita = tita - fgrid
      end do
      if(k == 2) tita = tita + fgrid
     else
      do while(tita < t)
        tita = tita + fgrid
      end do
      if(k == 1) tita = tita - fgrid
     end if
    end function tita
  end function fgrid
  ! Подпрограмма, выводящая линии координатной сетки и их координаты
  ! Содержит процедуры:
  ! fgs - находит шаг координатной сетки в оконных координатах
  ! outString - формирует строку strin,
  ! которая содержит координаты выводимой линии
  ! makeStrin - формирует строку, которая используется для вывода координат сетки
  subroutine gridt(ti1, ta1, ti2, ta2, ti3, ta3, gt, grid)
    real(4) :: ti1, ta1, ti2, ta2, ti3, ta3  ! Протяженность осей координат
    character(1) :: gt ! Вид линий: 'x' или 'y'
    ! strin - строка, содержащая координату линии координатной сетки
    character(10) :: strin
    ! grid - шаг координатной сетки
    real(4) :: t, pt1(3), pt2(3), pt3(3), grid
    logical(4) :: fl
    integer(4) :: gs  ! Шаг сетки в оконных координатах
    integer(4) :: tcur, tlast  ! tlast - координата последнего вывода текста
    ! Применяется для предотвращения наложения строк,
    ! содержащих координаты сетки. Перекрытие строк может возникнуть при
    ! незначительной ширине (высоте) окна вывода
    gs = fgs( )
    tlast = 0
    tcur = 0  ! Текущая позиция вывода текста
    ! Находим положение первой выводимой линии координатной сетки
    t = ti1 - grid
    do while(t < ta1)  ! Вывод линий координатной сетки
      fl = .true.  ! Истина, если линии выводятся
      t = t + grid  ! Если gt = 'x', то выводятся x-линии
      !!if(abs(t) < 0.9 * grid) fl = .false.
      if(gt == 'x') then
        pt1 = (/ t, ti2, ti3 /)
        pt2 = (/ t, ta2, ti3 /)
      else if(gt == 'y') then
         pt1 = (/ ti2, t, ti3 /)
         pt2 = (/ ta2, t, ti3 /)
      end if
      if(coordval) call outString( )  ! Вывод координат или имени оси координат
      if(fl) then
       call fglBegin(gl_lines)  ! Вывод очередной линии координатной сетки
       call fglVertex3fv(loc(pt1))
       call fglVertex3fv(loc(pt2))
       call fglEnd( )
      end if
    end do
  contains
   ! fgs, outString и makeStrin - внутренние процедуры подпрограммы gridt
   ! fgs - находит шаг координатной сетки в оконных координатах
   function fgs( )
    integer(4) :: fgs, k  ! k - номер измерения
    real(8), dimension(4, 4) :: modelMatrix, projMatrix
    integer(4), dimension(4) :: viewport
    real(8) :: win1(2), win2(2), pt(4), ptn(4)
    call fglGetDoublev(gl_modelview_matrix, loc(modelMatrix))
    call fglGetDoublev(gl_projection_matrix, loc(projMatrix))
    call fglGetIntegerv(gl_viewport, loc(viewport))
    select case(gt)
    case('x'); k = 1; pt = (/ ti1, ti2, ti3, 1.0 /)
    case('y'); k = 2; pt = (/ ti2, ti1, ti3, 1.0 /)
    end select
    ptn = matmul(matmul(projMatrix, modelMatrix), pt)
    win1 = (ptn(1:2) + 1.0_8) * dfloat((viewport(3:4) - viewport(1:2)) / 2)
    pt(k) = pt(k) + real(grid, 8)
    ptn = matmul(matmul(projMatrix, modelMatrix), pt)
    win2 = (ptn(1:2) + 1.0_8) * dfloat((viewport(3:4) - viewport(1:2)) / 2)
    win1 = win2 - win1
    fgs = int(sqrt(sum(win1 * win1)))
   end function fgs
   ! outString - внутренняя процедура подпрограммы gridt
   subroutine outString( )  ! Формирует и выводит строку с текстом
     integer(4) :: slen  ! Длина строки с выводимым текстом
     ! Формируем строку strin, которая содержит координаты выводимой линии
     if(abs(t) > 0.1 * grid) then
       call makeStrin(strin, t, gt)
     else  ! Выводится координата 0.0
       strin = '0.0'
     end if
     tcur = tcur + gs  ! Текущая позиция вывода текста
     ! Если не нужен вывод 0.0 по оси y
     if(.not. fzer .and. gt == 'y' .and. strin == '0.0') return
     if(tlast > 0) then  ! Если выводится не первая координата
      slen = len_trim(strin)
      ! Проверим, можно ли разместить новый текст без перекрытий с предшествующим
      if(gt == 'x') then
        if(tcur - 2 * slen * spx < tlast) return
      else  ! gt = 'y'
        if(tcur - 3 * spy < tlast) return
      end if
     end if
     tlast = tcur  ! Текст можно разместить без перекрытий
     ! Вывод сформированной строки strin
     ! Растровая позиция первой цифры строки strin
     call fglRasterPos2fv(loc(pt1))
     ! Вывод координат
     call printString(trim(strin), gt)
   end subroutine outString
   ! makeStrin - внутренняя процедура подпрограммы gridt
   ! Формирует строку, которая используется для вывода координат сетки
   subroutine makeStrin(strin, t, gt)
    character(*) strin, gt
    real(4) :: t
    ! Максимальное число позиций для координат - 5; минимальное - 3
    real(4) :: ctpmin = 0.0099, ctmmax = -0.099
    ! Если 0.0010 <= t <= 9999., то используется формат F
    ! Если -999. <= t <= -0.010, то также используется формат F
    ! Если 9999. < t <= .9e+9), используется формат E5.1e1
    ! Если t = 0, то возвращается '0.0'
    ! Во всех остальных случаях возвращается пробел
    integer(4), parameter :: np = 7, nm = 5
    integer(4) :: k, i, j  ! Номер выбранного в массиве fmt формата
    character(1) :: ch
    character(10), dimension(np) ::  &
     fmtp = (/ '(F5.4)', '(F4.3)', '(F3.2)', '(F3.1)', '(F3.0)', '(F4.0)', '(F5.0)' /)
    character(10), dimension(nm) :: fmtm = (/ '(F5.3)', '(F4.2)', '(F4.1)', '(F4.0)', '(F5.0)' /)
    character(10) :: fmt
    fmt = ' '
    if(t >= 0.00095 .and. t < 10000. .or. t >= -999. .and.  t <= -0.0095) then
     k = 1
     if(t > 0) then
       ! (Минимальное положительное значение) * 10 для формата F
       ct = ctpmin
       do while(ct <= t .and. k < np)
        k = k + 1
        ct = ct * 10
       end do
     else
       ! (Максимальное отрицательное значение) / 10 для формата F
       ct = ctmmax
       do while(ct > t .and. k < nm)
        k = k + 1
        ct = ct * 10
       end do
     end if
     ! if(t >= 0.0010 .and. t < 0.0100) fmt = '(F5.4)'
     ! if(t >= 0.010 .and. t < 0.100) fmt = '(F4.3)'
     ! if(t >= 0.10 .and. t < 1.00) fmt = '(F3.2)'
     ! if(t >= 1.0 .and. t < 10.0) fmt = '(F3.1)'
     ! if(t >= 10.0 .and. t < 100.0) fmt = '(F3.0)'
     ! if(t >= 100.0 .and. t < 1000.0) fmt = '(F4.0)'
     ! if(t >= 1000.0 .and. t < 10000.0) fmt = '(F5.0)'
     !
     ! if(t <= -0.010 .and. t > -0.100) fmt = '(F5.3)'
     ! if(t <= -0.10 .and. t > -1.00) fmt = '(F5.2)'
     ! if(t <= -1.0 .and. t > -10.0) fmt = '(F4.1)'
     ! if(t <= -10. .and. t > -100.) fmt = '(F4.0)'
     ! if(t <= -100. .and. t > -1000.) fmt = '(F5.0)'
     if(t > 0) then
      fmt = fmtp(k)
     else
      fmt = fmtm(k)
     end if
    else if(t > 0.0 .and. t <= .9e+9) then
      fmt = '(e5.1e1)'
    else if(abs(t) < tiny(t)) then
      strin = '0.0'
      return
    else
      strin = ' '
      return
    end if
    write(strin, fmt) t  ! Преобразование число - строка
    ! Меняем порядок следования символов в строке
    if(gt == 'y') then
     k = len_trim(strin)
     do i = 1, k / 2
      ch = strin(i:i)
      j = k - i + 1
      strin(i:i) = strin(j:j)
      strin(j:j) = ch
     end do
    end if
   end subroutine makeStrin
  end subroutine gridt
end module points
!
! Содержит интерфейсы подпрограмм myReshape1 и display1
! Подпрограммы myReshape1 и display1 должны обладать атрибутом EXTENAL
module GLface2
  interface
   subroutine myReshape1(w, h)
    !ms$ attributes stdcall, alias : '_myReshape1@8' :: myReshape1
    integer(4) :: w, h
   end subroutine myReshape1
   subroutine display1( )
    !ms$ attributes stdcall, alias : '_display1@0' :: display1
   end subroutine display1
  end interface
end module GLface2
!
! Вывод графика цены и линии тренда или индикатора ТА
program curve
  use points
  implicit none
  character(100) :: pth = 'c:\FoxMicex\'
  character(250) :: fnKF = 'kFFls.txt', fnTtl = 'ttl.txt', fn
  character(100) cmpn, bs
  character(10), allocatable, dimension(:) :: kFFls
  character(1) :: st
  integer :: k, n
  real :: vlxN, vlxX, vlyN = huge(1.0), vlyX = -huge(1.0)
  fnKF = trim(pth) // trim(fnKF)
  fnTtl = trim(pth) // trim(fnTtl)
  ! Title file
  open(1, file = fnTtl)
  read(1, *) cmpn
  read(1, *) bs
  read(1, *) vlxN
  read(1, *) vlxX
  close(1)
  ! Types of unloaded files
  open(1, file = fnKF)
  kF = 0
  do while(.NOT. eof(1))
   kF = kF + 1
   read(1, *) st
   read(1, *) st
  end do
  allocate(kFFls(kF), ifun(kF), cols(4, kF))
  rewind 1
  kF = 0
  do while(.NOT. eof(1))
   kF = kF + 1
   read(1, *) kFFls(kF)
   read(1, *) ifun(kF), cols(:, kF)
  end do
  close(1)
  fn = trim(pth) // trim(kFFls(1)) // '.txt'
  open(1, file = fn)
  ! npox - число точек, используемых при выводе графика
  npox = 0
  do while(.NOT. eof(1))
   npox = npox + 1
   read(1, *) st
  end do
  close(1)
  allocate(px(npox, kF), py(npox, kF))
  do k = 1, kF
   if(ifun(kF) /= 'N') call fllNVl( )
  end do
  ! Вывод выполняется на белом фоне; оси координат выводятся черным цветом
  cmpn = trim(cmpn) // '; base = ' // trim(bs)
  call drawCurve(cmpn, vlxN, vlxX, vlyN, vlyX, .true., .true.)
contains
  subroutine fllNVl( )
   real y
   integer m
   fn = trim(pth) // trim(kFFls(k)) // '.txt'
   open(1, file = fn)
   m = 0
   do while(.NOT. eof(1))
    m = m + 1
    read(1, *) px(m, k), y
    py(m, k) = y
    if(ifun(k) == 'F' .and. y == 0.0) cycle
    vlyN = min(vlyN, y)
    vlyX = max(vlyX, y)
   end do
   close(1)
  end subroutine fllNVl
end program curve
!
! Подпрограмма вывода графиков функций одной переменной
subroutine drawCurve(cmpn, xmi, xma, ymi, yma, grid, coords)
  use msfwin
  use points
  use GLface2  ! Содержит интерфейсы myReshape1 и display1
  character(*) :: cmpn
  real(4) :: xmi, xma, ymi, yma  ! Диапазоны изменения координат
  logical(4) :: grid, coords  ! grid = .TRUE., когда отображается сетка
  real(4) :: x, y, dx  ! coords = .TRUE., когда выводятся координаты
  integer(4) :: i, j
  gridval = grid
  coordval = coords  ! coordval = .TRUE., если выводятся координаты
  ! Вычисляем протяженность осей координат и шаги координатной сетки (см. points.f90)
  call setVars(xmi, xma, ymi, yma)
  ! Смотрим, нужно ли выводить 0.0 по оси y (не нужно, если нижний левый угол
  ! графика - это начало координат)
  if(abs(xmi1) < tiny(xmi1) .and. abs(ymi1) < tiny(ymi1)) fzer = .false.
  call initGL(cmpn)  ! Выводятся графики вида y = f(x)  (см. points.f90)
  ! Формирование списков команд, пригодных для вывода текста (см. figures.f90)
  call makeFigures()
  ! При изменении размеров окна вызывается подпрограмма myReshape1
  call fauxReshapeFunc(loc(myReshape1))
  ! Подпрограмма display1 выполняет роль оконной процедуры
  ! Вызывается каждый раз при перемещении и изменении размеров окна вывода
  ! и выводит оси координат, координатную сетку и графики функций
  call fauxMainLoop(loc(display1))
  deallocate(px, py)
end subroutine drawCurve
!
subroutine myReshape1(w, h)
  !ms$ attributes stdcall, alias : '_myReshape1@8' :: myReshape1
  use points  ! Подпрограмма, формирующая матрицу проецирования
  integer(4) :: w, h
  call fglViewport(0, 0, w, h)  ! Задание видового порта
  call fglMatrixMode(gl_projection); call fglLoadIdentity( )
  call fgluOrtho2D(xl, xr, yb, yt)
end subroutine myReshape1
!
! Выводит оси координат, координатную сетку и графики функций
subroutine display1( ) ! Вывод изображения
  !ms$ attributes stdcall, alias : '_display1@0' :: display1
  use points
  integer(4) :: i, j
  real(4) :: cc(3), x, y, y2
  logical chClr
  call fglClearColor(1.0, 1.0, 1.0, 1.0)  ! Цвет фона - белый
  call fglClear(gl_color_buffer_bit)  ! Очистка буфера цвета
  call fglColor3f(0.0, 0.0, 0.0)  ! Текущий цвет - черный
  call fglLineWidth(1.0)
  if(gridval) then  ! Вывод координатной сетки по образцу
   call fglLineStipple(2, pattern)  ! Повторяем каждый бит образца 2 раза
   ! Теперь вывод линии будет выполняться с применением образца
   call fglEnable(gl_line_stipple)
   call gridt(xmi1, xma1, ymi1, yma1, 0.0, 0.0, 'x', gridx)
   call gridt(ymi1, yma1, xmi1, xma1, 0.0, 0.0, 'y', gridy)
   call fglDisable(gl_line_stipple)
  end if
  if(1 == 0) then
   call fglBegin(gl_lines)  ! Вывод осей координат (отключен)
    call fglVertex2f(xmi1, 0.0)
    call fglVertex2f(xma1, 0.0)
    call fglVertex2f(0.0, ymi1)
    call fglVertex2f(0.0, yma1)
   call fglEnd( )
   call fglRasterPos2f(xma1, 0.0)  ! Вывод имени оси x
   letter = rasterfont(15, :)
   call fglBitmap(spx, spy, 10.0, -4.0, 0.0, 0.0, loc(letter))
   call fglRasterPos2f(0.0, yma1)  ! Вывод имени оси y
   letter = rasterfont(16, :)
   call fglBitmap(spx, spy, -4.0, 10.0, 0.0, 0.0, loc(letter))
  end if
  ! Вывод на экран осей координат и сетки
  call fglFlush( )
  ! Вывод графиков функций
  do j = 1, kF
   if(ifun(j) == 'S' .or. ifun(j) == 'N') cycle
   ! cc - текущий цвет
   cc = cols(1:3, j)
   call fglColor3fv(loc(cc))
   select case(ifun(j))
   case('T')
    call fglLineWidth(1.0)
    call fglBegin(gl_lines)
    call fglVertex2f(px(1, j), py(1, j))
    call fglVertex2f(px(2, j), py(2, j))
    call fglEnd( )
    ! Отображение графика на экране
    call fglFlush( )
   case('L')
    call fglLineWidth(1.0)
    call fglBegin(gl_line_strip)
    do i = 1, npox
     call fglVertex2f(px(i, j), py(i, j))
    end do
    call fglEnd( )
    call fglFlush( )
   case('D')
    call fglLineStipple(2, pattern)
    call fglEnable(gl_line_stipple)
    call fglLineWidth(1.0)
    call fglBegin(gl_line_strip)
    do i = 1, npox
     call fglVertex2f(px(i, j), py(i, j))
    end do
    call fglEnd( )
    call fglFlush( )
    call fglDisable(gl_line_stipple)
   case('P')
    call fglPointSize(2.0)
    call fglBegin(gl_points)
     do i = 1, npox
      call fglVertex2f(px(i, j), py(i, j))
     end do
    call fglEnd( )
    call fglFlush( )
   case('C')  ! Columns from y = 0
    chClr = cols(4, j) == 1.0
    call fglLineWidth(2.0)
    do i = 1, npox
     x = px(i, j)
     y = py(i, j)
     call fglBegin(gl_lines)
      call fglVertex2f(x, 0.0)
      call fglVertex2f(x, y)
     call fglEnd( )
     if(chClr) call stCrrntClr(i)
    end do
    call fglFlush( )
   case('V')  ! Verticle lines
    call fglLineWidth(2.0)
    do i = 1, npox
     x = px(i, j)
     call fglBegin(gl_lines)
      call fglVertex2f(x, py(i, j))
      call fglVertex2f(x, py(i, j + 1))
     call fglEnd( )
    end do
    call fglFlush( )
   case('F')  ! Fractals
    call fglPointSize(5.0)
    call fglBegin(gl_points)
     do i = 1, npox
      if(py(i, j) == 0.0) cycle
      call fglVertex2f(px(i, j), py(i, j))
     end do
    call fglEnd( )
    call fglFlush( )
   end select
  end do
contains
   subroutine stCrrntClr(k)
    integer k
    if(k < npox) then
     y = py(k, j)
     y2 = py(k + 1, j)
     if(y < y2) then
      cc = (/ 0.0, 1.0, 0.0 /)
     elseif(y > y2) then
      cc = (/ 1.0, 0.0, 0.0 /)
     else
      cc = (/ 0.0, 0.0, 1.0 /)
     end if
     call fglColor3fv(loc(cc))
    end if
   end subroutine stCrrntClr
end subroutine display1

Используется платформа Compaq Visual Fortran 6.6a. В меню Tools – Options – Directories – Include files указана директория C:\Program Files\Microsoft Visual Studio\DF98\Include. В том же меню для Library files указана директория C:\Program Files\Microsoft Visual Studio\DF98\Lib.
С деталями и примерами программирования на Фортране с использованием OpenGL можно познакомиться в [5-7].

Линии поддержки и сопротивления

Линия Поддержки (ЛП, Support Line) на заданном временном промежутке проходит через точку с минимальной ценой (рис. 7) и, наоборот, Линия Сопротивления (ЛС, Resistance Line) на том же временном промежутке проходит через точку с максимальной ценой.

Показан коридор поддержки актива

Рис. 7. Линии поддержки и сопротивления

Эти линии образуют коридор поддержки (КП), в пределах которого изменяется цена актива. При незначительной волатильности можно надеяться, что границы продолженного в будущее коридора не будут нарушены. Тогда при приближении цены к ЛП трейдер вправе ожидать последующего роста цены актива и, наоборот, при подходе к ЛС цена актива, скорее всего, начнет в скором будущем снижаться.
Если же цена варьируется вблизи границы КП, а затем выходит за его пределы (пробивает ЛП или ЛС), то можно предположить, что наблюдаемый тренд обладает значительной силой. Поэтому в случае пробития ЛП (нисходящий тренд) трейдер получает сигнал к продаже актива, а в случае восходящего тренда (пробита ЛС) – сигнал на покупку актива.
КП может быть использован трейдером для задания защитных стоп-приказов.
Положение ЛП и ЛС постоянно меняется под действием разных факторов:

Если после пробоя ЛП некоторое время сохраняется нисходящий тренд, постепенно переходящий в боковой, то ЛП превращается в ЛС, а новое положение ЛП определяется по ценам актива, наблюдаемым после пробоя ЛП. То же справедливо и для ЛС, которая будучи пробитой может далее рассматриваться как ЛП.
Пробой КП может носить временный характер, и цены вновь окажутся в пределах коридора. Возврат цен в КП может быть, в частности, обусловлен сомнениями трейдеров в обоснованности пробоя КП. Так, возврат в КП можно наблюдать, если при его пробое объем торгов был незначителен. В период осмысления, наступающий через некоторое время после пробоя КП, объем торгов ненадолго может вырасти. Однако если сомнения в обоснованности новой цены возобладают, то цена вернется в КП. При этом, если была пробита ЛП, то прервется нисходящий тренд, и в минусе окажутся медведи, а если – ЛС, то прервется восходящий тренд, и проиграют быки.
Если же пробой произошел на больших объемах, то тренд окажется, скорее всего, устойчивым, и возврата в прежний КП не будет.

Форма отладки программ вычисления индикаторов ТА

Отладка процедур расчета индикаторов ТА поддерживается приведенной на рис. 8 формой.

Вычисление значений индикаторов технического анализа

Рис. 8. Форма отладки процедур вычисления индикаторов ТА

После нажатия на кнопку с именем индикатора ТА, выполняется расчет индикатора выбранного актива в пределах указанной даты и если установлен флажок Выгружать, то формируется необходимое число файлов с рассчитанными значениями. Выгруженные данные употребляются для построения графика индикатора.

Вычисление индикаторов ТА

Вспомогательные процедуры

Вычисление индикатора предваряет обращение к функции prpsT, извлекающей необходимые расчетные данные.
Результаты расчета переносятся процедурой svMVDtFls в mVDt-файлы для последующей визуализации.

*
* Подготовка к расчету индикатора ТА
FUNCTION prpsT(oForm, pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt, n)
 WITH oForm
  PDtVl = .PDt.Value
  IDSVl = .IDSlct
  opgACVl = .OpgAC.Value
  opgACVl2 = .OpgAC2.Value
  chckNldVl = .ChckNld.Value
  DO CASE
  CASE opgACVl2 = 2
   slctVl = 'hgh'
   prcKndVl = 'High_'
  CASE opgACVl2 = 3
   slctVl = 'lw'
   prcKndVl = 'Low_'
  CASE opgACVl2 = 4
   slctVl = 'cls'
   prcKndVl = 'Close_'
  CASE opgACVl2 = 5 OR opgACVl2 < 2
   slctVl = '0.5 * (hgh + lw)'
   prcKndVl = 'Median_'
  CASE opgACVl2 = 6
   slctVl = '(hgh + lw + cls) / 3.0'
   prcKndVl = 'Typical_'
  ENDCASE
 ENDWITH
 DIMENSION rrMV[3]
 rrMV[1] = 'S'
 rrMV[2] = 'E'
 rrMV[3] = 'V'
 prcKndVl = prcKndVl + rrMV[opgACVl] + 'MA'
 pDtVl2 = pDtVl - IIF(DOW(pDtVl) = 2, 3, 1)
 ali0 = 'stcksTrdngF'
 ali = 'crStcksTrdng'
 tbl = _tmpPth + ali
 SELECT &slctVl AS mV, pn, cls, hgh, lw, vlm, DAY(dt) AS dy,;
  TRANSFORM(HOUR(dt)) + ":" + IIF(MINUTE(dt) > 5, TRANSFORM(MINUTE(dt)),;
   '0' + TRANSFORM(MINUTE(dt))) AS tm;
  FROM (ali0);
  WHERE IDS = IDSVl;
   AND TTOD(dt) BETWEEN pDtVl2 AND pDtVl;
  ORDER BY dt;
  INTO TABLE (tbl)
 rCnt = _TALLY
 IF rCnt < n
  MESSAGEBOX("Number of selected records < n = " + TRANSFORM(n))
  RETURN .F.
 ENDIF
 RETURN .T.
ENDFUNC && prpsT
*
* Выгрузка индикатора ТА в mVDt-файлы (режим отладки)
PROCEDURE svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, ttlVl, prcKndVl, gTp, n, arrFlds, sBsVl, llVl)
 LOCAL k, kFFlsWrdVl, mvBsVl, mVDtVl, flNmVl
 IF chckNldVl
  * Kind of files
  kFFlsWrdVl = 'mVDt' + _NEXTLINE + gTp
  mvBsVl = nldMv(ali, IDSVl, pDtVl, 'ttl2.txt', ttlVl, prcKndVl, arrFlds[1], sBsVl, 0, _prjPth + 'mVDt.txt', llVl)
  FOR k = 2 TO n
   mVDtVl = 'mVDt' + TRANSFORM(k)
   kFFlsWrdVl = kFFlsWrdVl + _NEXTLINE + mVDtVl + _NEXTLINE + gTp
   flNmVl = _prjPth + mVDtVl + '.txt'
   nldMv(ali, IDSVl, pDtVl, '', '', '', arrFlds[k], .F., mVBsVl, flNmVl, llVl)
  NEXT
  * Create file kFFls2.txt with types of unloaded files
  STRTOFILE(kfFlsWrdVl, _prjPth + 'kFFls2.txt')
  MESSAGEBOX('mvDt-file' + IIF(n = 1, ' is', 's are') + ' created')
 ENDIF
 GO TOP
 BROWSE LAST
ENDPROC && svMVDtFls

Организация материала

Описание берется из указанных в конце работы источников и приводится при необходимости к следующей схеме:

Далее приводятся программа, обеспечивающая расчет графика индикатора и его выгрузку в текстовый файл (в некоторых случаях формируются несколько файлов). Предваряется заголовком Программа.
Часть программ оформлены в виде процедур, имеющих параметр hwVl (принимает значения 1 или 2). Вызов с hwVl = 1 выполняется из формы отладки кода расчета индикатора, вызов с hwVl = 2 выполняется из формы оценки эффективности индикатора.
При описании индикатора ТА употребляются ранее введенные обозначения.
В формулах вычисления индикаторов ТА нередко используются скользящее среднее значение. В таких формулах на месте скользящего среднего употребляется имя МА, означающее либо SMA, либо EMA, либо VMA.
Прочие обозначения поясняются по месту описания индикатора.

Аллигатор (Alligator)

Аллигатор (Alligator) Б. Вильямса (B. Williams) – это комбинация трех линий баланса Челюсть, Зубы и Губы Аллигатора (Alligators jaw, teeth и lips, рис. 9).

Челюсть, Зубы и Губы индикатора технического анализа Аллигатор

Рис. 9. Аллигатор (график цены показан черным цветом)

По умолчанию челюсти Аллигатора отображаются синей, зубы – красной, а губы – зеленой линиями. Для первой линии по умолчанию используется 13-интервальная MA, смещенная на 8 баров в будущее, для второй – 8-интервальная MA, смещенная на 5 баров в будущее, а для третьей – 5-интервальная MA, смещенная на 2 бара в будущее. По умолчанию для всех трех линий берется медианная цена. График цены показан на рисунке черной линией.

Расчет:

ALLIGATORS_JAW = MA(M, B1, C1)
ALLIGATORS_TEETH = MA(M, B2, C2)
ALLIGATORS LIPS = MA(M, B3, C3),
где Bi – период сглаживания, Ci – сдвиг в будущее.

Использование:

С помощью Аллигатора можно определить направление тренда или его отсутствие.

Также Аллигатор помогает в определении разметки волн Эллиотта: если цена находится за пределами пасти Аллигатора, то возможно формирование импульсной волны, если – внутри пасти, то есть предпосылки для формирования корректирующей волны.

Программа (с заданными по умолчанию параметрами):

При вызове программы в результате нажатия на кнопку Fractals наряду с Аллигатором выводятся и Фракталы. Если же нажата кнопка Gator, то будет выведен Гатор осциллятор

* Click-обработчик кнопки Alligator
clcltLlgtr(thisForm, .F., .F., 1)

* Расчет и оценка индикатора Alligator
PROCEDURE clcltLlgtr(oFrm, frctlsVl, gtrVl, hwVl, thsVl)
 LOCAL pDtVl, IDSVl, opgACVl, slctVl, prcKndVl, ali, rCnt
 a1 = 13
 c1 = 8
 a2 = 8
 c2 = 5
 a3 = 5
 c3 = 3
 IF hwVl = 1
  LOCAL chckNldVl
  * See someprocs.prg
  prpsVl = prpsT(oFrm, @PDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, a1 + c1 + 1)
 ELSE
  LOCAL dsMntVl, smVl, kVl, fpVl
  prpsVl = prpsT2(oFrm, thsVl, @pDtVl, @dsMntVl, @IDSVl, @opgACVl, @slctVl, @prcKndVl, @ali, @rCnt, @smVl, @fpVl, @kVl, 2)
 ENDIF
 IF NOT prpsVl
  RETURN
 ENDIF
 ALTER TABLE (ali) ADD ajw N(8, 2) ADD ath N(8, 2) ADD alp N(8, 2)
 IF opgACVl = 2
  GO c1
  aJwVl = mV
  GO c2
  aThVl = mV
  GO c3
  aLpVl = mV
 ENDIF
 FOR k = a1 + c1 TO rCnt
  rStrt1 = k - a1 - c1 + 1
  rStrt2 = k - a2 - c2 + 1
  rStrt3 = k - a3 - c3 + 1
  rNd1 = rStrt1 + a1 - 1
  rNd2 = rStrt2 + a2 - 1
  rNd3 = rStrt3 + a3 - 1
  DO CASE
  CASE opgACVl = 1
   aJwVl = clcSMA('mV', rStrt1, rNd1)
   aThVl = clcSMA('mV', rStrt2, rNd2)
   aLpVl = clcSMA('mV', rStrt3, rNd3)
  CASE opgACVl = 2
   aJwVl = clcEMA('mV', aJwVl, rStrt1, a1)
   aThVl = clcEMA('mV', aThVl, rStrt2, a2)
   aLpVl = clcEMA('mV', aLpVl, rStrt3, a3)
  CASE opgACVl = 3
   aJwVl = clcSMA('mV * vlm', rStrt1, rNd1) / clcSMA('vlm', rStrt1, rNd1)
   aThVl = clcSMA('mV * vlm', rStrt2, rNd2) / clcSMA('vlm', rStrt2, rNd2)
   aLpVl = clcSMA('mV * vlm', rStrt3, rNd3) / clcSMA('vlm', rStrt3, rNd3)
  ENDCASE
  GO k
  REPLACE ajw WITH aJwVl ath WITH aThVl alp WITH aLpVl
 NEXT
 IF hwVl < 3
  nFldsVl = IIF(frctlsVl, 7, IIF(gtrVl, 2, 4))
 ELSE
  nFldsVl = 11
 ENDIF
 DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
 ttlVl = TRANSFORM(a1) + '_' + TRANSFORM(a2) + '_' + TRANSFORM(a3) + ')'
 IF NOT gtrVl
  arrFlds[1] = 'ajw'
  arrFlds[2] = 'ath'
  arrFlds[3] = 'alp'
  arrGTps[1] = 'L 0.0 0.0 1.0 0.0'
  arrGTps[2] = 'L 1.0 0.0 0.0 0.0'
  arrGTps[3] = 'L 0.0 1.0 0.0 0.0'
  ttlVl = 'Alligator(' + ttlVl
 ENDIF
 DO CASE
 CASE frctlsVl
  ALTER TABLE (ali) ADD fp N(8, 2) ADD fm N(8, 2)
  DIMENSION arrHVl[5], arrLVl[5]
  FOR k = 1 TO 5
   GO k
   arrHVl[k] = hgh
   arrLVl[k] = lw
  NEXT
  kFltVl = 0.0
  FOR k = 6 TO rCnt
   IF arrHVl[3] > arrHVl[1] AND arrHVl[3] > arrHVl[2] AND arrHVl[3] > arrHVl[4] AND arrHVl[3] > arrHVl[5]
    GO k - 3
    REPLACE fp WITH (1.0 + kFltVl) * hgh
   ENDIF
   IF arrLVl[3] < arrLVl[1] AND arrLVl[3] < arrLVl[2] AND arrLVl[3] < arrLVl[4] AND arrLVl[3] < arrLVl[5]
    GO k - 3
    REPLACE fm WITH (1.0 - kFltVl) * lw
   ENDIF
   FOR k2 = 1 TO 4
    arrHVl[k2] = arrHVl[k2 + 1]
    arrLVl[k2] = arrLVl[k2 + 1]
   NEXT
   GO k
   arrHVl[5] = hgh
   arrLVl[5] = lw
  NEXT
  arrFlds[4] = 'fp'
  arrFlds[5] = 'fm'
  arrFlds[6] = 'hgh'
  arrFlds[7] = 'lw'
  arrGTps[4] = 'F 1.0 0.0 0.0 0.0'
  arrGTps[5] = 'F 0.0 0.0 1.0 0.0'
  arrGTps[6] = 'V 0.0 0.0 0.0 0.0'
  arrGTps[7] = 'S 0.0 0.0 0.0 0.0'
  IF hwVl = 3
   arrFlds[8] = 'bsg'
   arrGTps[8] = 'V 1.0 0.0 0.0 0.0'
   arrFlds[9] = 'bsg2'
   arrGTps[9] = 'S 1.0 0.0 0.0 0.0'
   arrFlds[10] = 'ssg'
   arrGTps[10] = 'V 0.0 0.0 1.0 0.0'
   arrFlds[11] = 'ssg2'
   arrGTps[11] = 'S 0.0 0.0 1.0 0.0'
  ENDIF
  ttlVl = 'Fractals_' + ttlVl
 CASE gtrVl
  arrFlds[1] = 'ajw - ath'
  arrFlds[2] = 'alp - ath'
  arrGTps[1] = 'C 1.0 0.0 0.0 1.0'
  arrGTps[2] = 'C 1.0 0.0 0.0 1.0'
  ttlVl = 'Gator(' + ttlVl
 OTHERWISE
  arrFlds[4] = 'mV'
  arrGTps[4] = 'L 0.0 0.0 0.0 0.0'
 ENDCASE
 IF hwVl = 1
  svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., gtrVl)
 ELSE
  CALCULATE MAX(mV) TO mVSllVl
  IF hwVl = 3
   mxVl = mVSllVl
   CALCULATE MIN(mV) TO mnVl
   ALTER TABLE (ali) ADD bsg N(8, 2) ADD bsg2 N(8, 2) ADD ssg N(8, 2) ADD ssg2 N(8, 2)
   REPLACE bsg WITH mnVl bsg2 WITH mnVl ssg WITH mnVl ssg2 WITH mnVl ALL
  ELSE
   mxVl = ''
  ENDIF
  smVl2_2 = clcXptdPrft(smVl, dsMntVl, pDtVl, fpVl)
  smVl2 = smVl
  STORE 0 TO stcsMntVl, mntBsVl, mntSllsVl, fVl, smLftVl2
  LOCATE FOR dy = DAY(pDtVl)
  DO WHILE NOT EOF()
   fndNxtFrctl('fp', 'fm')
   IF NOT EOF() AND mntBsVl = mntSllsVl AND mV < ath
    IF kVl = 1
     oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
    ELSE
     IF stcsMntVl = 0 AND mV < mVSllVl
      oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
     ENDIF
    ENDIF
   ENDIF
   fndNxtFrctl('fm', 'fp')
   IF NOT EOF() AND mntSllsVl = mntBsVl - 1 AND mV > ath
    IF kVl = 1
     oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
    ELSE
     smTmpVl = mV * stcsMntVl + smLftVl2
     IF smTmpVl > smVl2
      oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
      mVSllVl = mV
     ENDIF
    ENDIF
   ENDIF
  ENDDO
  frmPdt(oFrm, stcsMntVl, smVl2, smLftVl2, mntBsVl, mntSllsVl, smVl, fVl, smVl2_2)
  svMVDtFls2(ali, IDSVl, pDtVl, dsMntVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps)
 ENDIF
ENDPROC && clcltLlgtr

PROCEDURE fndNxtFrctl(fldVl, fldVl2)
 LOCAL sFnd
 DO WHILE NOT EOF()
  DO WHILE NOT EOF() AND &fldVl = 0
   SKIP
  ENDDO
  sFnd = .T.
  kSkpVl = 0
  DO WHILE NOT EOF() AND kSkpVl < 3
   kSkpVl = kSkpVl + 1
   SKIP
   IF &fldVl > 0 OR &fldVl2 > 0
    sFnd = .F.
    EXIT
   ENDIF
  ENDDO
  IF sFnd
   RETURN
  ENDIF
 ENDDO
ENDPROC && fndNxtFrctl

Чудесный осциллятор (AO)

Чудесный осциллятор (Awesome Oscillator, АО) Б. Вильямса (B. Williams) помогает определить текущее поведение движущей силы рынка (рис. 10).

<Блюдце индикатора технического анализа Чудесный осциллятор

Рис. 10. AO: сигналы на покупку

Индикатор аналогичен индикатору MACD: АО, как и MACD, колеблется вокруг нулевой отметки, но в отличие от MACD не имеет сигнальной линии. Индикатор OSC – это АО, взятый с обратным знаком.

Расчет:

AO = MA(P, Shrt) – MA(P, Lng), где

Shrt – протяженность короткого временного промежутка, по умолчанию равна 5;
Lng – протяженность длинного временного промежутка, по умолчанию равна 34.
По умолчанию P – это M.

Использование:

AO показывает отношение движущей силы (изменения рыночного равновесия) рынка краткосрочного временного промежутка Shrt по сравнению с долгосрочным Lng.
Как и MACD, АО подает сигналы разворота текущего движения.
Сигнал АО на покупку образуется:

Сигналы на продажу зеркальны. Сигнал АО на продажу образуется:

Сигналы АО можно использовать совместно с сигналами индикатора АС. В этом случае можно употребить следующую тактику:

Эффективность:

Следование сигналам индикатора привело на тестовой выборке к сокращению портфеля.

Программа:

* Click-обработчик кнопки AO
clcltAO(thisForm, 1, 5, 34)

* Расчет и оценка индикаторов AO и OSC
PROCEDURE clcltAO(oFrm, hwVl, sVl, lVl, thsVl)
 LOCAL pDtVl, IDSVl, opgACVl, slctVl, prcKndVl, ali, rCnt
 csVl = 2
 IF hwVl < 3
  LOCAL chckNldVl
  prpsVl = prpsT(oFrm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, lVl)
 ELSE && Режим оценки AO
  LOCAL dsMntVl, smVl, kVl, fpVl
  prpsVl = prpsT2(oFrm, thsVl, @pDtVl, @dsMntVl, @IDSVl, @opgACVl, @slctVl, @prcKndVl, @ali, @rCnt, @smVl, @fpVl, @kVl, 2)
 ENDIF
 IF NOT prpsVl
  RETURN
 ENDIF
 ALTER TABLE (ali) ADD ao N(8, 2)
 IF opgACVl = 2
  GO sVl
  aSVl = mV
  GO lVl
  aLVl = mV
 ENDIF
 FOR k = lVl TO rCnt
  rStrt1 = k - sVl + 1
  rStrt2 = k - lVl + 1
  DO CASE
  CASE opgACVl = 1
   aSVl = clcSMA('mV', rStrt1, k)
   aLVl = clcSMA('mV', rStrt2, k)
  CASE opgACVl = 2
   aSVl = clcEMA('mV', aSVl, rStrt1, sVl)
   aLVl = clcEMA('mV', aLVl, rStrt2, lVl)
  CASE opgACVl = 3
   aSVl = clcSMA('mV * vlm', rStrt1, k) / clcSMA('vlm', rStrt1, k)
   aLVl = clcSMA('mV * vlm', rStrt2, k) / clcSMA('vlm', rStrt2, k)
  ENDCASE
  GO k
  REPLACE ao WITH IIF(hwVl = 2, aLVl - aSVl, aSVl - aLVl)
 NEXT
 nFldsVl = 1
 DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
 arrFlds[1] = 'ao'
 arrGTps[1] = 'C 1.0 0.0 0.0 1.0'
 ttlVl = IIF(hwVl = 2, '', 'Awesome_') + 'Oscillator(' + TRANSFORM(sVl) + '-' + TRANSFORM(lVl) + ')'
 IF hwVl < 3
  svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)
 ELSE
  svMVDtFls2(ali, IDSVl, pDtVl, dsMntVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps)
  *
  smVl2_2 = clcXptdPrft(smVl, dsMntVl, pDtVl, fpVl)
  CALCULATE MAX(mV) TO mVSllVl
  smVl2 = smVl
  STORE 0 TO stcsMntVl, mntBsVl, mntSllsVl, fVl, smLftVl2
  GO TOP
  DO WHILE NOT EOF()
   DO WHILE NOT EOF() AND NOT fndScndMnOrXCrssng('ao') && Buy signal
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl)
    ELSE
     IF stcsMntVl = 0 AND mV < mVSllVl
      oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl)
     ENDIF
    ENDIF
   ENDIF
   DO WHILE NOT EOF() AND NOT fndScndMnOrXCrssng('-ao') && Sell signal
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl)
    ELSE
     smTmpVl = mV * stcsMntVl + smLftVl2
     IF smTmpVl > smVl2
      oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl)
      mVSllVl = mV
     ENDIF
    ENDIF
   ENDIF
  ENDDO
  frmPdt(oFrm, stcsMntVl, smVl2, smLftVl2, mntBsVl, mntSllsVl, smVl, fVl, smVl2_2)
 ENDIF
ENDPROC && clcltAO
*
* Retuns .T. if buy / sell signal is found
FUNCTION fndScndMnOrXCrssng(fld)
 DO WHILE NOT EOF() AND &fld >= 0
  SKIP
 ENDDO
 IF NOT EOF()
  fldVl = &fld
  SKIP
  DO WHILE NOT EOF() AND &fld <= fldVl
   fldVl = &fld
   SKIP
  ENDDO
  IF NOT EOF() AND &fld < 0
   fldVl2 = fldVl
   DO WHILE NOT EOF() AND &fld < 0 AND &fld >= fldVl
    fldVl = &fld
    SKIP
    IF NOT EOF()
     IF &fld < 0
      DO WHILE NOT EOF() AND &fld <= fldVl
       fldVl = &fld
       SKIP
      ENDDO
      IF NOT EOF()
       IF &fld < 0
        RETURN fldVl > fldVl2
*!*        ELSE
*!*         RETURN .T.
       ENDIF
      ENDIF
*!*      ELSE
*!*       RETURN .T.
     ENDIF
    ENDIF
   ENDDO
  ENDIF
 ENDIF
 RETURN .F.
ENDFUNC && fndScndMnOrXCrssng

Осциллятор Ускорение/Замедление (AC)

Осциллятор Б. Вильямса (B. Williams) Ускорение/Замедление (Acceleration/Deceleration Oscillator, AC, рис. 11) измеряет ускорение/замедление движущей силы рынка (ДСР).

<Acceleration/Deceleration Oscillator

Рис. 11. AC-сигналы на покупку и на продажу "выше нулевой линии"

С помощью AO можно определить ДСР. AC информирует о динамике ДСР: AC положителен, если ДСР нарастает (ускорение), и отрицателен – в противном случае.

Расчет:

AC = AO – MA(AO, n)

Использование:

АС следует игнорировать, пока не появится первый фрактал на покупку (продажу) за пределами пасти Аллигатора.
Не следует покупать при замедлении и продавать при ускорении.
AC подает следующие сигналы:

Сигнал на покупку "выше нулевой линии" поступает, если появились два последовательных бара с более высокими значениями, чем последний наименьший бар (правое увеличение на рис. 11). Ордер Buy Stop размещается на 1 пипс выше максимума сигнального бара с.
Сигнал на продажу "выше нулевой линии" поступает, когда появились три последовательных бара с более низкими значениями, чем последний наибольший бар (см. левое увеличение на рис. 11). Ордер Sell Stop размещается на 1 пипс ниже минимума сигнального бара d.
Сигнал на покупку "ниже нулевой линии" подается, когда появились три последовательных бара с более высокими значениями, чем последний наименьший бар. Ордер Buy Stop размещается на 1 пипс выше максимума сигнального бара.
Сигнал на продажу "ниже нулевой линии" поступает, если появились два последовательных бара с более низкими значениями, чем последний наибольший бар. Ордер Sell Stop размещается на 1 пипс ниже минимума сигнального бара.
Если бар b или c лежит ниже нулевой линии, то c становится сигнальным.
Сигнал АС отменяется, если перед исполнением отложенного ордера гистограмма изменила цвет.

Эффективность:

Следование сигналам индикатора привело на тестовой выборке к сокращению портфеля.

Программа:

Обработчик нажатия на кнопку AC вызывает процедуру clcltAC:

* Click-обработчик кнопки AC
clcltAC(thisForm, 1)

* Расчет и оценка индикатора AC
PROCEDURE clcltAC(oFrm, hwVl, thsVl)
 LOCAL pDtVl, IDSVl, opgACVl, slctVl, prcKndVl, ali, rCnt
 sVl = 5
 lVl = 34
 IF hwVl = 1
  LOCAL chckNldVl
  prpsVl = prpsT(oFrm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, lVl)
 ELSE
  LOCAL dsMntVl, smVl, kVl, fpVl
  prpsVl = prpsT2(oFrm, thsVl, @pDtVl, @dsMntVl, @IDSVl, @opgACVl, @slctVl, @prcKndVl, @ali, @rCnt, @smVl, @fpVl, @kVl, 2)
 ENDIF
 IF NOT prpsVl
  RETURN
 ENDIF
 ALTER TABLE (ali) ADD ao N(8, 2) ADD ac N(8, 2)
 lVl2 = lVl - sVl
 IF opgACVl = 2
  GO lVl2 + 1
  aSVl = mV
  GO 1
  aLVl = mV
 ENDIF
 FOR k = lVl TO rCnt
  DO CASE
  CASE opgACVl = 1
   aSVl = clcSMA('mv', k - sVl + 1, k)
   aLVl = clcSMA('mv', k - lVl + 1, k)
  CASE opgACVl = 2
   aLVl = clcEMA('mv', aLVl, k, lVl)
   aSVl = clcEMA('mv', aSVl, k, sVl)
  CASE opgACVl = 3
   aSVl = clcSMA('mv * vlm', k - sVl + 1, k) / clcSMA('vlm', k - sVl + 1, k)
   aLVl = clcSMA('mv * vlm', k - lVl + 1, k) / clcSMA('vlm', k - lVl + 1, k)
  ENDCASE
  GO k
  REPLACE ao WITH aSVl - aLVl
 NEXT
 IF opgACVl = 2
  GO lVl + 1
  aSVl = ao
 ENDIF
 FOR k = lVl + sVl TO rCnt
  DO CASE
  CASE opgACVl = 1
   aSVl = clcSMA('ao', k - sVl + 1, k)
  CASE opgACVl = 2
   aSVl = clcEMA('ao', aSVl, k, sVl)
  CASE opgACVl = 3
   aSVl = clcSMA('ao * vlm', k - sVl + 1, k) / clcSMA('vlm', k - sVl + 1, k)
  ENDCASE
  GO k
  REPLACE ac WITH ao - aSVl
 NEXT
 nFldsVl = IIF(hwVl < 3, 1, 5)
 DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
 IF hwVl < 3
  arrFlds[1] = 'ac'
  arrGTps[1] = 'C 1.0 0.0 0.0 1.0'
 ELSE
  arrFlds[1] = 'mV'
  arrGTps[1] = 'L 0.0 0.0 0.0 0.0'
  arrFlds[2] = 'bsg'
  arrGTps[2] = 'V 1.0 0.0 0.0 0.0'
  arrFlds[3] = 'bsg2'
  arrGTps[3] = 'S 1.0 0.0 0.0 0.0'
  arrFlds[4] = 'ssg'
  arrGTps[4] = 'V 0.0 0.0 1.0 0.0'
  arrFlds[5] = 'ssg2'
  arrGTps[5] = 'S 0.0 0.0 1.0 0.0'
 ENDIF
 ttlVl = 'Accelerator_Decelerator_Oscillator(' + TRANSFORM(sVl) + '-' + TRANSFORM(lVl) + ')'
 IF hwVl = 1
  svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)
 ELSE
  CALCULATE MAX(mV) TO mVSllVl
  IF hwVl = 3
   mxVl = mVSllVl
   CALCULATE MIN(mV) TO mnVl
   ALTER TABLE (ali) ADD bsg N(8, 2) ADD bsg2 N(8, 2) ADD ssg N(8, 2) ADD ssg2 N(8, 2)
   REPLACE bsg WITH mnVl bsg2 WITH mnVl ssg WITH mnVl ssg2 WITH mnVl ALL
  ELSE
   mxVl = ''
  ENDIF
  smVl2_2 = clcXptdPrft(smVl, dsMntVl, pDtVl, fpVl)
  smVl2 = smVl
  STORE 0 TO stcsMntVl, mntBsVl, mntSllsVl, fVl, smLftVl2
  LOCATE FOR dy = DAY(pDtVl)
  DO WHILE NOT EOF()
   DO WHILE NOT EOF() AND NOT bSllSgnlSFnd('ac')
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
    ELSE
     IF stcsMntVl = 0 AND mV < mVSllVl
      oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
     ENDIF
    ENDIF
   ENDIF
   DO WHILE NOT EOF() AND NOT bSllSgnlSFnd('-ac')
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
    ELSE
     smTmpVl = mV * stcsMntVl + smLftVl2
     IF smTmpVl > smVl2
      oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
      mVSllVl = mV
     ENDIF
    ENDIF
   ENDIF
  ENDDO
  frmPdt(oFrm, stcsMntVl, smVl2, smLftVl2, mntBsVl, mntSllsVl, smVl, fVl, smVl2_2)
  svMVDtFls2(ali, IDSVl, pDtVl, dsMntVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps)
 ENDIF
ENDPROC && clcltAC
*
* Retuns .T. if buy / sell signal is found
FUNCTION bSllSgnlSFnd(fld)
 DO WHILE NOT EOF()
  fldVl0 = &fld
  SKIP
  IF NOT EOF()
   fldVl = &fld
   SKIP
   IF NOT EOF()
    fldVl2 = &fld
    IF fldVl < fldVl0 AND fldVl < fldVl2
     SKIP
     IF NOT EOF()
      fldVl3 = &fld
      IF fldVl3 > fldVl2
       IF fldVl > 0
        RETURN .T.
       ELSE
        SKIP
        RETURN NOT EOF() AND &fld > fldVl3
       ENDIF
      ENDIF
     ENDIF
    ENDIF
   ENDIF
  ENDIF
 ENDDO
 RETURN .F.
ENDFUNC && bSllSgnlSFnd

Индекс направления движения усредненной цены (ADX)

Индекс направления движения усредненной цены (Average Directional Movement Index, ADX, рис. 12) помогает определить направление движения среднего значения цены на основе индикаторов направленности +DI (индикатор позитивного направления движения цены, Positive Directional Index) и DI (индикатор негативного направления движения цены, Negative Directional Index), построенных по 14 интервалам.

Average Directional Movement Index, Positive Directional Index, Negative Directional Index

Рис. 12. ADX, +DI и DI

На рис. 12 ADX показан красным, +DI – зеленым, а DI – синим цветом.

Расчет:

В источниках имеются по крайней мере два способа расчета индикатора:
Один поддерживается следующей схемой:

  1. n = 14.
  2. Рассчитать в каждой точке j временной шкалы положительное и отрицательное направленное движение цены (Directional Movement, +/–DM) и ее истинный диапазон TR (True Range):
    +DMj = max(Hj – Hj - 1, 0); DMj = max(Lj - 1 – Lj, 0); TRj = max(Hj – Lj, |Hj – Cj - 1|, |Lj – Cj - 1|)
    (+DM1 = DM1 = 0; TR1 = H1 – L1).
  3. Сгладить значения +DM, DM и TR (выражение дается только для TR):
    TRn = sum(TRj, j = 1, n); TRk = TRk-1 – TRk-1 / n + TRk для k = n + 1, nMax, где nMax – номер последней отобранной записи.
  4. +DIk = 100 * +DMk / TRk; DIk = 100 * DMk / TRk
  5. DXk = 100 * |+DIkDIk| / (+DIk + DIk)
  6. ADX2*n = sum(DXj, j = n + 1, 2 * n); ADXi = (ADX i - 1 * (n – 1) + DX i) / n для i = 2 * n + 1, nMax

Второй способ имеет некоторые отличия:

  1. n = 14.
  2. +DMj = max(Hj – Hj - 1, 0); DMj = max(Lj - 1 – Lj, 0);
    DMj = 0, если +DMj > DMj; +DMj = 0, если DMj > +DMj; +DMj = DMj = 0, если +DMj = DMj;
    TRj = max(Hj – Lj, |Hj – Cj - 1|, |Lj – Cj - 1|) (+DM1 = DM1 = 0; TR1 = H1 – L1).
  3. +DIk = MA(+SDIj, n); DIk = MA(SDIj, n) для k = n + 1, nMax, где
    если TRj не 0, то +SDIj = +DMj / TRj; SDIj = DMj / TRj, иначе +SDIj = SDIj = 0.
  4. DXk = 100 * |+DIkDIk| / (+DIk + DIk)
  5. ADXi = MA(DXk, n) для i = 2 * n + 1, nMax.

Использование (см. также +/–DM):

Если рынок консолидируется, то +DM и DM будут равны нулю, сообщая об отсутствии движения. Чем больше разница между +DM и DM, тем сильнее тренд. Тесное переплетение +DM и DM говорит о незначительных колебаниях цены (о флэте).
Значения ADX находятся в диапазоне 0 – 100. Значение ниже 20 сигнализирует о слабом тренде, значение выше 40 – о сильном тренде (восходящем или нисходящем).
Сигналы ADX:

  1. Покупай, когда +DI пересекает DI снизу вверх.
  2. Продавай, когда DI пересекает +DI сверху вниз.
  3. Началом тренда может быть точка, в которой ADX пересекает 20 снизу вверх, а ослаблением тренда – точка, в которой ADX снижается ниже 40.

Сигнал на вход, поданный пересечением DI и +DI, нередко (актив находится в торговом диапазоне) бывает ложным.
ADX показывает наличие или отсутствие тренда, а для определения точек и направления входа следует использовать +/–DM или иные индикаторы.

Эффективность:

Следование сигналам индикатора привело на тестовой выборке к сокращению портфеля.

Программа (рассчитываются и выгружаются ADX, +DI и DI):

* Click-обработчик кнопки ADX
clcltADX(thisForm, 1, 14)

* Расчет и оценка индикаторов ADX и +/-DM
PROCEDURE clcltADX(oFrm, hwVl, n, thsVl)
 LOCAL pDtVl, IDSVl, opgACVl, slctVl, prcKndVl, ali, rCnt
 csVl = 2
 IF hwVl < 3
  LOCAL chckNldVl
  prpsVl = prpsT(oFrm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, 2 * n)
 ELSE && Режим оценки ADX
  LOCAL dsMntVl, smVl, kVl, fpVl
  prpsVl = prpsT2(oFrm, thsVl, @pDtVl, @dsMntVl, @IDSVl, @opgACVl, @slctVl, @prcKndVl, @ali, @rCnt, @smVl, @fpVl, @kVl, 2)
 ENDIF
 IF NOT prpsVl
  RETURN
 ENDIF
 ALTER TABLE (ali) ADD tr N(8, 2) ADD dmp N(8, 2) ADD dmm N(8, 2) ADD sdp2 N(8, 2)ADD sdm2 N(8, 2)
 GO 1
 clsVl = cls
 hghVl = hgh
 lwVl = lw
 REPLACE tr WITH hgh - lw
 FOR k = 2 TO rCnt
  GO k
  trVl = MAX(hgh - lw, ABS(hgh - clsVl), ABS(lw - clsVl))
  dmHVl = MAX(hgh - hghVl, 0)
  dmLVl = MAX(lwVl - lw, 0)
  IF csVl = 2
   DO CASE
   CASE dmHVl > dmLVl
    dmLVl = 0
   CASE dmLVl > dmHVl
    dmHVl = 0
   CASE dmLVl = dmHVl
    STORE 0 TO dmLVl, dmHVl
   ENDCASE
  ENDIF
  REPLACE tr WITH trVl dmp WITH dmHVl dmm WITH dmLVl;
   sdp2 WITH IIF(trVl = 0, 0, dmHVl / trVl) sdm2 WITH IIF(trVl = 0, 0, dmLVl / trVl)
  clsVl = cls
  hghVl = hgh
  lwVl = lw
 NEXT
 STORE 0.0 TO trNVl, dmpNVl, dmmNVl
 FOR k = 1 TO n
  GO k
  trNVl = trNVl + tr
  dmpNVl = dmpNVl + dmp
  dmmNVl = dmmNVl + dmm
 NEXT
 ALTER TABLE (ali) ADD dipN N(8, 2) ADD dimN N(8, 2) ADD dx N(8, 2)ADD adx N(8, 2);
  ADD dipN2 N(8, 2) ADD dimN2 N(8, 2) ADD dx2 N(8, 2)ADD adx2 N(8, 2)
 * Case 2
 IF opgACVl = 2
  STORE 0.0 TO dipNVl2, dimNVl2
  FOR k = 1 TO n
   GO k
   dipNVl2 = dipNVl2 + sdp2
   dimNVl2 = dimNVl2 + sdm2
  NEXT
  dipNVl2 = dipNVl2 / n
  dimNVl2 = dimNVl2 / n
 ENDIF
 * Case 1 & 2
 FOR k = n + 1 TO rCnt
  * Case 1
  GO k
  trN2Vl = trNVl - trNVl / n + tr
  dmpN2Vl = dmpNVl - dmpNVl / n + dmp
  dmmN2Vl = dmmNVl - dmmNVl / n + dmm
  dipNVl = 100 * dmpN2Vl / trN2Vl
  dimNVl = 100 * dmmN2Vl / trN2Vl
  dxVl = 100 * ABS(dipNVl - dimNVl) / (dipNVl + dimNVl)
  REPLACE dipN WITH dipNVl dimN WITH dimNVl dx WITH dxVl
  trNVl = trN2Vl
  dmpNVl = dmpN2Vl
  dmmNVl = dmmN2Vl
  * Case 2
  DO CASE
  CASE opgACVl = 1
   dipNVL2 = clcSMA('sdp2', k - n, k)
   dimNVL2 = clcSMA('sdm2', k - n, k)
  CASE opgACVl = 2
   dipNVL2 = clcEMA('sdp2', dipNVL2, k, n)
   dimNVL2 = clcEMA('sdm2', dimNVL2, k, n)
  CASE opgACVl = 3
   dipNVL2 = clcSMA('sdp2 * vlm', k - n, k) / clcSMA('vlm', k - n, k)
   dimNVL2 = clcSMA('sdm2 * vlm', k - n, k) / clcSMA('vlm', k - n, k)
  ENDCASE
  dxVl2 = ABS(100 * (dipNVL2 - dimNVL2) / (dipNVL2 + dimNVL2))
  REPLACE dipN2 WITH 100 * dipNVl2 dimN2 WITH 100 * dimNVl2 dx2 WITH dxVl2
 NEXT
 * Case 1
 adxVl = 0
 FOR k = n + 1 TO 2 * n
  GO k
  adxVl = adxVl + dx
 NEXT
 adxVl = adxVl / n
 FOR k = 2 * n + 1 TO rCnt
  GO k
  adxVl = (adxVl * (n - 1) + dx) / n
  REPLACE adx WITH adxVl
 NEXT
 * Case 2
 n2 = 2 * n
 IF opgACVl = 2
  adxVl2 = 0
  FOR k = n + 1 TO n2
   GO k
   adxVl2 = adxVl2 + dx2
  NEXT
  adxVl2 = adxVl2 / n
  REPLACE adx2 WITH adxVl2
 ENDIF
 FOR k = n2 + 1 TO rCnt
  kS = k - n + 1
  DO CASE
  CASE opgACVl = 1
   adxVl2 = clcSMA('dx2', kS, k)
  CASE opgACVl = 2
   adxVl2 = clcEMA('dx2', adxVl2, k, n)
  CASE opgACVl = 3
   adxVl2 = clcSMA('dx2 * vlm', kS, k) / clcSMA('vlm', kS, k)
  ENDCASE
  GO k
  REPLACE adx2 WITH adxVl2
 NEXT
 nFldsVl = IIF(MOD(hwVl, 2) = 1, 3, 2)
 DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
 dpNVl = 'dipN' + TRANSFORM(csVl)
 dmNVl = 'dimN' + TRANSFORM(csVl)
 arrFlds[1] = dpNVl
 arrFlds[2] = dmNVl
 arrGTps[1] = 'L 0.0 1.0 0.0 0.0'
 arrGTps[2] = 'L 0.0 0.0 1.0 0.0'
 IF nFldsVl = 3 && ADX
  arrFlds[3] = 'adx' + TRANSFORM(csVl)
  arrGTps[3] = 'L 1.0 0.0 0.0 0.0'
  ttlVl = 'Average_Directional_Movement + TRANSFORM(csVl) + (' + TRANSFORM(n) + ')'
 ELSE && DM
  ttlVl = 'Directional_Movement(' + TRANSFORM(n) + ')'
 ENDIF
 IF hwVl < 3
  svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .F.)
 ELSE
  svMVDtFls2(ali, IDSVl, pDtVl, dsMntVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps)
  *
  smVl2_2 = clcXptdPrft(smVl, dsMntVl, pDtVl, fpVl)
  CALCULATE MAX(mV) TO mVSllVl
  smVl2 = smVl
  STORE 0 TO stcsMntVl, mntBsVl, mntSllsVl, fVl, smLftVl2
  bWhnCrssBT(dpNVl, dmNVl, kVl, @smVl2, smVl, fpVl, @mntBsVl, @mntSllsVl, @stcsMntVl, @smLftVl2, @fVl, @mVSllVl, pDtVl)
  frmPdt(oFrm, stcsMntVl, smVl2, smLftVl2, mntBsVl, mntSllsVl, smVl, fVl, smVl2_2)
 ENDIF
ENDPROC && clcltADX

Индикатор Накопление/Распределение (A/D)

Индикатор Накопление/Распределение (Accumulation/Distribution, A/D) определяется изменением цены и объема (A/D является менее распространенным вариантом Индикатора Балансового Объема). Объем выступает в роли весового коэффициента при изменении цены: чем больше объем, тем значительнее вклад изменения цены в значение индикатора.

Accumulation/Distribution

Рис. 13. A/D

Расчет:

A/Di = (2 * Ci – Li – Hi) * Vi / (Hi – Li) + A/Di–1, где i – номер интервала.

Использование:

Рост индикатора означает накопление (покупку) ценной бумаги, поскольку подавляющая доля объема торгов связана с восходящим движением цен. Падение индикатора означает распределение (продажу) ценной бумаги, поскольку подавляющая доля объема торгов связана с нисходящим движением цен.
Несоответствие A/D с ценой предупреждает о надвигающемся изменении цены. Обычно в таких случаях ценовая тенденция изменяет свое движение в сторону движения индикатора. Например, если индикатор падает, а цена актива растет, то вскоре возможен ее разворот вниз.

Программа:

* Click-обработчик кнопки A/D
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, 1)
 RETURN
ENDIF
ALTER TABLE (ali) ADD cumad N(15, 2)
GO TOP
dhlVl = hgh - lw
cumadVl = IIF(dhlVl = 0, 0, (2.0 * cls - lw - hgh) * vlm / dhlVl)
FOR k = 2 TO rCnt
 GO k
 dhlVl = hgh - lw
 cumadVl = cumadVl + IIF(dhlVl = 0, 0, (2.0 * cls - lw - hgh) * vlm / dhlVl)
 REPLACE cumad WITH cumadVl
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'cumad'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Accumulation_Distribution', '', nFldsVl, @arrFlds, @arrGTps, .T., .T.)

Индекс товарного канала (CCI)

Индекс товарного канала (Commodity Channel Index, CCI) измеряет отклонение цены актива от его среднестатистической цены (рис. 14).

Commodity Channel Index

Рис. 14. CCI

Расчет:

CCIi = (Ti – MAi(T, n)) / (MDi * 0.015), где MDi = sum(|MAi(T, n) – Ti|) / n

Высокие значения индекса указывают на то, что цена серьезно превышает среднюю цену, а низкие – на то, что цена слишком занижена.

Использование:

  1. Выявление расхождения или схождения.
  2. В качестве индикатора перекупленности/перепроданности актива. Значения CCI выше 100 говорят о перекупленности (возможен корректирующий спад), а значения ниже –100 – о перепроданности (возможен корректирующий подъем).

Программа:

* Click-обработчик кнопки CCI
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 14
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 RETURN
ENDIF
ALTER TABLE (ali) ADD ma N(8, 2) ADD cci N(8, 2)
IF opgACVl = 2
 maVl = 0.0
 FOR k = 1 TO n - 1
  maVl = maVl + mV
 NEXT
 maVl = maVl / (n - 1)
ENDIF
FOR k = n TO rCnt
 kS = k - n + 1
  DO CASE
 CASE opgACVl = 1
  maVl = clcSMA('mV', kS, k)
 CASE opgACVl = 2
  maVl = clcEMA('mV', maVl, k, n)
 CASE opgACVl = 3
  maVl = clcSMA('mV * vlm', kS, k) / clcSMA('vlm', kS, k)
 ENDCASE
 GO k
 REPLACE ma WITH maVl
NEXT
n2 = 2 * n
FOR k = n2 + 1 TO rCnt
 mdVl = clcSMA('ABS(ma - mV)', k - n2 + 1, k)
 GO k
 REPLACE cci WITH (mV - ma) / (0.015 * mdVl)
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'cci'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Commodity_Channel_Index(' + TRANSFORM(n) + ')',;
 prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)

Осциллятор Чайкина (ChO)

Осциллятор Чайкина (Chaikin's Oscillator, ChO) – это разность скользящих средних индикатора A/D. Таким образом, ChO измеряет темп изменения A/D.

Chaikin's Oscillator

Рис. 15. ChO

Расчет:

ChO = MA(A/D, Shrt) – MA(A/D, Lng), где

Shrt – длительность короткого временного промежутка, по умолчанию равна 3,
Lng – длительность длинного интервала, по умолчанию равна 10.

Использование:

Сигналы на покупку.

  1. Цена достигает нового минимума, а значение ChO выше предыдущего минимума. Сигнал наиболее значим, когда актив находится в зоне перепроданности. Для синхронизации с аналогичным сигналом MACD покупка откладывается до пересечения ChO нулевой отметки снизу вверх.
  2. Цена находится в сильном восходящем тренде (измеряется иным индикатором), а ChO разворачивается снизу вверх в области отрицательных значений.

Сигналы на продажу.

  1. Цена достигает нового максимума, а значение ChO ниже предыдущего максимума. Сигнал наиболее значим, когда актив находится в зоне перекупленности. Полезно дождаться подтверждения сигнала, которое поступает при пересечении индикатором нулевой отметки сверху вниз.
  2. Цена находится в сильном нисходящем тренде (измеряется иным индикатором), а ChO разворачивается сверху вниз в области положительных значений.

Употребляется совместно с индикаторами торгового диапазона (конверты скользящих средних, полосы Боллинжера и др.) и с индикаторами, которые показывают зоны перекупленности и перепроданности (RSI, SO, CCI и др.). Автор индикатора полагает, что сигналы в направлении текущей тенденции всегда более надежны, чем сигналы противоположные существующему тренду.
Недостаток: трудно интерпретировать резкие движения индикатора.

Эффективность:

Следование сигналам индикатора привело на тестовой выборке к сокращению портфеля.

Программа:

* Click-обработчик кнопки ChO
clcltChO(thisForm, 1)

* Расчет и оценка индикатора ChO
PROCEDURE clcltChO(oFrm, hwVl, thsVl)
 LOCAL pDtVl, IDSVl, opgACVl, slctVl, prcKndVl, ali, rCnt
 shrt = 3
 lng = 10
 IF hwVl = 1
  LOCAL chckNldVl
  prpsVl = prpsT(oFrm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, lng)
 ELSE
  LOCAL dsMntVl, smVl, kVl, fpVl
  prpsVl = prpsT2(oFrm, thsVl, @pDtVl, @dsMntVl, @IDSVl, @opgACVl, @slctVl, @prcKndVl, @ali, @rCnt, @smVl, @fpVl, @kVl, 2)
 ENDIF
 IF NOT prpsVl
  RETURN
 ENDIF
 ALTER TABLE (ali) ADD cumad N(15, 2) ADD cho N(15, 2)
 GO TOP
 dhlVl = hgh - lw
 cumadVl = IIF(dhlVl = 0, 0, (2.0 * cls - lw - hgh) * vlm / dhlVl)
 FOR k = 2 TO rCnt
  GO k
  dhlVl = hgh - lw
  cumadVl = cumadVl + IIF(dhlVl = 0, 0, (2.0 * cls - lw - hgh) * vlm / dhlVl)
  REPLACE cumad WITH cumadVl
 NEXT
 STORE 0 TO maVl, maVl2
 IF opgACVl = 2
  FOR k = 2 TO lng - 1
   IF k < shrt
    maVl = maVl + cumad
   ENDIF
   maVl2 = maVl2 + cumad
  NEXT
  maVl = maVl / (shrt - 1)
  maVl2 = maVl2 / (lng - 1)
 ENDIF
 FOR k = shrt TO rCnt
  kS = k - shrt + 1
  kS2 = k - lng + 1
  DO CASE
  CASE opgACVl = 1
   maVl = clcSMA('cumad', kS, k)
   IF k >= lng
    maVl2 = clcSMA('cumad', kS2, k)
   ENDIF
  CASE opgACVl = 2
   maVl = clcEMA('cumad', maVl, k, shrt)
   IF k >= lng
    maVl2 = clcEMA('cumad', maVl2, k, lng)
   ENDIF
  CASE opgACVl = 3
   maVl = clcSMA('cumad * vlm', kS, k) / clcSMA('vlm', kS, k)
   IF k >= lng
    maVl2 = maVl = clcSMA('cumad * vlm', kS2, k) / clcSMA('vlm', kS2, k)
   ENDIF
  ENDCASE
  GO k
  REPLACE cho WITH maVl - maVl2
 NEXT
 nFldsVl = IIF(hwVl < 3, 1, 5)
 DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
 IF hwVl < 3
  arrFlds[1] = 'cho'
  arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
 ELSE
  arrFlds[1] = 'mV'
  arrGTps[1] = 'L 0.0 0.0 0.0 0.0'
  arrFlds[2] = 'bsg'
  arrGTps[2] = 'V 1.0 0.0 0.0 0.0'
  arrFlds[3] = 'bsg2'
  arrGTps[3] = 'S 1.0 0.0 0.0 0.0'
  arrFlds[4] = 'ssg'
  arrGTps[4] = 'V 0.0 0.0 1.0 0.0'
  arrFlds[5] = 'ssg2'
  arrGTps[5] = 'S 0.0 0.0 1.0 0.0'
 ENDIF
 ttlVl = 'Chaikin_Oscillator(' + TRANSFORM(shrt) + '_' + TRANSFORM(lng) +')'
 IF hwVl = 1
  svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)
 ELSE
  CALCULATE MAX(mV) TO mVSllVl
  IF hwVl = 3
   mxVl = mVSllVl
   CALCULATE MIN(mV) TO mnVl
   ALTER TABLE (ali) ADD bsg N(8, 2) ADD bsg2 N(8, 2) ADD ssg N(8, 2) ADD ssg2 N(8, 2)
   REPLACE bsg WITH mnVl bsg2 WITH mnVl ssg WITH mnVl ssg2 WITH mnVl ALL
  ELSE
   mxVl = ''
  ENDIF
  smVl2_2 = clcXptdPrft(smVl, dsMntVl, pDtVl, fpVl)
  smVl2 = smVl
  STORE 0 TO stcsMntVl, mntBsVl, mntSllsVl, fVl, smLftVl2
  LOCATE FOR dy = DAY(pDtVl)
  DO WHILE NOT EOF()
   DO WHILE NOT EOF() AND NOT fndNxtMnMx('cho', 'mV') && Buy signal
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
    ELSE
     IF stcsMntVl = 0 AND mV < mVSllVl
      oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
     ENDIF
    ENDIF
   ENDIF
   DO WHILE NOT EOF() AND NOT fndNxtMnMx('-cho', '-mV') && Sell signal
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
    ELSE
     smTmpVl = mV * stcsMntVl + smLftVl2
     IF smTmpVl > smVl2
      oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
      mVSllVl = mV
     ENDIF
    ENDIF
   ENDIF
  ENDDO
  frmPdt(oFrm, stcsMntVl, smVl2, smLftVl2, mntBsVl, mntSllsVl, smVl, fVl, smVl2_2)
  svMVDtFls2(ali, IDSVl, pDtVl, dsMntVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps)
 ENDIF
ENDPROC && clcltChO
*
* Buy (sell) signal
FUNCTION fndNxtMnMx(fld, fld2)
 fldVl = &fld
 SKIP
 DO WHILE NOT EOF() AND &fld >= fldVl && Move up
  fldVl = &fld
  SKIP
 ENDDO
 DO WHILE NOT EOF() AND &fld <= fldVl && Move down
  fldVl = &fld
  SKIP
 ENDDO
 fld2Vl = &fld2
 DO WHILE NOT EOF() AND &fld2 >= fld2Vl && Move up
  fld2Vl = &fld2
  SKIP
 ENDDO
 DO WHILE NOT EOF() AND &fld2 <= fld2Vl && Move down
  fld2Vl = &fld2
  SKIP
 ENDDO
 RETURN NOT EOF() AND &fld > fldVl
ENDFUNC && fndNxtMnMx

Полосы Боллинджера (BB)

Полосы Боллинджера (Bollinger Bands, BB) строятся вместе с графиком цены и представляют собой три линии, расстояние между которыми (ширина полос) пропорционально стандартному отклонению SD цены (рис. 16).

Bollinger Bands

Рис. 16. BB (график цены показан черным цветом)

Расчет:

BBLower = MA(P, n) – k * SD(P, n) – нижняя ограничительная линия;
ВВMiddle = MA(P, n) – средняя скользящая;
BBUpper = MA(P, n) + k * SD(P, n) – верхняя ограничительная линия;
SD = SQRT(sum(P – MA(P, n))^2) / n) – стандартное отклонение цены.
По умолчанию n = 20, k = 2.

Использование:

Ширина полос зависит от устойчивости рынка: полоса расширяется при нестабильности рынка и сужается при более устойчивых ценах.
Интерпретация индикатора:

Движение цен, начавшееся от одной границы BB, нередко достигает противоположной границы BB.

Эффективность:

Следование сигналам индикатора привело на тестовой выборке к сокращению портфеля.

Программа:

* Click-обработчик кнопки BB
clcltBB(thisForm, 1)

* Расчет и оценка индикатора BB
PROCEDURE clcltBB(oFrm, hwVl, thsVl)
 LOCAL pDtVl, IDSVl, opgACVl, slctVl, prcKndVl, ali, rCnt
 n = 20
 IF hwVl = 1
  LOCAL chckNldVl
  prpsVl = prpsT(oFrm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 ELSE
  LOCAL dsMntVl, smVl, kVl, fpVl
  prpsVl = prpsT2(oFrm, thsVl, @pDtVl, @dsMntVl, @IDSVl, @opgACVl, @slctVl, @prcKndVl, @ali, @rCnt, @smVl, @fpVl, @kVl, 2)
 ENDIF
 IF NOT prpsVl
  RETURN
 ENDIF
 kSDVl = 2
 ALTER TABLE (ali) ADD bbl N(8, 2) ADD bbm N(8, 2) ADD bbu N(8, 2)
 IF opgACVl = 2
  GO n
  bbmVl = mV
 ENDIF
 FOR k = n TO rCnt
  kS = k - n + 1
  * See someProcs.prg
  DO CASE
  CASE opgACVl = 1
   bbmVl = clcSMA('mV', kS, k)
  CASE opgACVl = 2
   bbmVl = clcEMA('mV', bbmVl, k, n)
  CASE opgACVl = 3
   bbmVl = clcSMA('mV * vlm', kS, k) / clcSMA('vlm', kS, k)
  ENDCASE
  GO k
  REPLACE bbm WITH bbmVl
 NEXT
 n2 = 2 * n
 FOR k = n2 TO rCnt
  kS = k - n + 1
  * See someProcs.prg
  sdVl = kSDVl * clcSD('mV', 'bbm', kS, k)
  GO k
  REPLACE bbl WITH bbm - sdVl bbu WITH bbm + sdVl
 NEXT
 nFldsVl = 4
 DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
 arrFlds[1] = 'bbl'
 arrFlds[2] = 'bbm'
 arrFlds[3] = 'bbu'
 arrFlds[4] = 'mV'
 arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
 arrGTps[2] = 'L 0.0 1.0 0.0 0.0'
 arrGTps[3] = 'L 0.0 0.0 1.0 0.0'
 arrGTps[4] = 'L 0.0 0.0 0.0 0.0'
 ttlVl = 'Bollinger_Bands(' + TRANSFORM(n) + ')'
 IF hwVl = 1
  svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps, .T., .F.)
 ELSE
  svMVDtFls2(ali, IDSVl, pDtVl, dsMntVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps)
  *
  smVl2_2 = clcXptdPrft(smVl, dsMntVl, pDtVl, fpVl)
  CALCULATE MAX(mV) TO mVSllVl
  smVl2 = smVl
  STORE 0 TO stcsMntVl, mntBsVl, mntSllsVl, fVl, smLftVl2
  GO TOP
  DO WHILE NOT EOF()
   DO WHILE NOT EOF() AND NOT bSllWhnCrrs('mV', 'bbl') && Buy signal
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl)
    ELSE
     IF stcsMntVl = 0 AND mV < mVSllVl
      oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl)
     ENDIF
    ENDIF
   ENDIF
   DO WHILE NOT EOF() AND NOT bSllWhnCrrs('mV', 'bbu') && Sell signal
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl)
    ELSE
     smTmpVl = mV * stcsMntVl + smLftVl2
     IF smTmpVl > smVl2
      oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl)
      mVSllVl = mV
     ENDIF
    ENDIF
   ENDIF
  ENDDO
  frmPdt(oFrm, stcsMntVl, smVl2, smLftVl2, mntBsVl, mntSllsVl, smVl, fVl, smVl2_2)
 ENDIF
ENDPROC && clcltBB
*
* Buy / sell when fld & fld2 are crossing
PROCEDURE bSllWhnCrrs(fld, fld2)
 DO WHILE NOT EOF()
  fldVl = &fld
  fld2Vl = &fld2
  SKIP
  IF NOT EOF()
   IF fldVl <= fld2Vl AND &fld >= &fld2 OR fldVl >= fld2Vl AND &fld <= &fld2
    RETURN .T.
   ENDIF
  ENDIF
 ENDDO
 RETURN .F.
ENDPROC && bSllWhnCrrs

Конверты (Envelopes)

Индикатор Конверты (Огибающие линии, Envelopes) включает две скользящие средние (MA). При этом одна МА строится со сдвигом вверх, а вторая – со сдвигом вниз. Огибающие линии (рис. 17) определяют верхнюю и нижнюю границы текущего торгового диапазона актива.

Огибающие линии

Рис. 17. Envelopes (график цены показан синим цветом)

Расчет:

LUp = MA(P, n) * (1 + k / 100) – верхняя линия;
LDown = MA(P, n) * (1 – k / 100) – нижняя линия
По умолчанию n = 20, k = 2.

Использование:

Сигнал продажи формируется, когда цена достигает верхней границы, а покупки – при достижении нижней границы. Оптимальное значение сдвига k зависит от волатильности актива: чем больше волатильность, тем выше сдвиг.

Эффективность:

Следование сигналам индикатора привело на тестовой выборке к сокращению портфеля.

Программа:

* Click-обработчик кнопки Envelopes
clcltNvlps(thisForm, 1)

* Расчет и оценка индикатора Envelopes
PROCEDURE clcltNvlps(oFrm, hwVl, thsVl)
 LOCAL pDtVl, IDSVl, opgACVl, slctVl, prcKndVl, ali, rCnt
 n = oFrm.SpinnerPrd.Value
 kSD = 0.015
 IF hwVl = 1
  LOCAL chckNldVl
  prpsVl = prpsT(oFrm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 ELSE
  LOCAL dsMntVl, smVl, kVl, fpVl
  prpsVl = prpsT2(oFrm, thsVl, @pDtVl, @dsMntVl, @IDSVl, @opgACVl, @slctVl, @prcKndVl, @ali, @rCnt, @smVl, @fpVl, @kVl, 2)
 ENDIF
 IF NOT prpsVl
  RETURN
 ENDIF
 ALTER TABLE (ali) ADD ml N(8, 2) ADD mu N(8, 2)
 IF opgACVl = 2
  GO n
  maVl = mV
 ENDIF
 FOR k = n TO rCnt
  kS = k - n + 1
  * See someProcs.prg
  DO CASE
  CASE opgACVl = 1
   maVl = clcSMA('mV', kS, k)
  CASE opgACVl = 2
   maVl = clcEMA('mV', maVl, k, n)
  CASE opgACVl = 3
   maVl = clcSMA('mV * vlm', kS, k) / clcSMA('vlm', kS, k)
  ENDCASE
  dVl = maVl * kSD
  GO k
  REPLACE ml WITH maVl - dVl mu WITH maVl + dVl
 NEXT
 nFldsVl = IIF(hwVl < 3, 3, 7)
 DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
 arrFlds[1] = 'ml'
 arrFlds[2] = 'mu'
 arrFlds[3] = 'mV'
 arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
 arrGTps[2] = 'L 0.0 1.0 0.0 0.0'
 arrGTps[3] = 'L 0.0 0.0 0.0 0.0'
 IF hwVl = 3
  arrFlds[4] = 'bsg'
  arrGTps[4] = 'V 1.0 0.0 0.0 0.0'
  arrFlds[5] = 'bsg2'
  arrGTps[5] = 'S 1.0 0.0 0.0 0.0'
  arrFlds[6] = 'ssg'
  arrGTps[6] = 'V 0.0 0.0 1.0 0.0'
  arrFlds[7] = 'ssg2'
  arrGTps[7] = 'S 0.0 0.0 1.0 0.0'
 ENDIF
 ttlVl = 'Envelopes(' + TRANSFORM(n) + ')'
 IF hwVl = 1
  svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps, .T., .F.)
 ELSE
  CALCULATE MAX(mV) TO mVSllVl
  CALCULATE MIN(mV) TO mnVl
  IF hwVl = 3
   mxVl = mVSllVl
   ALTER TABLE (ali) ADD bsg N(8, 2) ADD bsg2 N(8, 2) ADD ssg N(8, 2) ADD ssg2 N(8, 2)
   REPLACE bsg WITH mnVl bsg2 WITH mnVl ssg WITH mnVl ssg2 WITH mnVl ALL
  ELSE
   mxVl = ''
  ENDIF
  smVl2_2 = clcXptdPrft(smVl, dsMntVl, pDtVl, fpVl)
  smVl2 = smVl
  STORE 0 TO stcsMntVl, mntBsVl, mntSllsVl, fVl, smLftVl2
  dstVl = 0.1 * (mVSllVl - mnVl)
  mssDStrtVl = oFrm.CheckMssDStrt.Value
  LOCATE FOR dy = DAY(pDtVl)
  DO WHILE NOT EOF()
   DO WHILE NOT EOF() AND NOT prcNrBrdr('ml', dstVl, mssDStrtVl)
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
    ELSE
     IF stcsMntVl = 0 AND mV < mVSllVl
      oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
     ENDIF
    ENDIF
   ENDIF
   DO WHILE NOT EOF() AND NOT prcNrBrdr('mu', dstVl, mssDStrtVl)
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
    ELSE
     smTmpVl = mV * stcsMntVl + smLftVl2
     IF smTmpVl > smVl2
      oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
      mVSllVl = mV
     ENDIF
    ENDIF
   ENDIF
  ENDDO
  frmPdt(oFrm, stcsMntVl, smVl2, smLftVl2, mntBsVl, mntSllsVl, smVl, fVl, smVl2_2)
  svMVDtFls2(ali, IDSVl, pDtVl, dsMntVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps)
 ENDIF 
ENDPROC && clcltNvlps
*
* Buy (sell) signal
FUNCTION prcNrBrdr(fld, dstVl, mssDStrtVl)
 DO WHILE NOT EOF()
  IF mssDStrtVl
   DO WHILE DELETED()
    SKIP
   ENDDO
  ENDIF
  SKIP
  IF ABS(&fld - mV) < dstVl
   RETURN .T.
  ENDIF
 ENDDO
 RETURN .F.
ENDFUNC && prcNrBrdr

Скользящее среднее цены (MA)

Скользящее среднее (Moving Average, MA) используется как самостоятельный индикатор (рис. 18).

Simple Moving Average

Рис. 18. SMA (график цены показан зеленым цветом)

Использование:

Сигналы на покупку поступают, когда цена актива превышает MA.
Сигналы на продажу подаются, когда цена находится ниже MA.
MA не находит минимум и максимум цены, чтобы подать сигнал о входе в рынок или выходе из него. С помощью МА можно начать покупать (продавать) при приближении к текущему ценовому минимума (максимуму).

Эффективность:

Следование сигналам индикатора привело на тестовой выборке к сокращению портфеля.

Программа:

Замечание. Приводимая ниже процедура clcltMA также вызывается при нажатии на кнопки SROC и PCU.

* Click-обработчик кнопки MA
clcltMA(thisForm, 1)

* Расчет и оценка индикатора MA
PROCEDURE clcltMA(oFrm, hwVl, srcVl, lVl, uVl, thsVl)
 LOCAL pDtVl, IDSVl, opgACVl, slctVl, prcKndVl, ali, rCnt
 prdVl = oFrm.SpinnerPrd.Value
 IF hwVl < 4
  LOCAL chckNldVl
  prpsVl = prpsT(oFrm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, prdVl)
 ELSE
  LOCAL dsMntVl, smVl, kVl, fpVl
  prpsVl = prpsT2(oFrm, thsVl, @pDtVl, @dsMntVl, @IDSVl, @opgACVl, @slctVl, @prcKndVl, @ali, @rCnt, @smVl, @fpVl, @kVl, 2)
 ENDIF
 IF NOT prpsVl
  RETURN
 ENDIF
 IF hwVl = 3 && PCU
  ALTER TABLE (ali) ADD lCh N(8, 2) ADD uCh N(8, 2)
 ELSE
  ALTER TABLE (ali) ADD ma N(8, 2)
  IF hwVl = 2 && SROC
   ALTER TABLE (ali) ADD sroc N(8, 4)
  ENDIF
 ENDIF
 k2Vl = IIF(hwVl = 2, 2, 1)
 LOCAL k24
 FOR k24 = 1 TO k2Vl
  n = prdVl - IIF(k24 = 2, srcVl, 0)
  IF opgACVl = 2
   maVl = 0.0
   FOR k = 1 TO n - 1
    maVl = maVl + mV
   NEXT
   maVl = maVl / (n - 1)
  ENDIF
  FOR k = n TO rCnt
   kS = k - n + 1
   DO CASE
   CASE opgACVl = 1
    maVl = clcSMA('mV', kS, k)
   CASE opgACVl = 2
    maVl = clcEMA('mV', maVl, k, n)
   CASE opgACVl = 3
    maVl = clcSMA('mV * vlm', kS, k) / clcSMA('vlm', kS, k)
   ENDCASE
   GO k
   DO CASE
   CASE hwVl = 3 && PCU
    REPLACE lCh WITH maVl * (1 - lVl) uCh WITH maVl * (1 + uVl)
   CASE k24 = 1
    REPLACE ma WITH maVl
   CASE k24 = 2 && SROC
    REPLACE sroc WITH ma / maVl * 100
   ENDCASE
  NEXT
 NEXT
 DO CASE
 CASE hwVl = 1 OR hwVl > 3 && MA
  nFldsVl = IIF(hwVl = 5, 6, 2)
  DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
  arrFlds[1] = 'ma'
  arrFlds[2] = 'mV'
  arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
  arrGTps[2] = 'L 0.0 0.0 0.0 0.0'
  ttlVl = 'Moving_Average(' + TRANSFORM(prdVl) + ')'
  IF hwVl = 5
   arrFlds[3] = 'bsg'
   arrGTps[3] = 'V 1.0 0.0 0.0 0.0'
   arrFlds[4] = 'bsg2'
   arrGTps[4] = 'S 1.0 0.0 0.0 0.0'
   arrFlds[5] = 'ssg'
   arrGTps[5] = 'V 0.0 0.0 1.0 0.0'
   arrFlds[6] = 'ssg2'
   arrGTps[6] = 'S 0.0 0.0 1.0 0.0'
  ENDIF
 CASE hwVl = 2 && SROC
  nFldsVl = 1
  DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
  arrFlds[1] = 'sroc'
  arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
  ttlVl = 'Smoothed_Rate_Of_Change(' + TRANSFORM(prdVl) + '_' + TRANSFORM(srcVl)+ ')'
 CASE hwVl = 3 && PCU
  nFldsVl = 4
  DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
  arrFlds[1] = 'lCh'
  arrFlds[2] = 'uCh'
  arrFlds[3] = 'hgh'
  arrFlds[4] = 'lw'
  arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
  arrGTps[2] = 'L 0.0 1.0 0.0 0.0'
  arrGTps[3] = 'V 0.0 0.0 0.0 0.0'
  arrGTps[4] = 'S 0.0 0.0 0.0 0.0'
  ttlVl = 'Price_Channel_Upper(' + TRANSFORM(prdVl) + ')'
 ENDCASE
 IF hwVl < 4
  svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .F.)
 ELSE
  CALCULATE MAX(mV) TO mVSllVl
  IF hwVl = 5
   mxVl = mVSllVl
   CALCULATE MIN(mV) TO mnVl
   ALTER TABLE (ali) ADD bsg N(8, 2) ADD bsg2 N(8, 2) ADD ssg N(8, 2) ADD ssg2 N(8, 2)
   REPLACE bsg WITH mnVl bsg2 WITH mnVl ssg WITH mnVl ssg2 WITH mnVl ALL
  ELSE
   mxVl = ''
  ENDIF
  smVl2_2 = clcXptdPrft(smVl, dsMntVl, pDtVl, fpVl)
  smVl2 = smVl
  STORE 0 TO stcsMntVl, mntBsVl, mntSllsVl, fVl, smLftVl2
  dffrncVl = 0.0045
  LOCATE FOR dy = DAY(pDtVl)
  DO WHILE NOT EOF()
   DO WHILE NOT EOF() AND NOT bSllSgnlSFndMA('b', 1.0 + dffrncVl)
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
    ELSE
     IF stcsMntVl = 0 AND mV < mVSllVl
      oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
     ENDIF
    ENDIF
   ENDIF
   DO WHILE NOT EOF() AND NOT bSllSgnlSFndMA('s', 1.0 - dffrncVl)
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
    ELSE
     smTmpVl = mV * stcsMntVl + smLftVl2
     IF smTmpVl > smVl2
      oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
      mVSllVl = mV
     ENDIF
    ENDIF
   ENDIF
  ENDDO
  frmPdt(oFrm, stcsMntVl, smVl2, smLftVl2, mntBsVl, mntSllsVl, smVl, fVl, smVl2_2)
  svMVDtFls2(ali, IDSVl, pDtVl, dsMntVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps)
 ENDIF
ENDPROC && clcltMA
*
* Retuns .T. if buy / sell signal is found
FUNCTION bSllSgnlSFndMA(bSllVl, dffrncVl)
 DO WHILE NOT EOF()
  SKIP
  IF bSllVl = 'b'
   IF ma > dffrncVl * mV
    RETURN .T.
   ENDIF
  ELSE
   IF ma < dffrncVl * mV
    RETURN .T.
   ENDIF
  ENDIF
 ENDDO
 RETURN .F.
ENDFUNC && bSllSgnlSFnd

Волатильность Чайкина (ChV)

Индикатор Волатильность Чайкина (Chaikin's Volatility, ChV) оценивает расстояние между максимальной и минимальной ценами (рис. 19).

Chaikin's Volatility

Рис. 19. ChV

Расчет:

SP = H – L
CV = (MAi(SP, n) – MAi–n(SP, n)) / MAi–n(SP, n) * 100
По умолчанию n = 10.

Использование:

Резкий рост индикатора может означать, что цена приближается к своему минимуму; продолжительное снижение индикатора сигнализирует о росте цены актива. Рекомендуется подтверждать сигналы индикатора такими индикаторами, как MA и Envelopes.

Программа:

* Click-обработчик кнопки ChV
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 10
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 RETURN
ENDIF
ALTER TABLE (ali) ADD chv N(8, 2)
n2 = 2 * n
mVVl = '(hgh - lw)'
IF opgACVl = 2
 STORE 0 TO maVl, maVl2
 FOR k = 1 TO n - 1
  maVl = maVl + &mVVl
 NEXT
 maVl = maVl / (n - 1)
 maVl2 = 0.0
 FOR k = n TO n2 - 1
  maVl2 = maVl2 + &mVVl
 NEXT
 maVl2 = maVl2 / (n - 1)
ENDIF
FOR k = n2 TO rCnt
 kS = k - n2 + 1
 kS2 = k - n + 1
  DO CASE
 CASE opgACVl = 1
  maVl = clcSMA(mVVl, kS, kS + n - 1)
  maVl2 = clcSMA(mVVl, kS2, kS2 + n - 1)
 CASE opgACVl = 2
  maVl = clcEMA(mVVl, maVl, k - n, n)
  maVl2 = clcEMA(mVVl, maVl2, k, n)
 CASE opgACVl = 3
  maVl = clcSMA(mVVl + ' * vlm', kS, kS + n - 1) / clcSMA('vlm', kS, kS + n - 1)
  maVl2 = clcSMA(mVVl + ' * vlm', kS2, kS2 + n - 1) / clcSMA('vlm', kS2, kS2 + n - 1)
 ENDCASE
 GO k
 REPLACE chv WITH (maVl2 - maVl) / maVl * 100
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'chv'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Chaikin_Volatility(' + TRANSFORM(n) + ')',;
 'H-L_EMA', nFldsVl, @arrFlds, @arrGTps, .F., .F.)

Схождение/Расхождение скользящих средних (MACD линейный вариант)

Схождение/расхождение скользящих средних (Moving Average Convergence/Divergence, MACD) используется для оценки силы тренда, его направления и прогнозирования колебаний цен и точек разворота. Предложен Джеральдом Аппелем (Gerald Appel). Выводится в виде гистограммы и сигнальной линии (рис. 20).

Moving Average Convergence/Divergence

Рис. 20. MACD

Расчет:

MACD = МА(P, Lng) – MA(P, Shrt), выведен на рис. 20 в виде гистограммы;
MACD_Signal = MA(MACD, n), выведен на рис. 20 в виде линии
По умолчанию Lng = 26, Shrt = 12, n = 9.

Использование:

  1. Появление максимума или минимума на сигнальной линии означает, что, возможно, скоро последует сигнал MACD.
  2. Если MACD находится выше нулевой линии, то на рынке наблюдается восходящий тренд. Пересечение MACD нулевой линии снизу вверх дает сигнал на покупку. На силу сигнала влияет время пребывания MACD выше нулевой линии. Если MACD находился выше нулевой линии в течение продолжительного времени, а потом стал снижаться и пересек нулевую линию сверху вниз, то это рассматривается как сигнал на продажу. Если MACD находился в отрицательной зоне в течение нескольких месяцев, то подъем выше нуля и затем возврат ниже нулевой линии, скорее всего, говорит о временной коррекции.
  3. Если MACD находится ниже нулевой линии, то на рынке наблюдается нисходящий тренд. Пересечение MACD нулевой линии сверху вниз дает сигнал на продажу. На силу сигнала влияет время пребывания MACD ниже нулевой линии.
  4. Если формируется Бычье расхождение (новый максимум на графике цены выше предыдущего, а новый максимум на графике MACD ниже предыдущего), то это свидетельствует о слабости текущего восходящего тренда и вероятном скором развороте вниз. Рекомендуется получить подтверждающие сигналы иных индикаторов. Бычье расхождение может быть слабо подтверждено пересечением MACD своей сигнальной линии сверху вниз.
  5. Если формируется Медвежье схождение (новый минимум на графике цены ниже предыдущего, а новый минимум на графике MACD выше предыдущего, то это свидетельствует о слабости текущего нисходящего тренда и вероятном скором разворота вверх. Однако, как и в случае Бычьего расхождения, не стоит открывать позиций на покупку, не дождавшись подтверждений разворота тренда иными индикаторами ТА. Медвежье схождение может быть слабо подтверждено пересечением MACD своей сигнальной линии снизу вверх.
  6. Многократные расхождения (тройные и более Бычьи расхождения и Медвежьи схождения) трактуются так же, как и обычные, с той лишь разницей, что представляют собой более сильные сигналы MACD.
  7. Когда MACD находится ниже нуля и пересекает сигнальную линию снизу вверх и если нет медвежьего схождения, то наиболее вероятен рост цен.
  8. Когда MACD находится выше нуля и пересекает сигнальную линию сверху вниз и если нет бычьего расхождения, то наиболее вероятно падение цен.
  9. Если на рынке явно отсутствует тренд, то достижение максимума MACD говорит о том, что краткосрочные цены очень значительно отклонились от долгосрочных – это слабый сигнал на продажу. В случае если MACD обозначил свой минимум – это слабый сигнал на покупку. Сила этих сигналов пропорциональна величине максимума или минимума.

Программа:

* Click-обработчик кнопки MACD
PARAMETERS hstgtVl
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
sVl = 12
lVl = 20
n = 9
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, lVl)
 RETURN
ENDIF
ALTER TABLE (ali) ADD mcd N(8, 2) ADD sgnl N(8, 2)
IF opgACVl = 2
 GO sVl
 mcdSVl = mV
 GO lVl
 mcdLVl = mV
ENDIF
FOR k = lVl TO rCnt
 rStrt1 = k - sVl + 1
 rStrt2 = k - lVl + 1
 DO CASE
 CASE opgACVl = 1
  mcdSVl = clcSMA('mV', rStrt1, k)
  mcdLVl = clcSMA('mV', rStrt2, k)
 CASE opgACVl = 2
  mcdSVl = clcEMA('mV', mcdSVl, rStrt1, sVl)
  mcdLVl = clcEMA('mV', mcdLVl, rStrt2, lVl)
 CASE opgACVl = 3
  mcdSVl = clcSMA('mV * vlm', rStrt1, k) / clcSMA('vlm', rStrt1, k)
  mcdLVl = clcSMA('mV * vlm', rStrt2, k) / clcSMA('vlm', rStrt2, k)
 ENDCASE
 GO k
 REPLACE mcd WITH mcdLVl - mcdSVl
NEXT
IF opgACVl = 2
 GO lVl + n
 sgnlVl = mcd
ENDIF
FOR k = lVl + n TO rCnt
 rStrt1 = k - n + 1
 DO CASE
 CASE opgACVl = 1
  sgnlVl = clcSMA('mcd', rStrt1, k)
 CASE opgACVl = 2
  sgnlVl = clcEMA('mcd', sgnlVl, rStrt1, n)
 CASE opgACVl = 3
  sgnlVl = clcSMA('mcd * vlm', rStrt1, k) / clcSMA('vlm', rStrt1, k)
 ENDCASE
 GO k
 REPLACE sgnl WITH sgnlVl
NEXT
IF NOT hstgtVl
 nFldsVl = 2
 DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
 arrFlds[1] = 'mcd'
 arrFlds[2] = 'sgnl'
 arrGTps[1] = 'C 1.0 0.0 0.0 1.0'
 arrGTps[2] = 'L 0.0 0.0 0.0 0.0'
 svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
  'MACD(' + TRANSFORM(sVl) + '-' + TRANSFORM(lVl) + ')',;
  prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)
ELSE
 nFldsVl = 1
 DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
 arrFlds[1] = 'mcd - sgnl'
 arrGTps[1] = 'C 1.0 0.0 0.0 1.0'
 svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
  'MACD_Histogam(' + TRANSFORM(sVl) + '-' + TRANSFORM(lVl) + ')',;
  prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)
ENDIF

Осциллятор ценовых моментов Чанде (CMO)

Осциллятор ценовых моментов Чанде (Chande Momentum Oscillator, CMO) сигнализирует о перекупленности/перепроданности актива.

Chande Momentum Oscillator

Рис. 19. CMO

Расчет:

CMO = (sum1 – sum2) / (sum1 + sum2) * 100,
где sum1 = sum(CMO1, n) – суммарное значение CMO1 за n интервалов.
sum2 = sum(CMO2, n) – суммарное значение CMO2 за n интервалов.
df = Pi – Pi–1.
Если df > 0, то CMO1 = df, CMO2 = 0. Если df < 0, то CMO2 = –df, CMO1 = 0.
В качестве P преимущественно берется цена закрытия.

Использование:

Эти уровни соответствуют уровням 70/30 индикатора RSI.

Программа:

* Click-обработчик кнопки CMO
LOCAL pDtVl, IDSVl, opgACVl,chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 14
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 RETURN
ENDIF
ALTER TABLE (ali) ADD cmo1 N(8, 2) ADD cmo2 N(8, 2) ADD cmo N(8, 2)
FOR k = 2 TO rCnt
 GO k - 1
 mVVl = mV
 GO k
 dfVl = mV - mVVl
 STORE 0 TO cmoVl1, cmoVl2
 DO CASE
 CASE dfVl > 0
  cmoVl1 = dfVl
 CASE dfVl < 0
  cmoVl2 = -dfVl
 ENDCASE
 REPLACE cmo1 WITH cmoVl1 cmo2 WITH cmoVl2
NEXT
FOR k = n TO rCnt
 kS = k - n + 1
 sCmoVl1 = clcSMA('cmo1', kS, k) * n
 sCmoVl2 = clcSMA('cmo2', kS, k) * n
 cmoVl = (sCmoVl1 - sCmoVl2) / (sCmoVl1 + sCmoVl2) * 100
 GO k
 REPLACE cmo WITH cmoVl
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'cmo'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Chande_Momentum_Oscillator(' + TRANSFORM(n) + ')',;
 prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)

Индекс силы Элдера (EFI)

Индекс силы Элдера (Elder's Force Index, EFI). Предложен А. Элдером. Измеряет силу быков после поддержки и силу медведей после понижения (рис. 22).

Elder's Force Index

Рис. 22. EFI

Расчет:

Сила движения рынка зависит от его направления, размаха и объема. Если текущий бар закрывается выше предыдущего, это свидетельствует о том, что сила положительна, если наоборот ниже, то сила отрицательна. Чем больше цены различаются, тем существеннее сила. Чем больше объем сделок, тем больше сила.

FI = (1 – Pi–1 / Pi) * Vi
EFIi = MA(FI, n)
По умолчанию n = 14

Использование:

EFI можно применять как в сочетании со скользящей средней, так и отдельно. При короткой скользящей средней можно выявить наиболее оптимальные моменты для открытия и закрытия позиций, а при длинной индикатор может определить грядущую смену тренда.
Подает следующие сигналы:

Программа:

* Click-обработчик кнопки EFI
LOCAL pDtVl, IDSVl, opgACVl,chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 14
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 RETURN
ENDIF
ALTER TABLE (ali) ADD fi N(15, 4) ADD efi N(15, 4)
FOR k = 2 TO rCnt
 GO k - 1
 mVVl = mV
 GO k
 REPLACE fi WITH (1.0 - mVVl / mV) * vlm
NEXT
FOR k = n TO rCnt
 efiVl = clcSMA('fi', k - n + 1, k)
 GO k
 REPLACE efi WITH efiVl
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'efi'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Elder_Force_Index(' + TRANSFORM(n) + ')',;
 prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)

Индекс относительной силы (RSI)

Индекс относительной силы (Relative Strength Index, RSI, рис. 23), предложенный У. Вайлдером (Welles Wilder), – это следующий за ценами осциллятор, который колеблется от 0 до 100.

Relative Strength Index

Рис. 23. RSI

Расчет:

RSI = 100 / (1 + D(P, n) / U(P, n)), где
U(P, n) – скользящее среднее роста цены;
D(P, n) – скользящее среднее падения цены.
По умолчанию n = 14; популярны значения 9, 10 и 25 (меньшие значения берутся при торговле на краткосрочных ценовых движениях).

Использование:

Распространенным методом анализа RSI является поиск расхождения (дивергенции) между ценами и значением индикатора. Подобное расхождение свидетельствует о вероятности разворота цен. Сигналы RSI используются совместно с сигналами разворота тренда, подаваемыми иными индикаторами.
RSI может предоставить ранний сигнал покупки/продажи.
RSI сигнализирует о перепроданности/перекупленности рынка. На графике RSI задаются два контрольных уровня (по умолчанию 30 и 70, нередко употребляются 20 и 80). Подъем RSI выше 70 означает перенасыщенность рынка покупками и вхождение в область продаж. Снижение RSI ниже 30 говорит о перенасыщенности рынка продажами и вхождение в область покупок. Действительные сигналы на покупку/продажу даются при развороте RSI. Пересечения RSI уровня 50 также можно использовать как сигналы покупки/продажи.

Сигналы RSI:

Эффективность:

Следование сигналам индикатора привело на тестовой выборке к сокращению портфеля.

Программа:

* Click-обработчик кнопки RSI
clcltRSI(thisForm, 1)

* Расчет и оценка индикатора RSI
PROCEDURE clcltRSI(oFrm, hwVl, thsVl)
 LOCAL pDtVl, IDSVl, opgACVl, slctVl, prcKndVl, ali, rCnt
 n = oFrm.SpinnerPrd.Value
 IF hwVl = 1
  LOCAL chckNldVl
  prpsVl = prpsT(oFrm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 ELSE
  LOCAL dsMntVl, smVl, kVl, fpVl
  prpsVl = prpsT2(oFrm, thsVl, @pDtVl, @dsMntVl, @IDSVl, @opgACVl, @slctVl, @prcKndVl, @ali, @rCnt, @smVl, @fpVl, @kVl, 2)
 ENDIF
 IF NOT prpsVl
  RETURN
 ENDIF
 ALTER TABLE (ali) ADD rsi N(7, 2)
 FOR k = n + 1 TO rCnt
  kS = k - n + 1
  uVl = clcUDMA('mV', kS, k, .T.)
  dVl = clcUDMA('mV', kS, k, .F.)
  GO k
  REPLACE rsi WITH 100.0 / (1.0 + IIF(uVl = 0, 0, dVl / uVl))
 NEXT
 nFldsVl = IIF(hwVl < 3, 1, 5)
 DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
 IF hwVl < 3
  arrFlds[1] = 'rsi'
  arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
 ELSE
  arrFlds[1] = 'mV'
  arrGTps[1] = 'L 0.0 0.0 0.0 0.0'
  arrFlds[2] = 'bsg'
  arrGTps[2] = 'V 1.0 0.0 0.0 0.0'
  arrFlds[3] = 'bsg2'
  arrGTps[3] = 'S 1.0 0.0 0.0 0.0'
  arrFlds[4] = 'ssg'
  arrGTps[4] = 'V 0.0 0.0 1.0 0.0'
  arrFlds[5] = 'ssg2'
  arrGTps[5] = 'S 0.0 0.0 1.0 0.0'
 ENDIF
 ttlVl = 'Relative_Strength_Index(' + TRANSFORM(n) + ')'
 prcKndVl = LEFT(prcKndVl, RAT('_', prcKndVl)) + 'UDMA'
 IF hwVl = 1
  svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .F.)
 ELSE
  CALCULATE MAX(mV) TO mVSllVl
  IF hwVl = 3
   mxVl = mVSllVl
   CALCULATE MIN(mV) TO mnVl
   ALTER TABLE (ali) ADD bsg N(8, 2) ADD bsg2 N(8, 2) ADD ssg N(8, 2) ADD ssg2 N(8, 2)
   REPLACE bsg WITH mnVl bsg2 WITH mnVl ssg WITH mnVl ssg2 WITH mnVl ALL
  ELSE
   mxVl = ''
  ENDIF
  smVl2_2 = clcXptdPrft(smVl, dsMntVl, pDtVl, fpVl)
  smVl2 = smVl
  STORE 0 TO stcsMntVl, mntBsVl, mntSllsVl, fVl, smLftVl2
  brdrVl = 30
  LOCATE FOR dy = DAY(pDtVl)
  DO WHILE NOT EOF()
   DO WHILE NOT EOF() AND NOT bSllSgnlSFndRS('rsi', brdrVl)
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
    ELSE
     IF stcsMntVl = 0 AND mV < mVSllVl
      oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
     ENDIF
    ENDIF
   ENDIF
   DO WHILE NOT EOF() AND NOT bSllSgnlSFndRS('-rsi', -100 + brdrVl)
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
    ELSE
     smTmpVl = mV * stcsMntVl + smLftVl2
     IF smTmpVl > smVl2
      oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
      mVSllVl = mV
     ENDIF
    ENDIF
   ENDIF
  ENDDO
  frmPdt(oFrm, stcsMntVl, smVl2, smLftVl2, mntBsVl, mntSllsVl, smVl, fVl, smVl2_2)
  svMVDtFls2(ali, IDSVl, pDtVl, dsMntVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps)
 ENDIF
ENDPROC && clcltRSI
*
* Retuns .T. if buy / sell signal is found
FUNCTION bSllSgnlSFndRS(fld, brdrVl)
 LOCAL fldVl, fldVl2, fldVl3, fldVl4, sKVl, sKVl2
 fldVl = &fld
 SKIP
 DO WHILE NOT EOF() AND &fld >= fldVl && Move up
  fldVl = &fld
  SKIP
 ENDDO
 DO WHILE NOT EOF() AND &fld <= fldVl && Move down
  fldVl = &fld
  SKIP
 ENDDO
 sKVl = NOT EOF() AND fldVl < brdrVl
 fldVl2 = IIF(sKVl, fldVl, '')
 DO WHILE NOT EOF() AND &fld >= fldVl && Move up
  fldVl = &fld
  SKIP
 ENDDO
 fldVl3 = IIF(EOF(), '', fldVl)
 DO WHILE NOT EOF() AND &fld <= fldVl && Move down
  fldVl = &fld
  SKIP
 ENDDO
 fldVl4 = IIF(EOF(), '', fldVl)
 DO WHILE NOT EOF() AND &fld >= fldVl && Move up
  fldVl = &fld
  IF fldVl > fldVl3
   RETURN IIF(EMPTY(fldVl2), .F., fldVl4 >= fldVl2 AND fldVl4 < brdrVl)
  ENDIF
  SKIP
 ENDDO
 RETURN .F.
ENDFUNC && bSllSgnlSFndRS

Ишимоку Кинко Хайо (IKH)

Индикатор Ишимоку Кинко Хайо (Ichimoku Kinko Hyo, IKH) предназначен для определения рыночного тренда, уровней поддержки и сопротивления и для генерации сигналов покупки и продажи. Лучше всего индикатор работает на недельных и дневных графиках. Состоит из линий Tenkan-sen (TS, разворотная линия, красная), Kijun-sen (KS, основная линия, зеленая) и Chinkou Span (CS, запаздывающая линия, синяя) и линий Senkou Span A (SSA, желтая) и Senkou Span B (SSB, фиолетовая), между которыми штрихуется область, называемая облаком (рис. 24).

Ichimoku Kinko Hyo

Рис. 24. IKH (график цены показан черным цветом)

Расчет:

  1. TS показывает середину цены за промежуток n. TS = (max(H, n) + min(L, n)) / 2;
  2. KS показывает середину цены за промежуток m. KS = (max(H, m) + min(L, m)) / 2;
  3. CS показывает цену закрытия, сдвинутую назад на m. CS = shift(C, –m);
  4. SSA – это полусумма TS и KS, сдвинутая вперед на m. SSA = shift((TS + KS) / 2, m);
  5. SSB показывает середину цены за промежуток z, сдвинутую вперед наm. SSB = shift((max(H, z) + min(L, z)) / 2, m).

По умолчанию n = 9, m = 26 и z = 52 (число недель в году).
На дневном графике 9 – это 1.5 рабочих недели, 26 – число рабочих дней в месяце (в Японии на момент создания IKH).
На недельном графике: 9 недель – это примерно 2 месяца, 26 недель составляют полугодие.

Использование:

  1. TS используется как индикатор краткосрочного, быстрого тренда. Если TS растет или падает, то тренд существует. Поэтому в некоторых случаях, можно входить и выходить из рынка после изменения направления TS.
  2. Если рынок внутри торгового диапазона и если цены находятся в облаке и TS направлена вверх, то происходит движение к верхней границе диапазона, если – вниз, то – к нижней. Цены стоят на месте, если TS находится в узком коридоре. Отсюда вытекает стратегия, основанная на развороте TS: если цена внутри облака, то при развороте вверх TS – покупка, при развороте TS вниз – продажа.
  3. KS показывает более долгосрочный тренд и его направление. Интерпретация такая же, как и TS.
  4. KS используется как показатель движения рынка. Если цена выше KS, то цена, скорее всего, продолжит рост. Когда цена пересекает эту линию вероятно дальнейшее изменения тренда.
  5. Когда TS пересекает KS снизу вверх, генерируется сигнал на покупку, когда – сверху вниз, то подается сигнал на продажу. Этот тип сигнала является слабым, если цена находятся в облаке.
  6. К наиболее достоверным сигналам IKH относят пересечение цены и SSB. Если цена пересекает SSB сверху вниз, то подается сигнал на продажу, если – снизу вверх, то на покупку. Это сигнал усиливается, если цены выходят за границы облака. (Аналог стратегии, основанной на пресечении цены со своей скользящей средней.)
  7. Вход вверх можно осуществлять, если цена находится у нижней границы облака, и – вниз, если цена находится у верхней границы облака. При этом считается, что облако должно быть достаточно широким. (Аналог стратегии, основанной на анализе пересечения двух скользящих средних.)
  8. Если цена находится в облаке, то рынок считается нетрендовым, и края облака образуют уровни поддержки и сопротивления. Если цена находится над облаком, то его верхняя линия образует первый уровень поддержки, а нижняя – второй. Если цена находится под облаком, то нижняя линия образует первый уровень сопротивления, а верхняя – второй. Если CS пересекает график цены снизу вверх, то это является сигналом к покупке. Если сверху вниз – сигналом к продаже.
  9. Параллельность TS, KS и SSB означает развитие на рынке устойчивого тренда. При восходящем тренде сверху TS, в центре KS, а снизу SSB, а при нисходящем наоборот: сверху SSB, а снизу TS.
  10. Когда цена внутри облака и если CS пересекает цену снизу вверх, то это дает сигнал на покупку. Если CS пересекает цену сверху вниз, то дается сигнал на продажу. Полезно поставить стоп за границей облака: при покупке – под нижней границей; в случае продажи – над верхней границей.
  11. На уровнях, отстоящих от границы облака на 10 – 20%, выставляются ордера по взятию прибыли. Кроме того, позиция закрывается и в случае получения любого обратного сигнала (разворотTS, цена и CS разнонаправлены и цена пересекает CS, цена пересекает SSA или SSB).

Замечание. В качестве цены берется значение Close.

Линии IKH оперативно реагируют на появление новых экстремумов (не запаздывают, как скользящие).
Во флэте сложно эффективно использовать индикатор.

Эффективность:

Следование сигналам индикатора привело на тестовой выборке к сокращению портфеля.

Программа:

* Click-обработчик кнопки IKH
clcltIKH(thisForm, 1)

* Расчет и оценка индикатора IKH
PROCEDURE clcltIKH(oFrm, hwVl, thsVl)
 LOCAL pDtVl, IDSVl, opgACVl, slctVl, prcKndVl, ali, rCnt
 n = 9
 m = 26
 z = 52
 IF hwVl = 1
  LOCAL chckNldVl
  prpsVl = prpsT(oFrm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, z + m)
 ELSE
  LOCAL dsMntVl, smVl, kVl, fpVl
  prpsVl = prpsT2(oFrm, thsVl, @pDtVl, @dsMntVl, @IDSVl, @opgACVl, @slctVl, @prcKndVl, @ali, @rCnt, @smVl, @fpVl, @kVl, 2)
 ENDIF
 IF NOT prpsVl
  RETURN
 ENDIF
 ALTER TABLE (ali) ADD ts N(8, 2) ADD ks N(8, 2) ADD cs N(8, 2) ADD ssa N(8, 2) ADD ssb N(8, 2)
 FOR k = z + m TO rCnt
  tsVl = clcPrcMddl(k, n)
  ksVl = clcPrcMddl(k, m)
  GO MIN(rCnt, k + m)
  csVL = cls
  ssaVL = 0.5 * (clcPrcMddl(k - m, n) + clcPrcMddl(k - m, m))
  ssbVL = clcPrcMddl(k - m, z)
  GO k
  REPLACE ts WITH tsVl ks WITH ksVl cs WITH csVl ssa WITH ssaVl ssb WITH ssbVl
 NEXT
 nFldsVl = 8
 DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
 arrFlds[1] = 'ts'
 arrFlds[2] = 'ks'
 arrFlds[3] = 'cs'
 arrFlds[4] = 'ssa'
 arrFlds[5] = 'ssb'
 arrFlds[6] = 'ssa'
 arrFlds[7] = 'ssb'
 arrFlds[8] = 'mV'
 arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
 arrGTps[2] = 'L 0.0 1.0 0.0 0.0'
 arrGTps[3] = 'L 0.0 0.0 1.0 0.0'
 arrGTps[4] = 'L 0.5 0.5 0.0 0.0'
 arrGTps[5] = 'L 0.0 1.0 1.0 0.0'
 arrGTps[6] = 'V 0.5 0.5 0.5 0.0'
 arrGTps[7] = 'S 0.0 0.0 0.0 0.0'
 arrGTps[8] = 'L 0.0 0.0 0.0 0.0'
 ttlVl = 'Ichimoku_Kinko_Hyo(' + TRANSFORM(n) + '_' + TRANSFORM(m) + '_' + TRANSFORM(z) + ')'
 IF hwVl = 1
  svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .F.)
 ELSE
  svMVDtFls2(ali, IDSVl, pDtVl, dsMntVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps)
  *
  smVl2_2 = clcXptdPrft(smVl, dsMntVl, pDtVl, fpVl)
  CALCULATE MAX(mV) TO mVSllVl
  smVl2 = smVl
  STORE 0 TO stcsMntVl, mntBsVl, mntSllsVl, fVl, smLftVl2
  IF kVl < 3
   bWhnCrssBT('ts', 'ks', kVl, @smVl2, smVl, fpVl, @mntBsVl, @mntSllsVl, @stcsMntVl, @smLftVl2, @fVl, @mVSllVl, pDtVl)
  ELSE
   bWhnCrssBT('mV', 'ssb', kVl, @smVl2, smVl, fpVl, @mntBsVl, @mntSllsVl, @stcsMntVl, @smLftVl2, @fVl, @mVSllVl, pDtVl)
  ENDIF
  frmPdt(oFrm, stcsMntVl, smVl2, smLftVl2, mntBsVl, mntSllsVl, smVl, fVl, smVl2_2)
 ENDIF
ENDPROC && clcltIKH

Индекс облегчения рынка Билла Уильямса (BW MFI)

Индекс облегчения рынка Билла Уильямса (Bill Williams Market Facilitation Index, BW MFI) показывает изменение цены на каждом интервале указанного временного диапазона (рис. 25).

Bill Williams Market Facilitation Index

Рис. 25. BW MFI

Расчет:

BW MFI = (H – L) / V

Использование:

BW MFI употребляется совместно с графиком объема продаж:

Программа:

* Click-обработчик кнопки BW MFI
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, 1)
 RETURN
ENDIF
ALTER TABLE (ali) ADD bmwfi N(8, 2)
CALCULATE AVG(vlm) TO vgVlmVl IN (ali)
REPLACE bmwfi WITH (hgh - lw) / (vlm / vgVlmVl) ALL IN (ali)
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'bmwfi'
arrGTps[1] = 'C 1.0 0.0 0.0 1.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Bill_Williams_Market_Facilitation_Index', '', nFldsVl, @arrFlds, @arrGTps, .F., .F.)

Момент цены (Momentum)

Индикатор темпа Момент цены (Momentum) измеряет колебание цены актива (рис. 26).

Индикатор темпа Momentum

Рис. 26. Momentum

Расчет:

Mmi = Pi / Pi – n * 100
По умолчанию n = 5

Использование:

Эффективность:

Следование сигналам индикатора привело на тестовой выборке к сокращению портфеля.

Программа:

* Click-обработчик кнопки Momentum
clcltMmntm(thisForm, 1)

* Расчет и оценка индикатора Momentum
PROCEDURE clcltMmntm(oFrm, hwVl, thsVl)
 LOCAL pDtVl, IDSVl, opgACVl, slctVl, prcKndVl, ali, rCnt
 n = oFrm.SpinnerPrd.Value
 IF hwVl = 1
  LOCAL chckNldVl
  prpsVl = prpsT(oFrm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 ELSE
  LOCAL dsMntVl, smVl, kVl, fpVl
  prpsVl = prpsT2(oFrm, thsVl, @pDtVl, @dsMntVl, @IDSVl, @opgACVl, @slctVl, @prcKndVl, @ali, @rCnt, @smVl, @fpVl, @kVl, 2)
 ENDIF
 IF NOT prpsVl
  RETURN
 ENDIF
 ALTER TABLE (ali) ADD mmntm N(8, 2)
 FOR k = n + 1 TO rCnt
  GO k - n
  mVVl = mv
  GO k
  REPLACE mmntm WITH 100.0 * mV / mVVl
 NEXT
 nFldsVl = IIF(hwVl < 3, 1, 5)
 DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
 IF hwVl < 3
  arrFlds[1] = 'mmntm'
  arrGTps[1] = 'L 1.0 0.0 0.0 1.0'
 ELSE
  arrFlds[1] = 'mV'
  arrGTps[1] = 'L 0.0 0.0 0.0 0.0'
  arrFlds[2] = 'bsg'
  arrGTps[2] = 'V 1.0 0.0 0.0 0.0'
  arrFlds[3] = 'bsg2'
  arrGTps[3] = 'S 1.0 0.0 0.0 0.0'
  arrFlds[4] = 'ssg'
  arrGTps[4] = 'V 0.0 0.0 1.0 0.0'
  arrFlds[5] = 'ssg2'
  arrGTps[5] = 'S 0.0 0.0 1.0 0.0'
 ENDIF
 ttlVl = 'Momentum(' + TRANSFORM(n) + ')'
 IF hwVl = 1
  svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .F.)
 ELSE
  CALCULATE MAX(mV) TO mVSllVl
  IF hwVl = 3
   mxVl = mVSllVl
   CALCULATE MIN(mV) TO mnVl
   ALTER TABLE (ali) ADD bsg N(8, 2) ADD bsg2 N(8, 2) ADD ssg N(8, 2) ADD ssg2 N(8, 2)
   REPLACE bsg WITH mnVl bsg2 WITH mnVl ssg WITH mnVl ssg2 WITH mnVl ALL
  ELSE
   mxVl = ''
  ENDIF
  smVl2_2 = clcXptdPrft(smVl, dsMntVl, pDtVl, fpVl)
  smVl2 = smVl
  STORE 0 TO stcsMntVl, mntBsVl, mntSllsVl, fVl, smLftVl2
  LOCATE FOR dy = DAY(pDtVl)
  DO WHILE NOT EOF()
   DO WHILE NOT EOF() AND NOT bSllSgnlSFndMmntm('mmntm')
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
    ELSE
     IF stcsMntVl = 0 AND mV < mVSllVl
      oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
     ENDIF
    ENDIF
   ENDIF
   DO WHILE NOT EOF() AND NOT bSllSgnlSFndMmntm('-mmntm')
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
    ELSE
     smTmpVl = mV * stcsMntVl + smLftVl2
     IF smTmpVl > smVl2
      oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
      mVSllVl = mV
     ENDIF
    ENDIF
   ENDIF
  ENDDO
  frmPdt(oFrm, stcsMntVl, smVl2, smLftVl2, mntBsVl, mntSllsVl, smVl, fVl, smVl2_2)
  svMVDtFls2(ali, IDSVl, pDtVl, dsMntVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps)
 ENDIF
ENDPROC && clcltMmntm
*
* Retuns .T. if buy / sell signal is found
FUNCTION bSllSgnlSFndMmntm(fld)
 fldVl = &fld
 SKIP
 DO WHILE NOT EOF() AND &fld >= fldVl && Move up
  fldVl = &fld
  SKIP
 ENDDO
 DO WHILE NOT EOF() AND &fld <= fldVl && Move down
  fldVl = &fld
  SKIP
 ENDDO
 RETURN .T.
ENDFUNC && bSllSgnlSFndMmntm

Индекс денежных потоков (MnFI)

Индекс денежных потоков (Money Flow Index, MnFI) показывает интенсивность, с которой деньги вкладываются в актив или выводятся из него. Разработан Ласло Бириньи мл. (Laszlo Birinyi, Jr.), как вариант индикатора OBV. Построение и интерпретация индикатора аналогична RSI, с той разницей, что в MnFI учитывается и объем: торги с большим объемом дают больший вклад в MnFI, чем торги с меньшим объемом (рис. 27).

Money Flow Index

Рис. 27. MnFI

Расчет:

MnFIj = 100 – 100 / (1 + mRj)
Отношение денежных потоков (money ratio):
mRj = sum(pMFi) / sum(nMFi), i = j – n + 1, j
Положительный денежный поток (positive money flow):
pMFi = Ti * Vi, если Ti > Ti – 1
Отрицательный денежный поток (negative money flow):
nMFi = Ti * Vi, если Ti < Ti – 1
По умолчанию n = 14.
Денежный поток возвращает функция clcMF

Использование:

MnFI полезен при выборе актива, поскольку информирует о его сильных и слабых сторонах.
Цена обычно следует за MnFI: как правило, он показывает, что на восходящем тренде финансовые потоки направлены в рынок и меняют направление при падении цены.
При наличии подтверждения от иных индикаторов MnFI может быть употреблен для выбора точки входа.
Сигналы MnFI:

Программа:

* Click-обработчик кнопки MnFI
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 14
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 RETURN
ENDIF
ALTER TABLE (ali) ADD mnfi N(8, 2)
FOR k = n + 1 TO rCnt
 pMFVl = clcMF(k, n, .T.)
 nMFVl = clcMF(k, n, .F.)
 GO k
 REPLACE mnfi WITH 100.0 - IIF(nMFVl = 0.0, 0, 100.0 / (1.0 + pMFVl / nMFVl))
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'mnfi'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Money_Flow_Index(' + TRANSFORM(n) + ')', '', nFldsVl, @arrFlds, @arrGTps, .F., .F.)

Балансовый объем (OBV)

Индикатор Балансовый объем (On Balance Volume, OBV) предложен Джо Гранвиллем (Joe Granville). Показывает направление движения денежных средств: на рынок или в обратном направлении (рис. 28).

On Balance Volume

Рис. 28. OBV

Расчет:

OBVi = OBVi – 1 + Vi, если Pi > Pi – 1
OBVi = OBVi – 1 – Vi, если Pi < Pi – 1

Использование:

Интерпретация OBV основана на предположении, что изменения OBV опережают ценовые. Тогда повышение OBV свидетельствует о том, что в актив вкладывают средства крупные игроки (осведомленных трейдеров или «умные деньги» можно увидеть, когда OBV начинает рост). Цена и OBV продолжат рост, когда вслед интерес к активу проявит и широкая публика.
Если цена опережает в своем движении OBV, то возникает так называемое "отсутствие подтверждения". Это может наблюдаться на вершине бычьего рынка, когда цена растет без соответствующего роста OBV или опережая рост OBV, или в основании медвежьего рынка, когда цена падает без соответствующего уменьшения OBV или снижается быстрее OBV.
О восходящей тенденции OBV можно говорить, если каждый новый пик выше предыдущего и каждая новая впадина выше предыдущей. По аналогии, нисходящая тенденция OBV предполагает последовательное понижение пиков и впадин. Установившаяся тенденция остается в силе до перелома OBV.
Перелом тенденции OBV может произойти двумя способами. В первом случае тенденция изменяется с восходящей на нисходящую, или с нисходящей на восходящую. Во втором случае тенденция OBV переходит в неопределенную и остается таковой на протяжении более трех интервалов.
Когда тенденция OBV меняется на восходящую или нисходящую, происходит так называемый "прорыв". Поскольку прорывы индикатора обычно предшествуют ценовым прорывам, инвесторам следует занимать длинные позиции при прорывах OBV вверх и, соответственно, продавать в случае прорыва OBV вниз. Открытые позиции нужно сохранять до тех пор, пока направление тенденции не изменится.
OBV может быть использован и для подтверждения текущего ценового движения, и для предупреждения о возможном развороте тренда. Если наблюдается дивергенция OBV и цены, то возможен разворот тренда.
Важны не действительные значения OBV, а его тренд.
Изменение OBV часто предшествует изменениям цены.
Пробитие OBV обычно влечет пробитие ценового тренда. Пробитие индикатора вверх является сигналом к покупке.
OBV подходит для торговли в краткосрочном цикле. Гранвиль советует, чтобы инвесторы действовали быстро и решительно, если они хотят получить прибыль от краткосрочного анализа OBV.
Сигналы OBV:

Имеются и иные учитывающие объем торгов индикаторы, например MnFI.

Программа:

* Click-обработчик кнопки OBV
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, 1)
 RETURN
ENDIF
ALTER TABLE (ali) ADD obv N(20, 2)
GO TOP
mVVl = mV
obvVl = mVVl
REPLACE obV WITH obvVl
FOR k = 2 TO rCnt
 GO k
 DO CASE
 CASE mV > mVVl
  obvVl = obvVl + mV
 CASE mV < mVVl
  obvVl = obvVl - mV
 ENDCASE
 REPLACE obv WITH obvVl
 mVVl = mV
NEXT
REPLACE obv WITH 0.1 * obv ALL
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'obv'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
prcKndVl = LEFT(prcKndVl, RAT('_', prcKndVl) - 1)
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, 'On_Balance_Volume',;
 prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)

Параболическая система (PSAR)

Параболическая система (Parabolic SAR, Parabolic Stop and Reverse, PSAR) – это система определения точек разворота позиций. Предложен У. Вайлдером (Welles Wilder) для анализа трендовых рынков в дополнение к другим следующим за рынком индикаторам (рис. 29).

Parabolic SAR

Рис. 29. PSAR

Расчет:

Нисходящий тренд: SARi = (Hi – 1 – SARi – 1) * AF + SARi – 1
Восходящий тренд: SARi = (Li – 1 – SARi – 1) * AF + SARi – 1
По умолчанию коэффициент ускорения AF = 0.02.

Использование:

Индикатор аналогичен скользящей средней, однако PSAR движется с большим ускорением и может менять положение относительно цены. Если цена пересекает PSAR, то происходит переворот индикатора: последующие его значения располагаются по другую сторону от цены. При восходящем тренде индикатор располагается ниже цен, в случае нисходящего тренда – выше. Переворот индикатора – это сигнал либо об окончании тренда, либо об его развороте. PSAR следует пользоваться при наличии тренда. При его отсутствии PSAR дает ложные сигналы.
PSAR является полезным инструментом для определения свежих трендовых движений на ранних стадиях, подавая сигналы покупки/продажи.
Когда PSAR ниже цены, следует покупать или становиться в длинную позицию. Когда PSAR выше цены, то следует продавать или открывать короткую позицию. Сигналы нужно подтверждать сигналами иных индикаторов. Так, Джон Мэрфи (John Murphy), автор Технического анализа финансовых рынков (Technical Analysis of the Financial Markets), рекомендует использовать с PSAR индекс DMI. В частности, когда +DI находится выше DI, все PSAR-сигналы на продажу можно игнорировать.

Программа:

* Click-обработчик кнопки PSAR
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, 1)
 RETURN
ENDIF
afVl = 0.02
ALTER TABLE (ali) ADD psar N(8, 2)
GO TOP
psarVl = (1.0 + afVl) * hgh
sShrtVl = .F.
FOR k = 1 TO rCnt
 GO k
 REPLACE psar WITH psarVl
 DO CASE
 CASE NOT sShrtVl AND hgh > psarVl
  psarVl = (1.0 - afVl) * lw
  sShrtVl = .T.
 CASE sShrtVl AND lw < psarVl
  psarVl = (1.0 + afVl) * hgh
  sShrtVl = .F.
 CASE sShrtVl
  psarVl = (lw - psarVl) * afVl + psarVl
 OTHERWISE
  psarVl = (hgh - psarVl) * afVl + psarVl
 ENDCASE
NEXT
nFldsVl = 3
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'psar'
arrFlds[2] = 'hgh'
arrFlds[3] = 'lw'
arrGTps[1] = 'P 1.0 0.0 0.0 0.0'
arrGTps[2] = 'V 0.0 0.0 0.0 0.0'
arrGTps[3] = 'S 0.0 0.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, 'Parabolic_SAR', '', nFldsVl, @arrFlds, @arrGTps, .F., .T.) svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, 'Parabolic_SAR', '', 'P', nFldsVl, @arrFlds, .F., .T.)

Стохастический осциллятор (SO)

Стохастический осциллятор (Stochastic Oscillator, SO) показывает, когда цена подходит близко к границе торгового диапазона, взятого за определенный период времени. Предложен Джорджем Лейном (Dr. George Lane). Состоит из двух кривых: быстрой, более чувствительной к изменению цены %К и медленной %D (рис. 30).

Stochastic Oscillator. Быстрая (%К) и медленная (%D) кривые

Рис. 30. SO (кривая %D показана пунктиром)

Расчет:

%K0 = 100 * (С – Ln) / (Hn – Ln)
%K = MA(%K0, m)
%D = MA(%K, m2), где
Ln и Hn – соответственно минимальная и максимальная цена за последние n интервалов.
По умолчанию n = 5 (обычно n берут из диапазона 5 – 21), а m = m2 = 3.
Значения Ln и Hn возвращают соответственно функции clcMinP и clcMaxP

Использование:

Сигналы SO:

Эффективность:

Следование сигналам индикатора привело на тестовой выборке к сокращению портфеля.

Программа:

* Click-обработчик кнопки SO
clcltSO(thisForm, 1)

* Расчет и оценка индикатора SO
PROCEDURE clcltSO(oFrm, hwVl, thsVl)
 LOCAL pDtVl, IDSVl, opgACVl, slctVl, prcKndVl, ali, rCnt
 m = 3
 m2 = 3
 n = oFrm.SpinnerPrd.Value && 5 by default
 mn = MAX(m, m2, n)
 IF hwVl = 1
  LOCAL chckNldVl
  prpsVl = prpsT(oFrm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, mn)
 ELSE
  LOCAL dsMntVl, smVl, kVl, fpVl
  prpsVl = prpsT2(oFrm, thsVl, @pDtVl, @dsMntVl, @IDSVl, @opgACVl, @slctVl, @prcKndVl, @ali, @rCnt, @smVl, @fpVl, @kVl, 2)
 ENDIF
 IF NOT prpsVl
  RETURN
 ENDIF
 ALTER TABLE (ali) ADD pk N(8, 2) ADD pk2 N(8, 2) ADD pd N(8, 2)
 FOR k = n + 1 TO rCnt
  lVl = clcMinP(k, n)
  hlVl = clcMaxP(k, n) - lVl
  pkVl = IIF(hlVl = 0.0, 100, 100 * (cls - lVl) / hlVl)
  GO k
  REPLACE pk WITH pkVl
 NEXT
 IF opgACVl = 2
  pk2Vl = 0.0
  FOR k = 1 TO m - 1
   pk2Vl = pk2Vl + pk
  NEXT
  pk2Vl = pk2Vl / (m - 1)
 ENDIF
 FOR k = m TO rCnt
  kS = k - m + 1
  DO CASE
  CASE opgACVl = 1
   pk2Vl = clcSMA('pk', kS, k)
  CASE opgACVl = 2
   pk2Vl = clcEMA('pk', pk2Vl, k, n)
  CASE opgACVl = 3
   pk2Vl = clcSMA('pk * vlm', kS, k) / clcSMA('vlm', kS, k)
  ENDCASE
  GO k
  REPLACE pk2 WITH pk2Vl
 NEXT
 IF opgACVl = 2
  pdVl = 0.0
  FOR k = 1 TO m2 - 1
   pdVl = pdVl + pk2
  NEXT
  pdVl = pdVl / (m2 - 1)
 ENDIF
 FOR k = m2 TO rCnt
  kS = k - m2 + 1
  DO CASE
  CASE opgACVl = 1
   pdVl = clcSMA('pk2', kS, k)
  CASE opgACVl = 2
   pdVl = clcEMA('pk2', pdVl, k, n)
  CASE opgACVl = 3
   pdVl = clcSMA('pk2 * vlm', kS, k) / clcSMA('vlm', kS, k)
  ENDCASE
  GO k
  REPLACE pd WITH pdVl
 NEXT
 nFldsVl = IIF(hwVl < 3, 2, 5)
 DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
 IF hwVl < 3
  arrFlds[1] = 'pk2'
  arrFlds[2] = 'pd'
  arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
  arrGTps[2] = 'D 0.0 0.0 1.0 0.0'
 ELSE
*!*   arrFlds[1] = 'mV'
  arrFlds[1] = 'pk2'
  arrGTps[1] = 'L 0.0 0.0 0.0 0.0'
  arrFlds[2] = 'bsg'
  arrGTps[2] = 'V 1.0 0.0 0.0 0.0'
  arrFlds[3] = 'bsg2'
  arrGTps[3] = 'S 1.0 0.0 0.0 0.0'
  arrFlds[4] = 'ssg'
  arrGTps[4] = 'V 0.0 0.0 1.0 0.0'
  arrFlds[5] = 'ssg2'
  arrGTps[5] = 'S 0.0 0.0 1.0 0.0'
 ENDIF
 ttlVl = 'Stochastic_Oscillator(' + TRANSFORM(n) + '_' + + TRANSFORM(m) + '_' + TRANSFORM(m2) + ')'
 IF hwVl = 1
  svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)
 ELSE
  CALCULATE MAX(mV) TO mVSllVl
  IF hwVl = 3
*!*    mxVl = mVSllVl
*!*    CALCULATE MIN(mV) TO mnVl
   CALCULATE MAX(pk2) TO mxVl
   CALCULATE MIN(pk2) TO mnVl
   ALTER TABLE (ali) ADD bsg N(8, 2) ADD bsg2 N(8, 2) ADD ssg N(8, 2) ADD ssg2 N(8, 2)
   REPLACE bsg WITH mnVl bsg2 WITH mnVl ssg WITH mnVl ssg2 WITH mnVl ALL
  ELSE
   mxVl = ''
  ENDIF
  smVl2_2 = clcXptdPrft(smVl, dsMntVl, pDtVl, fpVl)
  smVl2 = smVl
  STORE 0 TO stcsMntVl, mntBsVl, mntSllsVl, fVl, smLftVl2
  brdrVl = 20
  LOCATE FOR dy = DAY(pDtVl)
  DO WHILE NOT EOF()
   DO WHILE NOT EOF() AND NOT bSllSgnlSFndSO('pk2', 'pd', brdrVl)
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
    ELSE
     IF stcsMntVl = 0 AND mV < mVSllVl
      oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
     ENDIF
    ENDIF
   ENDIF
   DO WHILE NOT EOF() AND NOT bSllSgnlSFndSO('-pk2', '-pd', -100 + brdrVl)
   ENDDO
   IF NOT EOF()
    IF kVl = 1
     oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
    ELSE
     smTmpVl = mV * stcsMntVl + smLftVl2
     IF smTmpVl > smVl2
      oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
      mVSllVl = mV
     ENDIF
    ENDIF
   ENDIF
  ENDDO
  frmPdt(oFrm, stcsMntVl, smVl2, smLftVl2, mntBsVl, mntSllsVl, smVl, fVl, smVl2_2)
  svMVDtFls2(ali, IDSVl, pDtVl, dsMntVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps)
 ENDIF
ENDPROC && clcltSO
*
* Retuns .T. if buy / sell signal is found
FUNCTION bSllSgnlSFndSO(fld, fld2, brdrVl)
 LOCAL fldVl, fldVl2, sKVl
 fldVl = &fld
 fldVl2 = &fld2
 SKIP
 DO WHILE NOT EOF() AND (&fld >= fldVl OR &fld2 >= fldVl2) && Move up
  fldVl = &fld
  fldVl2 = &fld2
  SKIP
 ENDDO
 sKVl = .F.
 DO WHILE NOT EOF() AND (&fld <= fldVl OR &fld2 <= fldVl2) && Move down
  fldVl = &fld
  fldVl2 = &fld2
  skVl = skVl OR fldVl < brdrVl AND fldVl2 < brdrVl
  SKIP
 ENDDO
 sKVl = NOT EOF() AND skVl
 DO WHILE NOT EOF() AND (&fld >= fldVl OR &fld2 >= fldVl2) AND &fld < brdrVl AND &fld2 < brdrVl && Move up
  fldVl = &fld
  fldVl2 = &fld2
  SKIP
 ENDDO
 RETURN NOT EOF() AND sKVl
ENDFUNC && bSllSgnlSFndSO

Процентный диапазон Вильямса (%R)

Процентный Диапазон Вильямса (Williams Percent Range, %R) фиксирует перекупленность/перепроданность актива. Предложен Лари Вильямсом (Larry Williams). %R схож с SO; различия в том, что первый имеет перевернутую шкалу (рис. 31), а второй строится с использованием внутреннего сглаживания.

Williams Percent Range

Рис. 31. %R

Расчет:

%R = –100 * (Hn – C) / (Hn – Ln)
По умолчанию n = 5.
Значения Ln и Hn возвращают соответственно функции clcMinP и clcMaxP

Использование:

По опыту Вильямса %R является лучшие часовым инструментом для определения перекупленности/перепроданности:

По общему для всех индикаторов перекупленности/перепроданности правилу действовать по их сигналам следует, дождавшись поворота цен в соответствующем направлении. Так, если индикатор перекупленности/перепроданности указывает на состояние перекупленности, то прежде, чем продавать актив, разумно дождаться поворота цены вниз.
%R способен предвосхищать ценовые развороты. Он почти всегда образует пик и поворачивает вниз за определенный промежуток времени до того, как цена достигает пика и поворачивает вниз. Точно так же %R обычно образует впадину и заблаговременно поворачивает вверх. %R доказал свою эффективность при определении разворотов рынка.
Выделение доминирующего тренда при работе с %R обязательно. Для этого Вильямс советует использовать 10-недельные скользящие средние.
Как и другие индикаторы темпа, %R не эффективен при боковом тренде.

Сигналы %R:

Программа:

* Click-обработчик кнопки %R
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 5
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 RETURN
ENDIF
ALTER TABLE (ali) ADD wpr N(8, 2)
FOR k = n + 1 TO rCnt
 hVl = clcMaxP(k, n)
 hlVl = hVl - clcMinP(k, n)
 wprVl = -IIF(hlVl = 0.0, 100, 100 * (hVl - cls) / hlVl)
 GO k
 REPLACE wpr WITH wprVl
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'wpr'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Williams_Percent_Range(' + TRANSFORM(n) + ')', '', nFldsVl, @arrFlds, @arrGTps, .F., .T.)

Сила медведей (BrsP)

Индикатор Сила медведей (Bears Power, BrsP), введенный А. Элдером, указывает на возможное изменение направления тренда (рис. 32).

Bears Power

Рис. 32. BrsP

Расчет:

BrsP = L – EMA(P, n); по умолчанию n = 13

Использование:

BrsP используется вместе с трендовыми индикаторами, например c MA. Сигнал покупки возникает, когда трендовый индикатор направлен вверх, BrsP растет, хотя и находится ниже нуля. При этом желательно, чтобы на графике трендового индикатора наблюдалось расхождение.

Программа:

* Click-обработчик кнопки BrsP
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 13
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 RETURN
ENDIF
ALTER TABLE (ali) ADD brsp N(8, 2)
maVl = 0.0
FOR k = 1 TO n - 1
 maVl = maVl + lw
NEXT
maVl = maVl / (n - 1)
FOR k = n TO rCnt
 maVl = clcEMA('lw', maVl, k, n)
 GO k
 REPLACE brsp WITH lw - maVl
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'brsp'
arrGTps[1] = 'C 1.0 0.0 0.0 1.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Bears_Power_L_EMA(' + TRANSFORM(n) + ')', '', nFldsVl, @arrFlds, @arrGTps, .F., .T.)

Сила быков (BllsP)

Индикатор Сила быков (Bulls Power, BllsP), введенный А. Элдером, указывает на возможное изменение направления тренда (рис. 33).

Bulls Power

Рис. 33. BllsP

Расчет:

BllsP = H – EMA(P, n); по умолчанию n = 13

Использование:

BllsP используется вместе с трендовыми индикаторами, например c MA. Сигнал продажи возникает, когда трендовый индикатор имеет нисходящее направление, а BllsP расположен выше нуля, хотя убывает. При этом желательно, чтобы на графике трендового индикатора наблюдалось расхождение.

Программа:

* Click-обработчик кнопки BllsP
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 13
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 RETURN
ENDIF
ALTER TABLE (ali) ADD bllsp N(8, 2)
maVl = 0.0
FOR k = 1 TO n - 1
 maVl = maVl + hgh
NEXT
maVl = maVl / (n - 1)
FOR k = n TO rCnt
 maVl = clcEMA('hgh', maVl, k, n)
 GO k
 REPLACE bllsp WITH hgh - maVl
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'bllsp'
arrGTps[1] = 'C 1.0 0.0 0.0 1.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Bulls_Power_H_EMA(' + TRANSFORM(n) + ')', '', nFldsVl, @arrFlds, @arrGTps, .F., .T.)

Индикатор Демарка (DeMarker)

Индикатор Демарка (DeMarker, рис. 34) использует разницу ценовых максимумов и минимумов текущего и предыдущего баров.

DeMarker

Рис. 34. Индикатор DeMarker

Расчет:

Расчет длительных интервалов позволяет уловить долгосрочную тенденцию на рынке, а расчет коротких обеспечивает попадание сделки в русло основной тенденции.
DMrkr = MA(DeMax, n) / (MA(DeMax, n) + MA(DeMin, n)), где
Если Hi > Hi – 1, то DeMaxi = Hi – Hi – 1, иначе DeMaxi = 0
Если Li < Li – 1, то DeMini = Li – 1 – Li, иначе DeMini = 0
По умолчанию n = 7.

Использование:

При значении индикатора ниже 0.3 вероятно движение цены вверх, а выше 0.7 – вниз.

Программа:

* Click-обработчик кнопки DeMarker
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 7
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 RETURN
ENDIF
ALTER TABLE (ali) ADD dx N(8, 2) ADD dn N(8, 2) ADD dmrkr N(8, 4)
FOR k = 2 TO rCnt
 GO k - 1
 hVl = hgh
 lVl = lw
 GO k
 REPLACE dx WITH IIF(hgh > hVl, hgh - hVl, 0) dn WITH IIF(lw < lVl, lVl - lw, 0)
NEXT
IF opgACVl = 2
 STORE 0.0 TO dxVl, dnVl
 FOR k = 1 TO n - 1
  dxVl = dxVl + dx
  dnVl = dnVl + dn
 NEXT
 dxVl = dxVl / (n - 1)
 dnVl = dnVl / (n - 1)
ENDIF
FOR k = n TO rCnt
 kS = k - n + 1
 DO CASE
 CASE opgACVl = 1
  dxVl = clcSMA('dx', kS, k)
  dnVl = clcSMA('dn', kS, k)
 CASE opgACVl = 2
  dxVl = clcEMA('dx', dxVl, k, n)
  dnVl = clcEMA('dn', dnVl, k, n)
 CASE opgACVl = 3
  dxVl = clcSMA('dx * vlm', kS, k) / clcSMA('vlm', kS, k)
  dnVl = clcSMA('dn * vlm', kS, k) / clcSMA('vlm', kS, k)
 ENDCASE
 GO k
 REPLACE dmrkr WITH dxVl / (dxVl + dnVl)
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'dmrkr'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'DeMarker(' + TRANSFORM(n) + ')', prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .F.)

Скользящая средняя осциллятора (OsMA, MACD-гистограмма)

Индикатор Скользящая средняя осциллятора (Moving Average Of Oscillator, OsMA) – это разность между осциллятором и его сглаживанием. Частным случаем OsMA является MACD-гистограмма (рис. 35), в которой осциллятор – это основная линия MACD, а его сглаживание – сигнальная линия.

MACD-гистограмма

Рис. 35. OsMA (MACD-histogram)

Расчет:

OSMA = MACD – MACD_Signal

Использование:

Рост гистограммы говорит об увеличении силы быков. Это сигнал для сделок на повышение.
Падение гистограммы означает усиление медведей. Это сигнал для сделок на понижение.
Дивергенция означает ослабление тренда и возможную сильную коррекцию или разворот. Чем больше длина кадра графика цены, тем сильнее сигнал.

Программа:

* Click-обработчик кнопки MACD
thisForm.CmdMACD.Click(.T.)

Средний истинный диапазон (ATR)

Индикатор Средний истинный диапазон (Average True Range, ATR, рис. 36), введенный У. Вайлдером (Welles Wilder), характеризует волатильность актива.

Average True Range

Рис. 36. ATR

Расчет:

TRi = max(Hi – Li, |Hi – C i - 1 |, |Li – Ci - 1|)
ATR = MA(TR, n)
По умолчанию n = 14.

Использование:

ATR используется и самостоятельно, и в составе иных индикаторов, например в ADX.
Нередко ATR достигает высоких значений в основаниях рынка после стремительного падения цен, вызванного паническими продажами. Продолжительные горизонтальные движения на вершинах рынка и во время консолидации дают низкие значения индикатора. Повышение ATR сигнализирует о смене тренда, понижение – о сохранении тренда.

Программа:

* Click-обработчик кнопки ATR
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 14
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 RETURN
ENDIF
ALTER TABLE (ali) ADD tr N(8, 2) ADD atr N(8, 2)
GO 1
clsVl = cls
hghVl = hgh
lwVl = lw
REPLACE tr WITH hgh - lw
FOR k = 2 TO rCnt
 GO k
 REPLACE tr WITH MAX(hgh - lw, ABS(hgh - clsVl), ABS(lw - clsVl))
 clsVl = cls
 hghVl = hgh
 lwVl = lw
NEXT
IF opgACVl = 2
 atrVl = 0.0
 FOR k = 1 TO n
  GO k
  atrVl = atrVl + tr
 NEXT
 atrVl = atrVl / n
ENDIF
FOR k = n + 1 TO rCnt
 DO CASE
 CASE opgACVl = 1
  atrVl = clcSMA('tr', k - n, k)
 CASE opgACVl = 2
  atrVl = clcEMA('tr', atrVl, k, n)
 CASE opgACVl = 3
  atrVl = clcSMA('tr * vlm', k - n, k) / clcSMA('vlm', k - n, k)
 ENDCASE
 REPLACE atr WITH atrVl
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'atr'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Average_True_Range(' + TRANSFORM(n) + ')', prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .F.)

Фракталы

Индикатор Фракталы (Fractals, рис. 37), предложенный Б. Вильямсом (B. Williams), подает сигналы покупки/продажи.

Fractals и Alligator

Рис. 37. Фракталы

Фракталы на покупку показаны красной точкой, а на продажу – синей.

Расчет:

Фрактал на покупку – это серия из пяти последовательных баров, в которой перед самым высоким максимумом и за ним находятся по два бара с более низкими максимумами.
Фрактал на продажу – это серия из пяти последовательных баров, в которой перед самым низким минимумом и за ним находятся по два бара с более высокими минимумами.
Фракталы на покупку и на продажу могут иметь общие бары.

Вариант использования:

Фракталы используются совместно с Аллигатором. Они подают следующие сигналы:

Текущий фрактал может быть преодолен либо в результате "поражения" (появления антагонистического фрактала), либо в результате появления нового фрактала в том же направлении. В этих случаях предыдущий сигнал отменяется, а отложенный ордер снимается.
Войти в рынок можно после преодоления первого фрактала на покупку (отсчет ведется от начала текущей торговой сессии), приняв и оценив сигналы других индикаторов Б. Вильямса (Alligator, АО и AC).

Эффективность:

Следование сигналам индикатора привело на тестовой выборке к сокращению портфеля.

Программа:

* Click-обработчик кнопки Fractals
clcltLlgtr(thisForm, .T., .F., 1)

Гатор осциллятор (Gator)

Гатор осциллятор (Gator Oscillator, рис. 38) показывает схождение/расхождения линий баланса Аллигатора.

Gator Oscillator

Рис. 38. Gator

Расчет:

Gator отображается в виде двух гистограмм:

Использование:

Gator упрощает визуальное определение наличия или отсутствия тренда. С помощью Gator хорошо видны интервалы сближения и переплетения (Аллигатор спит) и интервалы расхождения (Аллигатор бодрствует) Линий Баланса.

Программа:

* Click-обработчик кнопки Gator
clcltLlgtr(thisForm, .F., .T., 1)

Индекс относительной бодрости (RVI)

Индекс относительной бодрости (Relative Vigor Index, RVI) исходит из того, что на бычьем рынке цена закрытия, как правило, выше цены открытия, а на медвежьем рынке наблюдается обратная картина. RVI выводится в виде базовой (красной) и сигнальной (зеленой) линий (рис. 39)

Relative Vigor Index

Рис. 39. RVI

Расчет:

Ai = (Ci – Oi + 2 * (Ci – 1 – Oi – 1) + 2 * (Ci – 2 – Oi – 2) + Ci – 3 – Oi – 3) / 6
B = (Hi – Li + 2 * (Hi – 1 – Li – 1) + 2 * (Hi – 2 – Li – 2) + Hi – 3 – Li – 3) / 6
NM = sum(A, n)
DNM = sum(B, n)
RVI = NM / DNM
RVIS = (RVI + 2 * RVIi – 1 + 2 * RVIi – 2 + RVIi – 3) / 6
По умолчанию n = 10.

Использование:

Пересечение линий RVI –это сигнал на покупку или продажу.

Программа:

* Click-обработчик кнопки RVI
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 10
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 RETURN
ENDIF
ALTER TABLE (ali) ADD a N(8, 2) ADD b N(8, 2) ADD rvi N(8, 4) ADD rvis N(8, 4)
DIMENSION arrAVl[4], arrBVl[4]
FOR k = 1 TO 4
 GO k
 arrAVl[k] = cls - pn
 arrBVl[k] = hgh - lw
NEXT
FOR k = 5 TO rCnt
 aVl = (arrAVl[4] + 2.0 * arrAVl[3] + 2.0 * arrAVl[2] + arrAVl[1]) / 6.0
 bVl = (arrBVl[4] + 2.0 * arrBVl[3] + 2.0 * arrBVl[2] + arrBVl[1]) / 6.0
 GO k - 1
 REPLACE a WITH aVl b WITH bVl
 FOR k2 = 1 TO 3
  arrAVl[k2] = arrAVl[k2 + 1]
  arrBVl[k2] = arrBVl[k2 + 1]
 NEXT
 GO k
 arrAVl[4] = cls - pn
 arrBVl[4] = hgh - lw
NEXT
GO rCnt
REPLACE a WITH aVl b WITH bVl
FOR k = n + 4 TO rCnt
 kS = k - n + 1
 nmVl = clcSMA('a', kS, k)
 dnmVl = clcSMA('b', kS, k)
 GO k
 REPLACE rvi WITH nmVl / dnmVl
NEXT
DIMENSION arrRSVl[4]
k2 = 0
FOR k = n + 4 TO n + 7
 GO k
 k2 = k2 + 1
 arrRSVl[k2] = rvi
NEXT
FOR k = n + 8 TO rCnt
 rsVl = (arrRSVl[4] + 2.0 * arrRSVl[3] + 2.0 * arrRSVl[2] + arrRSVl[1]) / 6.0
 GO k - 1
 REPLACE rvis WITH rsVl
 FOR k2 = 1 TO 3
  arrRSVl[k2] = arrRSVl[k2 + 1]
 NEXT
 GO k
 arrRSVl[4] = rvi
NEXT
GO rCnt
REPLACE rvis WITH rsVl
nFldsVl = 2
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'rvi'
arrFlds[2] = 'rvis'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
arrGTps[2] = 'L 0.0 1.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Relative_Vigor_Index(' + TRANSFORM(n) + ')', '', nFldsVl, @arrFlds, @arrGTps, .F., .T.)

Накопление/Распределение Вильямса (WA/D)

Накопление/Распределение Вильямса (Williams Accumulation/Distribution, WA/D) иллюстрирует рис. 40.

Williams Accumulation/Distribution

Рис. 40. WA/D

Расчет:

CumWADi = CumWadi – 1 + WADi, где
WADi = Pi – TL, если Pi > Pi – 1;
WADi = Pi – TH, если Pi < Pi – 1;
WADi = 0, если Pi = Pi – 1;
TH = max(Pi – 1, Hi) – истинный диапазон максимумов;
TL = min(Pi – 1, Li) – истинный диапазон минимумов.

Использование:

Показывает распределение актива, если цена формирует новые максимумы, а индикатор нового максимума сформировать не может.
Показывает накопление актива, если цена достигает новых минимумов, а индикатор достигнуть нового минимума не может.

Программа:

* Click-обработчик кнопки WA/D
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, 1)
 RETURN
ENDIF
ALTER TABLE (ali) ADD cumwad N(8, 2)
GO TOP
pVl = cls
cumwadVl = 0
FOR k = 2 TO rCnt
 GO k
 cumwadVl = cumwadVl + IIF(cls > pVl, cls - MIN(pVl, lw), IIF(cls < pVl, cls - MAX(pVl, hgh), 0))
 REPLACE cumwad WITH cumwadVl
 pVl = cls
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'cumwad'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Williams_Accumulation_Distribution', 'Close', nFldsVl, @arrFlds, @arrGTps, .T., .T.)

Скорость изменения цены (ROC)

Пример графика индикатора Скорость изменения цены (Price Rate Of Change, ROC) показан на рис. 41.

Price Rate Of Change

Рис. 41. ROC

Расчет:

ROCi = (Pi – Pi – n) / Pi – n * 100
По умолчанию n = 5.

Использование:

Аналогичен индикатору Momentum.

Программа:

* Click-обработчик кнопки ROC
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 5
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n + 1)
 RETURN
ENDIF
ALTER TABLE (ali) ADD roc N(8, 2)
FOR k = n + 1 TO rCnt
 GO k - n
 mVVl = mv
 GO k
 REPLACE roc WITH 100.0 * (mV - mVVl) / mVVl
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'roc'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
prcKndVl = LEFT(prcKndVl, RAT('_',prcKndVl) - 1)
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Price_Rate_Of_Change(' + TRANSFORM(n) + ')', prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)

Сглаженная скорость изменения (SROC)

График индикатора Сглаженная скорость изменения (Smoothed Rate Of Change, SROC) показан на рис. 42.

Smoothed Rate Of Change

Рис. 42. SROC

Расчет:

SROC = MA(P, n) / MA(P, n – k) * 100
По умолчанию P – это C, n = 10, а величина сглаживающего коэффициента k = 5.

Использование:

SROC вырабатывает меньше сигналов, чем ROC, но их достоверность выше.

Программа:

* Click-обработчик кнопки SROC
clcltMA(thisForm, 2, 5)

Стандартное отклонение (SD)

График индикатора Стандартное отклонение (Standard Deviation, SD) приведен на рис. 43.

Standard Deviation

Рис. 43. SD (график SMA показан красным цветом)

Расчет:

SDj = SQRT(sum(Pi – MAi(P, n))^2) / n), где i = j – n + 1, j.
По умолчанию n = 10.

Использование:

При больших значениях SD рынок сильно волатилен (цены разбросаны относительно скользящего среднего).
При маленьких значениях индикатора рынок более стабилен (цены близки к скользящему среднему).
Интерпретация SD:

SD используется при расчете иных индикаторов, например BB.

Программа:

* Click-обработчик кнопки SD
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 10
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 RETURN
ENDIF
ALTER TABLE (ali) ADD ma N(8, 2) ADD sd N(8, 2)
IF opgACVl = 2
 GO n
 maVl = mV
ENDIF
FOR k = n TO rCnt
 kS = k - n + 1
 * See someProcs.prg
 DO CASE
 CASE opgACVl = 1
  maVl = clcSMA('mV', kS, k)
 CASE opgACVl = 2
  maVl = clcEMA('mV', maVl, k, n)
 CASE opgACVl = 3
  maVl = clcSMA('mV * vlm', kS, k) / clcSMA('vlm', kS, k)
 ENDCASE
 GO k
 REPLACE ma WITH maVl
NEXT
n2 = 2 * n
FOR k = n2 TO rCnt
 kS = k - n + 1
 * See someProcs.prg
 sdVl = clcSD('mV', 'ma', kS, k)
 GO k
 REPLACE sd WITH sdVl
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'sd'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, 'Standard_Deviation(' + TRANSFORM(n) + ')',;
 prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .F.)

Тройная экспоненциально-сглаженная скользящая средняя (TRIX)

График индикатора Тройная экспоненциально-сглаженная скользящая средняя (Triple Exponential Moving Average, TRIX) показан на рис. 44.

Triple Exponential Moving Average

Рис. 44. TRIX

Расчет:

LnP = ln(P)
mLnP = EMA(LnP, n)
mLnP2 = EMA(mLnP, n)
mLnP3 = EMA(mLnP2, n)
TRIXi = (1 – mLnP3i / mLnP3i – 1) * 100
По умолчанию P – это C и n = 7.

Использование:

TRIX работает как фильтр, который отсеивает движения цены актива, незначительные по отношению к большему тренду. Значения TRIX колеблются около нулевой линии.

Программа:

* Click-обработчик кнопки TRIX
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 7
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, 3 * n + 1)
 RETURN
ENDIF
ALTER TABLE (ali) ADD lnp N(8, 2) ADD mlnp N(8, 2) ADD mlnp2 N(8, 2) ADD mlnp3 N(8, 2) ADD trix N(8, 4)
REPLACE lnp WITH LOG(mV) ALL
GO n
mlnpVl = lnp
FOR k = n TO rCnt
 mlnpVl = clcEMA('lnp', mlnpVl, k, n)
 GO k
 REPLACE mlnp WITH mlnpVl
NEXT
n2 = 2 * n
GO n2
mlnpVl2 = mlnp
FOR k = n2 TO rCnt
 mlnpVl2 = clcEMA('mlnp', mlnpVl2, k, n)
 GO k
 REPLACE mlnp2 WITH mlnpVl2
NEXT
n3 = 3 * n
GO n3
mlnpVl3 = mlnp2
FOR k = n3 TO rCnt
 mlnpVl3 = clcEMA('mlnp2', mlnpVl3, k, n)
 GO k
 REPLACE mlnp3 WITH mlnpVl3
NEXT
FOR k = n3 + 1 TO rCnt
 GO k - 1
 mlnpVl3 = mlnp3
 GO k
 REPLACE trix WITH (1.0 - mlnp3 / mlnpVl3) * 100.0
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'trix'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, 'Triple_Exponential_Moving_Average(' + TRANSFORM(n) + ')',;
 prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)

Вертикально-горизонтальный фильтр (VHF)

График индикатора Вертикально-горизонтальный фильтр (Vertical Horizontal Filter, VHF) приведен на рис. 45.

Vertical Horizontal Filter

Рис. 45. VHF

Расчет:

VHFi = Ai / Bi
Ai = Hj – Lj)
Bi = sum(abs(Pj – Pj – 1)), где
j = i – n + 1, i;
Hj и Lj – максимальная и минимальная цены взаданном временном промежутке из n интервалов, возвращаемые соответственно функциями clcMinP и clcMaxP.
По умолчанию P – это C и n = 28.
Сумма ценовых изменений возвращается функцией clcSPCH.

Использование:

VHF показывает, находятся ли цены в фазе тренда или в фазе застоя.

Программа:

* Click-обработчик кнопки VHF
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
n = 28
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 RETURN
ENDIF
ALTER TABLE (ali) ADD vhf N(8, 4)
GO TOP
FOR k = n TO rCnt
 aVl = clcMaxP(k, n) - clcMinP(k, n)
 bVl = clcSPCH('mV', k, n)
 GO k
 REPLACE vhf WITH aVl / bVl
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'vhf'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
prcKndVl = LEFT(prcKndVl, RAT('_',prcKndVl) - 1)
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Vertical_Horizontal_Filter(' + TRANSFORM(n) + ')', prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .F.)

Осциллятор объема (VO)

Осциллятор объема (Volume Oscillator, VO) – это разность двух МА объема продаж, выраженная в процентах (рис. 46).

Volume Oscillator

Рис. 46. VO

Расчет:

VO = (MA(V, Shrt) – MA(V, Lng)) / MA(V, Lng) * 100
По умолчанию MA – это EMA, Shrt = 5 и Lng = 10.

Использование:

VO может использоваться с целью определения направления движения основной тенденции объема (повышение или снижение). Так, повышение VO выше нуля может означать, что краткосрочный объемный тренд преобладает над долгосрочным объемным трендом.

Программа:

* Click-обработчик кнопки VO
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
sVl = 5
lVl = 10
n = 9
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, lVl)
 RETURN
ENDIF
ALTER TABLE (ali) ADD vo N(8, 4)
IF opgACVl = 2
 GO sVl
 maSVl = vlm
 GO lVl
 maLVl = vlm
ENDIF
FOR k = lVl TO rCnt
 rStrt1 = k - sVl + 1
 rStrt2 = k - lVl + 1
 DO CASE
 CASE opgACVl = 1 OR opgACVl = 3
  maSVl = clcSMA('vlm', rStrt1, k)
  maLVl = clcSMA('vlm', rStrt2, k)
 CASE opgACVl = 2
  maSVl = clcEMA('vlm', maSVl, rStrt1, sVl)
  maLVl = clcEMA('vlm', maLVl, rStrt2, lVl)
 ENDCASE
 GO k
 REPLACE vo WITH (maSVl - maLVl) / maLVl * 100
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'vo'
arrGTps[1] = 'L 1.0 0.0 0.0 1.0'
prcKndVl = LEFT(prcKndVl, RAT('_', prcKndVl, 2) - 1);
 + 'Volume' + SUBSTR(prcKndVl, RAT('_', prcKndVl))
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl,;
 'Volume_Oscillator(' + TRANSFORM(sVl) + '-' + TRANSFORM(lVl) + ')',;
 prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)

Канал цен (PCU)

Индикатор Канал цен (Price Channel Upper, PCU, рис. 47) – это сдвинутая вверх и вниз скользящая средняя.

Price Channel Upper

Рис. 47. PCU (график цены показан черным цветом)

Расчет:

U = MA(P, n) * (1 + u / 100);
L = MA(P, n) * (1 – d / 100), где
U – верхняя полоса канала цен;
L – нижняя полоса канала цен;
u – величина сдвига вверх;
d – величина сдвига вниз.
По умолчанию u = d = 10.

Использование:

При удачном выборе параметров канал будет соответствовать равновесному состоянию рынка, а все выходы цены за его пределы должны сопровождаться возвращением ее назад. Параметры u и d подбираются так, что около 95% цен располагаются внутри канала.

Программа:

* Click-обработчик кнопки PCU
clcltMA(thisForm, 3, 0, 0.02, 0.015)

Индикатор направленного изменения (+/–DM)

Индикатор направленного изменения (Directional Movement, +/–DM, рис. 48), введенный У. Вайлдером (Welles Wilder), использует фильтрацию по темпам и направлению изменения цены.

Directional Movement

Рис. 48. +/–DM

На рис. 48 +DI выведен красным цветом, а DI – зеленым.

Расчет:

Порядок расчета приведен при описании индикатора ADX.

Использование:

Предназначен для выявления трендовых участков рынка. Индикатор показывает долговременную тенденцию рынка и силу тренда.
Сигналы индикатора (см. также ADX):

Программа:

* Click-обработчик кнопки +/-DM
clcltADX(thisForm, 2, 14)

Индикатор аккумуляции объема (VA)

Индикатор аккумуляции объема (Volume Accumulation, VA, рис. 49) предложен M. Чайкиным (M. Chaikin) и Д. Ламбертом (D. Lambert).

Volume Accumulation

Рис. 49. VA

Расчет:

VAi = VAi – 1 + ((Ci – Li) – (Hi – Ci)) / (Hi – Li) * Vi

Использование:

Индикатор VA схож с OBV.
Сигналы VA:

Программа:

* Click-обработчик кнопки VA
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, 1)
 RETURN
ENDIF
ALTER TABLE (ali) ADD va N(20, 2)
GO TOP
REPLACE va WITH IIF(hgh - lw = 0, 0, (2 * cls - lw - hgh) / (hgh - lw))
vaVl = va
FOR k = 2 TO rCnt
 GO k
 vaVl = vaVl + IIF(hgh - lw = 0, 0, (2 * cls - lw - hgh) / (hgh - lw))
 REPLACE va WITH vaVl
NEXT
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'va'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, 'Volume_Accumulation', '', nFldsVl, @arrFlds, @arrGTps, .F., .T.)

Объемный тренд цены (VPT)

Объемный тренд цены (Volume Price Trend, VPT, рис. 50), учитывает не только направление изменения цены, но и сопутствующий объем.

Volume Price Trend

Рис. 50. VPT

Расчет:

VPTi = VPTi – 1 + Vi * (Pi – Pi – 1) / Pi – 1

Использование:

Большое положительное значение свдетельствует о перекупленности актива, большое отрицательное значение – о перепроданнности актива.

Программа:

* Click-обработчик кнопки VPT
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, 1)
 RETURN
ENDIF
ALTER TABLE (ali) ADD vpt N(20, 2)
GO TOP
mVVl = mV
vptVl = 0
REPLACE vpt WITH vptVl
FOR k = 2 TO rCnt
 GO k
 vptVl = vptVl + vlm * (mV - mVVl) / mVVl
 REPLACE vpt WITH vptVl
 mVVl = mV
NEXT
REPLACE vpt WITH 0.01 * vpt ALL
nFldsVl = 1
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'vpt'
arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
prcKndVl = LEFT(prcKndVl, RAT('_', prcKndVl) - 1)
svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, 'Volume_Price_Trend',;
 prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)

Осциллятор (OSC)

Осциллятор (Oscillator, OSC, рис. 51.) выявляет движения цен с заданным периодом.

Oscillator, Price Oscillator

Рис. 51. OSC

Иное название индикатора – Price Oscillator.

Расчет:

OSC = MA(P, Lng) – MA(P, Shrt), где

Lng – протяженность длинного временного промежутка, по умолчанию равна 34;
Shrt – протяженность короткого временного промежутка, по умолчанию равна 5.
По умолчанию P – это M.

Использование (см. также AO)

Большие положительные значения индикатора OSC означают завышенные цены по отношению к долгосрочной тенденции и дают сигнал к покупке. И наоборот, большие отрицательные значения индикатора дают сигнал к продаже.
Классический сигнал: бычье расхождение (медвежье схождение). При этом на явном подтвержденном бычьем тренде пересечение OSC снизу-вверх нулевой линии дает сигнал покупки (более ранним сигналом является разворот находящегося ниже нуля индикатора в направлении динамики тренда). И наоборот для медвежьего рынка.
Сигналы против тренда – разворот и выход индикатора из зоны перекупленности/перепроданности.
В итоге:

Программа:

* Click-обработчик кнопки OSC
clcltAO(thisForm, 2, 5, 34)

Временные зоны Фибоначчи (FTZ)

Временные зоны Фибоначчи (Fibonacci Time Zones, FTZ, рис. 52) – это зоны, между вертикальными линиями, проведенными в точках временной оси, отвечающим числам Фибоначчи: 0, 1, 1, 2, 3, 5, 8, … (каждое число последовательности равно сумме двух предыдущих чисел).

Fibonacci Time Zones

Рис. 52. FTZ

Расчет:

Первая линия FTZ проходит через точку минимума (максимума) графика, а следующие линии проводятся через точки временной оси, расстояние между которыми растет в соответствии с числами Фибоначчи.
Порядок построения FTZ следующий:

  1. Найти точку разворота t0.
  2. Найти последующий локальный экстремум (точка t1 на оси времени).
  3. Задать длину интервала Фибоначчи fbn = t1t0.
  4. Построить временные зоны Фибоначчи, используя найденное значение fbn.

Использование:

FTZ предполагает значительные изменения цены либо на самих линиях, либо около них.
FTZ более надежны при долгосрочном анализе ценовых колебаний и менее точны на краткосрочных графиках.

Программа:

* Click-обработчик кнопки FTZ
LOCAL pDtVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt
IF NOT prpsT(thisForm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, 3)
 RETURN
ENDIF
LOCAL ali2, tbl2
ali2 = 'stcksTL'
tbl2 = _tmpPth + ali2
dVl = DAY(pDtVl)
SELECT mV, tm, dy;
 FROM (ali);
 WHERE dy = dVl;
 INTO TABLE (tbl2)
ALTER TABLE (ali2) ADD tmx I ADD tpnts C(2) ADD ftz N(8, 2) ADD ftz2 N(8, 2)
REPLACE tmx WITH VAL(CHRTRAN(tm, ':', '')) ALL
* Try to understand a kind of trend (D, U or S)
* Try D trend first
STORE 0 TO dtVlD, mvMVlD, dtVlD2, mvMVlD2, dtVlU, mvMVlU, dtVlU2, mvMVlU2
rCntVl = RECCOUNT()
tLD = trnd('d', @dtVlD, @mvMVlD, @dtVlD2, @mvMVlD2, rCntVl)
LOCATE FOR tpnts = 't'
rFTZDVl = IIF(FOUND(), RECNO(), 0)
LOCATE FOR tpnts = 'm'
rFTZDVl2 = IIF(FOUND(), RECNO(), 0)
* Try U trend next
REPLACE tpnts WITH '' ALL
tLU = trnd('u', @dtVlU, @mvMVlU, @dtVlU2, @mvMVlU2, rCntVl)
LOCATE FOR tpnts = 't'
rFTZUVl = IIF(FOUND(), RECNO(), 0)
LOCATE FOR tpnts = 'm'
rFTZUVl2 = IIF(FOUND(), RECNO(), 0)
IF tLU > tLD
* tk = 'U'
 rFTZVl = rFTZUVl
 dFTZVl = rFTZUVl2 - rFTZUVl
ELSE
* tk = 'D'
 rFTZVl = rFTZDVl
 dFTZVl = rFTZDVl2 - rFTZDVl
ENDIF
IF dFTZVl = 0
 MESSAGEBOX("No Fibonacci Time Zones found")
 RETURN
ENDIF
CALCULATE MAX(mV) TO mxVl
CALCULATE MIN(mV) TO mnVl
REPLACE ftz WITH mnVl ftz2 WITH mnVl ALL
fbVl = 0
rFTZVlCrrnt = rFTZVl
fbVlPrv = 0
fbVlCrrnt = 1
DO WHILE rFTZVlCrrnt <= rCntVl
 GO rFTZVlCrrnt
 REPLACE ftz WITH mxVl
 rFTZVlCrrnt = rFTZVl + fbVlCrrnt * dFTZVl
 fbVlTmp = fbVlPrv + fbVlCrrnt
 fbVlPrv = fbVlCrrnt
 fbVlCrrnt = fbVlTmp
ENDDO
ALTER TABLE (ali2) DROP tmx
nFldsVl = 3
DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
arrFlds[1] = 'mV'
arrGTps[1] = 'L 0.0 0.0 0.0 0.0'
arrFlds[2] = 'ftz'
arrGTps[2] = 'V 1.0 0.0 0.0 0.0'
arrFlds[3] = 'ftz2'
arrGTps[3] = 'S 1.0 0.0 0.0 0.0'
pVl = RAT('_', prcKndVl) - 1
svMVDtFls(chckNldVl, ali2, IDSVl, pDtVl, 'Fibonacci_Time_Zones',;
 LEFT(prcKndVl, pVl), nFldsVl, @arrFlds, @arrGTps, .F., .T.)

Mesa-синусоиды (MESASWI)

Индикатор Mesa-синусоиды (Mesa Sine Wave, MESASWI, рис. 53), введенный Дж. Элхерсом (John Elhers), строится на основе двух синусоид Sine (красная линия) и Lead Sine (зеленая линия) для определения, находится ли рынок в циклической стадии или в стадии тренда.

Mesa Sine Wave

Рис. 53. MESASWI

Расчет:

RealParti = Σnj = 0sin(2π* j / (n + 1)) * Ci – n + j
ImagParti = Σnj = 0cos(2π * j / (n + 1)) * Ci – n + j
if ImagParti < 0 then
 DCPhasei = 3π / 2
else
 if ImagParti > 0.001 then
  DCPhasei = atan(RealParti / ImagParti) + π / 2
 else
  DCPhasei = π / 2
if DCPhasei > 3π / 2 then
 DCPhasei = DCPhasei – 2π
Sinei = sin(DCPhasei)
LeadSinei = sin(DCPhasei + π / 4)
Sinei = SMA(Sinei, n)
LeadSinei = SMA(LeadSinei, n)
По умолчанию n = 9

Использование:

При четком направленном движении обе линии Sine и Lead Sine движутся параллельно и показывают своим положением друг относительно друга направление тренда; при боковом же тренде линия Sine быстро реагирует на развороты рынка.
Открывайте длинную/закрывайте короткую позицию, когда Lead Sine ниже, чем Sine.
Открывайте короткую/закрывайте длинную позицию, когда Lead Sine выше, чем Sine.
Состояние цикла.

Состояние тренда.
Анализ ситуации ведется по иным индикаторам, использующим, в частности, скользящие средние.

Эффективность:

Следование сигналам индикатора привело на тестовой выборке к сокращению портфеля.

Программа:

* Click-обработчик кнопки MESASWI
clcltMESASWI(thisForm, 1)

* Расчет и оценка индикатора MESASWI
PROCEDURE clcltMESASWI(oFrm, hwVl, thsVl)
 LOCAL pDtVl, IDSVl, opgACVl, slctVl, prcKndVl, ali, rCnt
 n = 14
 IF hwVl = 1
  LOCAL chckNldVl
  prpsVl = prpsT(oFrm, @pDtVl, @IDSVl, @opgACVl, @chckNldVl, @slctVl, @prcKndVl, @ali, @rCnt, n)
 ELSE
  LOCAL dsMntVl, smVl, kVl, fpVl
  prpsVl = prpsT2(oFrm, thsVl, @pDtVl, @dsMntVl, @IDSVl, @opgACVl, @slctVl, @prcKndVl, @ali, @rCnt, @smVl, @fpVl, @kVl, 2)
 ENDIF
 IF NOT prpsVl
  RETURN
 ENDIF
 ALTER TABLE (ali) ADD sn N(8, 4) ADD lsn N(8, 4) ADD msn N(8, 4) ADD mlsn N(8, 4)
 STORE 0.0 TO rpVl, mpVl
 piVl = PI()
 p2Vl = 0.5 * piVl
 p3Vl = 3.0 * p2Vl
 p4Vl = 0.25 * piVl
 FOR k = n + 1 TO rCnt
  clcRPMP('cls', k, n, @rpVl, @mpVl)
  DO CASE
  CASE mpVl < 0.0
   dcVl = p3Vl
  CASE mpVl > 0.001
   dcVl = ATAN(rpVl / mpVl) + p2Vl
  OTHERWISE
   dcVl = p2Vl
  ENDCASE
  dcVl = IIF(dcVl > p3Vl, dcVl - 2.0 * piVl, dcVl)
  GO k
  REPLACE sn WITH SIN(dcVl) lsn WITH SIN(dcVl + p4Vl)
 NEXT
 FOR k = n TO rCnt
  kS = k - n + 1
  msnVl = clcSMA('sn', kS, k)
  mlsnVl = clcSMA('lsn', kS, k)
  GO k
  REPLACE msn WITH msnVl mlsn WITH mlsnVl
 NEXT
 nFldsVl = 2
 DIMENSION arrFlds[nFldsVl], arrGTps[nFldsVl]
 arrFlds[1] = 'msn'
 arrFlds[2] = 'mlsn'
 arrGTps[1] = 'L 1.0 0.0 0.0 0.0'
 arrGTps[2] = 'L 0.0 1.0 0.0 0.0'
 ttlVl = 'Mesa_Sine_Wave(' + TRANSFORM(n) + ')'
 IF hwVl = 1
  svMVDtFls(chckNldVl, ali, IDSVl, pDtVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps, .F., .T.)
 ELSE
  svMVDtFls2(ali, IDSVl, pDtVl, dsMntVl, ttlVl, prcKndVl, nFldsVl, @arrFlds, @arrGTps)
  *
  smVl2_2 = clcXptdPrft(smVl, dsMntVl, pDtVl, fpVl)
  CALCULATE MAX(mV) TO mVSllVl
  smVl2 = smVl
  STORE 0 TO stcsMntVl, mntBsVl, mntSllsVl, fVl, smLftVl2
  bWhnCrssBT('mlsn', 'msn', kVl, @smVl2, smVl, fpVl, @mntBsVl, @mntSllsVl, @stcsMntVl, @smLftVl2, @fVl, @mVSllVl, pDtVl)
  frmPdt(oFrm, stcsMntVl, smVl2, smLftVl2, mntBsVl, mntSllsVl, smVl, fVl, smVl2_2)
 ENDIF
ENDPROC && clcltMESASWI

Оценка эффективности индикаторов ТА

Порядок оценки эффективности индикатора ТА

Имитируется работа автоматического трейдера на базе одного или нескольких индикаторов. (Этот трейдер заключает сделки в автоматическом режиме.) Автомат продает и покупает выбранный актив в заданном временном интервале, работая по следующей схеме:

  1. Задать сумму S1, предназначенную для покупки актива. Сумма каждой покупки не превышает S1.
  2. Задать дату начала торгов и число торговых дней (для указанного временного интервала должны быть загружены статистические данные).
  3. Запустить автомат с указанными выше параметрами.
  4. Вычислить после завершения последнего торгового дня стоимость S2 портфеля автомата и его рентабельность с учетом комиссии Fee инвестиционного фонда P = S2 – S1 – Fee (комиссия одной сделки равна 0.0014 от суммы сделки).

Оценка выполняется на недельном интервале: торги начинаются в понедельник и завершаются в пятницу.

Форма оценки эффективности индикатора ТА

Приведенная схема поддерживается показанной на рис. 54 формой.

Форма для испытания автоматического биржевого трейдера

Рис. 54. Форма оценки автоматического трейдера

В качестве результата в форме выводятся рентабельность автомата, число покупок и продаж, а также возможная выгода (выводится в поле Рентабельность 2), вычисляемая исходя из предположения, что в день проводится одна сделка – покупка по минимальной цене дня и продажа по максимальной цене дня.
График цены одного из употребляемых в режиме оценки активов показан на рис. 55 (по оси X отложены номера точек, расстояние между точками равно 5 мин.).

График цены акций в тестовом временном интервале

Рис. 55. ОАО Газпром: график цены в тестовом интервале

Вспомогательные процедуры

Приводимые ниже процедуры prpsT2, svMVDtFls2, nldMv2, clcXptdPrft, bWhnCrssBT, oneBuy, oneSll и frmPdt используются при оценке эффективности индикаторов ТА.

*
* Подготовка к оценке эффективности индикатора ТА (режим одной недели)
FUNCTION prpsT2(oForm, thsVl, pDtVl, dsMntVl, IDSVl, opgACVl, chckNldVl, slctVl, prcKndVl, ali, rCnt, smVl, fpVl, kVl, kXVl)
 WITH oForm
  kVl = .SpinnerSNmbr.Value
  IF kVl > kXVl
   STORE 0 TO .TxtMntBs.Value, .TxtMntSlls.Value, .TxtPrft.Value
   MESSAGEBOX('kVl must be < ' + TRANSFORM(kXVl + 1))
   RETURN .F.
  ENDIF
  pDtVl = .PDt.Value
  dsMntVl = .TxtDsMnt.Value
  IDSVl = .IDSlct
  opgACVl = .OpgAC.Value
  opgACVl2 = .OpgAC2.Value
  .LabelNdctr.Caption = thsVl.Caption
  STORE 0 TO .TxtPrft.Value, .TxtFPL.Value, .TxtMntBs.Value, .TxtMntSlls.Value
  smVl = .TxtSm.Value
  fpVl = 0.01 * .TxtFP.Value
 ENDWITH
 DO CASE
 CASE opgACVl2 = 2
  slctVl = 'hgh'
  prcKndVl = 'High_'
 CASE opgACVl2 = 3
  slctVl = 'lw'
  prcKndVl = 'Low_'
 CASE opgACVl2 = 4
  slctVl = 'cls'
  prcKndVl = 'Close_'
 CASE opgACVl2 = 5 OR opgACVl2 < 2
  slctVl = '0.5 * (hgh + lw)'
  prcKndVl = 'Median_'
 CASE opgACVl2 = 6
  slctVl = '(hgh + lw + cls) / 3.0'
  prcKndVl = 'Typical_'
 ENDCASE
 DIMENSION rrMV[3]
 rrMV[1] = 'S'
 rrMV[2] = 'E'
 rrMV[3] = 'V'
 prcKndVl = prcKndVl + rrMV[opgACVl] + 'MA'
 pDtVl0 = pDtVl - 3
 pDtVl2 = pDtVl + dsMntVl - 1
 ali0 = 'stcksTrdngF'
 ali = 'crStcksTrdng'
 tbl = _tmpPth + ali
 SELECT &slctVl AS mV, pn, cls, hgh, lw, vlm, DAY(dt) AS dy,;
  TRANSFORM(HOUR(dt)) + ":" + IIF(MINUTE(dt) > 5, TRANSFORM(MINUTE(dt)),;
   '0' + TRANSFORM(MINUTE(dt))) AS tm;
  FROM (ali0);
  WHERE IDS = IDSVl;
   AND TTOD(dt) BETWEEN pDtVl0 AND pDtVl2;
  ORDER BY dt;
  INTO TABLE (tbl)
 rCnt = _TALLY
 IF rCnt = 0
  MESSAGEBOX("No records selected")
  RETURN .F.
 ENDIF
 RETURN .T.
ENDFUNC && prpsT2
*
* Выгрузка индикатора ТА в mVDt-файлы (режим оценки)
PROCEDURE svMVDtFls2(ali, IDSVl, pDtVl, dsMntVl, ttlVl, prcKndVl, n, arrFlds, arrGTps)
 LOCAL k, kFFlsWrdVl, mvBsVl, mVDtVl, flNmVl
 * Kind of files
 FOR k = 1 TO n
  mVDtVl = 'mVDt' + TRANSFORM(k)
  flNmVl = _prjPth + mVDtVl + '.txt'
  IF k = 1
   kFFlsWrdVl = mVDtVl + _NEXTLINE + arrGTps[k]
   nldMv2(ali, IDSVl, pDtVl, dsMntVl, 'ttl2.txt', ttlVl, prcKndVl, arrFlds[k], flNmVl)
  else
   kFFlsWrdVl = kFFlsWrdVl + _NEXTLINE + mVDtVl + _NEXTLINE + arrGTps[k]
   nldMv2(ali, IDSVl, pDtVl, dsMntVl, '', '', '', arrFlds[k], flNmVl)
  ENDIF
 NEXT
 * Create file kFFls2.txt with types of unloaded files
 STRTOFILE(kfFlsWrdVl, _prjPth + 'kFFls2.txt')
 MESSAGEBOX('mvDt-file' + IIF(n = 1, ' is', 's are') + ' created')
ENDPROC && svMVDtFls2 *
* Выгружает в файл fn график цены или индикатора ТА (поле mV) из таблицы ali
PROCEDURE nldMv2(ali, IDSVl, pDtVl, dsMntVl, ttlFnVl, ttlVl, prcKndVl, mV, fn)
 LOCAL f, dVl, pDtVl_, k
 dVl = DAY(pDtVl)
 IF NOT EMPTY(ttlFnVl)
  SEEK IDSVl IN stcks ORDER IDN
  SEEK stcks.IDC IN cmpns ORDER IDN
  SEEK stcks.IDK IN stcksKnds ORDER IDN
  pDtVl_ = CHRTRAN(TRANSFORM(pDtVl + 1), '.', '')
  stckVl = TRIM(cmpns.NmShrt) + '_' + TRIM(stcksKnds.NmShrt2)
  SELECT (ali)
  LOCATE FOR dy = dVl
  k = RECNO()
  * Title-file
  ttlWrd = stckVl + '_' + pDtVl_+ '_' + TRANSFORM(dsMntVl) + '_' + ttlVl + '_' + prcKndVl +;
   _NEXTLINE + '0' + _NEXTLINE + '1' + _NEXTLINE + TRANSFORM(RECCOUNT() - k + 1)
  STRTOFILE(ttlWrd, _prjPth + ttlFnVl)
 ENDIF
 f = FCREATE(fn)
 k = 0
 SCAN FOR dy >= dVl
  k = k + 1
  FPUTS(f, TRANSFORM(k) + ' ' + TRANSFORM(&mV))
 ENDSCAN
 FCLOSE(f)
ENDPROC && nldMv2 *
* Расчет ожидаемого дохода
FUNCTION clcXptdPrft(smVl, dsMntVl, pDtVl, fpVl)
 LOCAL smVl2, stcsMntVl, fVl, smLftVl2, m, dVl, rcVl, mXVl
 smVl2 = smVl
 STORE 0 TO stcsMntVl, fVl, smLftVl2
 FOR m = 0 TO dsMntVl - 1
  dVl = DAY(pDtVl + m)
  CALCULATE CNT() FOR dy = dVl TO dRcrdsMntVl
  LOCATE FOR dy = dVl
  FOR k = 1 TO 0.15 * dRcrdsMntVl
   DELETE && To miss day start
   SKIP
  NEXT
  CALCULATE MIN(mV) TO mNVl FOR dy = dVl
  LOCATE FOR mV = mNVl AND dy = dVl
  rcVl = RECNO()
  CALCULATE MAX(mV) TO mXVl FOR dy = dVl AND RECNO() > rcVl
  IF mXVl > 0
   oneBuy(smVl2, smVl, fpVl, mNVl, 0, @stcsMntVl, @smLftVl2, @fVl)
   oneSll(@smVl2, fpVl, mXVl, 0, stcsMntVl, smLftVl2, @fVl)
  ENDIF
 NEXT
 RETURN smVl2 - smVl - fVl
ENDFUNC && clcXptdPrft
*
* Покупай, когда fld пересекает fld2 снизу вверх. Подавай, когда fld пересекает fld2 сверху вниз
PROCEDURE bWhnCrssBT(fld, fld2, kVl, smVl2, smVl, fpVl, mntBsVl, mntSllsVl, stcsMntVl, smLftVl2, fVl, mVSllVl, pDtVl, mxVl)
 LOCATE FOR dy = DAY(pDtVl)
 DO WHILE &fld >= &fld2 AND NOT EOF()
  SKIP
 ENDDO
 DO WHILE NOT EOF()
  DO WHILE &fld <= &fld2 AND NOT EOF()
   SKIP
  ENDDO
  IF NOT EOF()
   IF MOD(kVl, 2) = 1
    oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
   ELSE
    IF stcsMntVl = 0 AND mV < mVSllVl
     oneBuy(smVl2, smVl, fpVl, mV, @mntBsVl, @stcsMntVl, @smLftVl2, @fVl, mxVl)
    ENDIF
   ENDIF
   DO WHILE &fld >= &fld2 AND NOT EOF()
    SKIP
   ENDDO
   IF NOT EOF()
    IF MOD(kVl, 2) = 1
     oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
    ELSE
     smTmpVl = mV * stcsMntVl + smLftVl2
     IF smTmpVl > smVl2
      oneSll(@smVl2, fpVl, mV, @mntSllsVl, @stcsMntVl, smLftVl2, @fVl, mxVl)
      mVSllVl = mV
     ENDIF
    ENDIF
   ENDIF
  ENDIF
 ENDDO
ENDPROC && bWhnCrssBT
*
* Расчет одной покупки
PROCEDURE oneBuy(smVl2, smVl, fpVl, mVVl, mntBsVl, stcsMntVl, smLftVl2, fVl, mxVl)
 LOCAL smVl3, smVl4
 mntBsVl = mntBsVl + 1
 smVl3 = MIN(smVl2, smVl)
 stcsMntVl = INT(smVl3 / mVVl)
 smVl4 = mVVl * stcsMntVl
 smLftVl2 = smVl2 - smVl4
 fVl = fVl + fpVl * smVl4
 IF NOT EMPTY(mxVl)
  REPLACE bsg WITH mxVl
 ENDIF
ENDPROC && oneBuy
*
* Расчет одной продажи
PROCEDURE oneSll(smVl2, fpVl, mVVl, mntSllsVl, stcsMntVl, smLftVl2, fVl, mxVl)
 LOCAL smVl4
 smVl4 = mVVl * stcsMntVl
 smVl2 = smVl4 + smLftVl2
 stcsMntVl = 0
 mntSllsVl = mntSllsVl + 1
 fVl = fVl + fpVl * smVl4
 IF NOT EMPTY(mxVl)
  REPLACE ssg WITH mxVl
 ENDIF
ENDPROC && oneSll
*
* Вывод результата в форму
PROCEDURE frmPdt(oFrm, stcsMntVl, smVl2, smLftVl2, mntBsVl, mntSllsVl, smVl, fVl, smVl2_2)
 IF stcsMntVl > 0
  GO BOTTOM
  smVl2 = mV * stcsMntVl + smLftVl2
 ENDIF
 WITH oFrm
  .TxtMntBs.Value = mntBsVl
  .TxtMntSlls.Value = mntSllsVl
  .TxtPrft.Value = smVl2 - smVl - fVl
  .TxtPrft2.Value = smVl2_2
  .TxtFPL.Value = fVl
 ENDWITH
ENDPROC && frmPdt

Результаты оценки эффективности индикаторов ТА

Результаты оценки ниже описанных стратегий сведены в приводимую ниже таблицу (стартовый размер портфеля равен 5000 руб., приводятся оценки базового и модифицированного вариантов, заголовок последнего предваряется звездочкой).

АктивИндикаторСделки / Выгода, руб. *Сделки / Выгода, руб.Расчетная выгода, руб.
Газпром (ао) AC18 / –3422 / –280260
ADX27 / –3601 / –448
AO11 / –292 2 / –345
BB14 / –606 2 / –229
ChO26 / –516 3 / –421
Envelopes12 / –751 / –265
IKH12 / –2421 / –221
MA8 / –2451 / –349
MESASWI18 / –4771 / –452
Momentum59 / –8612 / –326
RSI1 / –31 / –3
SO11 / –2261 / –329
Fractals6 / –3641 / –323
Сбербанк (ап) AC8 / –1461 / –148420
ADX32 / –3346 / 35
AO9 / –2762 / –106
BB16 / –464 2 / –6
ChO20 / –3203 / –106
Envelopes3 / 232 / 69
IKH12 / –361 / –5
MA5 / –2641 / –133
MESASWI18 / –2161 / –127
Momentum63 / –9282 / –87
RSI2 / 641 / 4
SO10 / –2761 / –113
Fractals5 / –2491 / –54
Татнефть (ап) AC6 / 944 / 152519
ADX23 / –6872 / 29
AO5 / 143 5 / 143
BB6 / –154 2 / 2
ChO12 / –1973 / –24
Envelopes2 / 1222 / 122
IKH5 / –442 / 110
MA6 / 731 / 72
MESASWI9 / –2193 / 98
Momentum28 / –1649 / 31
RSI1 / 611 / 61
SO6 / –441 / –13
Fractals3 / –1451 / –102

Далее приводятся проверяемые стратегии и код, эту проверку обеспечивающий.

AC

Проверяемая стратегия:

* Click-обработчик кнопки AC
hwVl = IIF(thisForm.CheckShwSgnls.Value, 3, 2) && 2 to see curve; 3 - buy / sell signals
clcltAC(thisForm, hwVl, this)

ADX

Проверяемая стратегия:

* Click-обработчик кнопки ADX
clcltADX(thisForm, 3, 14, this)

AO

Проверяемая стратегия:

Замечание. Пересечение с нулевой линией анализируется, если в функции fndScndMnOrXCrssng снят комментарий.

* Click-обработчик кнопки AO
clcltAO(thisForm, 3, 5, 34, this)

BB

Проверяемая стратегия:

* Click-обработчик кнопки BB
clcltBB(thisForm, 2, this)

ChO

Проверяемая стратегия:

* Click-обработчик кнопки ChO
hwVl = IIF(thisForm.CheckShwSgnls.Value, 3, 2) && 2 to see curve; 3 - buy / sell signals
clcltChO(thisForm, hwVl, this)

Envelopes

Проверяемая стратегия:

* Click-обработчик кнопки Envelopes
hwVl = IIF(thisForm.CheckShwSgnls.Value, 3, 2) && 2 to see curve; 3 - buy / sell signals
clcltNvlps(thisForm, hwVl, this)

На рис. 56 черным цветом показан график цены, красным и зеленым – линии Envelopes; красные вертикальные линии показывают покупки, а синие – продажи актива.

Envelopes: покупки и продажи

Рис. 56. Покупки и продажи Envelopes

IKH

Проверяются две следующие стратегии (в приведенную выше таблицу внесены данные по стратегии 1).

  1. Покупай, когда TS пересекает KS снизу вверх. Продавай, когда TS пересекает KS сверху вниз.
  2. Покупай, когда цена пересекает SSB снизу вверх. Продавай, когда цена пересекает SSB сверху вниз.

* Click-обработчик кнопки IKH
clcltIKH(thisForm, 2, this)

MA

Проверяемая стратегия:

* Click-обработчик кнопки MA
hwVl = IIF(thisForm.CheckShwSgnls.Value, 5, 4) && 4 to see curve; 5 - buy / sell signals
clcltMA(thisForm, hwVl, 0, 0, 0, this)

MESASWI

Проверяемая стратегия:

* Click-обработчик кнопки MESASWI
clcltMESASWI(thisForm, 2, this)

Momentum

Проверяемая стратегия:

* Click-обработчик кнопки Momentum
hwVl = IIF(thisForm.CheckShwSgnls.Value, 3, 2) && 2 to see curve; 3 - buy / sell signals
clcltMmntm(thisForm, hwVl, this)

RSI

Проверяемая стратегия:

* Click-обработчик кнопки RSI
hwVl = IIF(thisForm.CheckShwSgnls.Value, 3, 2) && 2 to see curve; 3 - buy / sell signals
clcltRSI(thisForm, hwVl, this)

SO

Проверяемая стратегия:

* Click-обработчик кнопки SO
hwVl = IIF(thisForm.CheckShwSgnls.Value, 3, 2) && 2 to see curve; 3 - buy / sell signals
clcltSO(thisForm, hwVl, this)

На рис. 57 черным цветом показан график %K; красные вертикальные линии показывают покупки, а синие – продажи актива.

Stochastic Oscillator: покупки и продажи

Рис. 57. Покупки и продажи SO

Fractals

Проверяемая стратегия:

* Click-обработчик кнопки Fractals
hwVl = IIF(thisForm.CheckShwSgnls.Value, 3, 2) && 2 to see curve; 3 - buy / sell signals
clcltLlgtr(thisForm, .T., .F., hwVl, this)

Заключение

Наблюдаемые результаты говорят о том, что механическое следование сигналам индикаторов не гарантирует положительного сальдо. Более того, при нисходящем тренде примененные стратегии убыточны.
Таким образом, проблема выбора индикатора остается открытой.

Источники

  1. Murphy J. Technical Analysis of the Financial Markets.
  2. Wilder W. New Concepts in Technical Trading Systems.
  3. Williams L. R. How I made One Million Dollars... Last Year... Trading Commodities.
  4. Бартеньев О. В. Microsoft Visual FoxPro. – М.: Диалог-МИФИ, 2005. - 672 с.
  5. Он же. Современный Фортран. – М.: Диалог-МИФИ, 2005. – 560 с.
  6. Он же. Фортран для профессионалов. Математическая библиотека IMSL: Ч.1. - М.: Диалог-МИФИ, 2000. – 320 с.
  7. Он же. Графика OpenGL: программирование на Фортране. – М.: Диалог-МИФИ, 2000. – 368 с.
  8. csidata.com
  9. enc.fxeuroclub.ru
  10. finforce.ru
  11. forex4you.org
  12. forex-broker.org
  13. forex-inf.narod.ru
  14. forexomania.info
  15. forexpros.ru
  16. forexrealm.com
  17. icquadro.ru
  18. metastock.forekc.ru
  19. monopolya.blogspot.com
  20. red-online.ru
  21. robot.analitica.ru
  22. ru.wikipedia.org
  23. samsebe-uchitel.ucoz.ru
  24. spekulant.ru
  25. stockcharts.com
  26. support.instaforex.com
  27. ta.mql4.com

Список работ

Рейтинг@Mail.ru