<< Пред. стр.

стр. 29
(общее количество: 46)

ОГЛАВЛЕНИЕ

След. стр. >>

// dlrv - набор [l..nb] средних долларовой волатильности
/ / nb — количество точек в наборе данных
// ts - ссылка на класс торгового стимулятора
Входы НА ОСНОВЕ циклов 241
ГЛАВА 10



// eqcls - набор [l..nb] уровней капитала по ценам закрытия

// объявляем локальные переменные
static int rc, cb, ncontracts, maxhold, ordertype, signal;
static int disp, k, modeltype, fcount, goodcycle, domperndx;
static float mmstp, ptlim, stpprice, limprice, tmp;
static float width, oldwidth, lper, sper, per, ratio;
static float exitatr[MAXBAR+1], **inphase, **inquad, **power;
static float peakpower, phase, peaknoise, domperiod;
static float buyphase, sellphase, phaseb, oldphase, oldphaseb;
static WAVFILT filter[20];

// копируем параметры в локальные переменные для удобного обращения
width = parms[l]; // ширина полосы пропускания фильтра (0.05 .. 0.20}
disp= parms[2]; // временное смещение в градусах
modeltype = parms[8]; // модель: 1=торговать развороты циклов
ordertype = parms[9]; // вход: 1=на открытии, 2=по лимитному приказу,
// 3=по стоп-приказу
maxhold = 10; // период максимального удержания позиции
ptlim = 4; // целевая прибыль в единицах волатильности
mmstp = 1; // защитная остановка в единицах волатильности

// Создаем искусственный набор цен закрытия в
// форме синусоиды. Это «плазмода» для проведения тестов.
// Модель должна хорошо торговать на данном наборе цен.
// #define USESIMEWAVE
#ifdef USESINEWAVE
per = 3.0;
ratio = exp (log (30.0/3.0) / (nb - 1));
sper=0.0;
for (cb = 1; cb <= nb; cb++) (
sper += 2.0 * PI * (1.0 / per);
cls[cb] = sin(sper);
per *= ratio;
}
#endif

// инициализируем группу равноотстоящих волновых фильтров
// заново инициализируем, если параметр ширины полосы изменился
if(width != oldwidth) {
lper ==30.0; // фильтр длинных периодов
sper = 3.О ; // фильтр коротких периодов
fcount = 2 0 ; // число фильтров в группе
ratio = exp (log (lper / sper) / (fcount - 1) ) ;
per = sper;
for(k = 1; k <= fcount; k++) (
filter[k-1].build_kernel(per, width);
per *= ratio;
}
oldwidth = width;
}
// рассчитываем выходы фильтров и откорректированный спектр мощности
// если матрицы (таблицы) пустые, то присваиваем им значения
if(inphase == NULL) inphase = matrix(l,fcount,1,MAXBAR);
if(inquad == NULL) inquad = matrix(1,fcount,l.MAXBAR);
if(power == NULL) power = matrix(1,fcount,1.MAXBAR);
for(k = 1 ; k <= fcount; k++) {
filter[k-1] .apply (cls, inphase[k] , inquad[k], nb);
for(cb = 1; cb <= nb; cb++)
power [k] [cb] = (inphase [k] [cb] * inphase [k] [cb] +
inquad [k] [cb] * inquad [k] [cb] )
/ filter[k-1].period();
ИССЛЕДОВАНИЕ входов в РЫНОК
242 ЧАСТЬ II



}

// сохраняем спектральный анализ выборки в файл
// эта процедура проводится для отладки
// #define WRITESAMPLE
#ifdef WRITESAMPLE
FILE *fil = fopen("test.dat", "wt");
for(cb = nb-1200; cb < nb; cb++) {
domperndx = 0 ;
peakpower = -1.0;
for(k = 1; k <= fcount; k++) (
if(power[k][cb] > peakpower) {
peakpower = power[k] [cb] ;
domperndx = k;
}
)
phase = (180.0 / PI) * atan2 (inquad [domperndx] [cb] ,
inphase[domperndx] [cb]);
for(k = 1; k <= fcount; k++) (
if (power [k] [cb] > 0.90 * peakpower)
fprintf(fil, " **");
else if (power[k][cb] > 0.75 * peakpower)
fprintf(fil, " ++");
else if (power[k][cb] > 0.5 * peakpower)
fprintf(fil, " + ");
else
fprintf(fil, " ");
)
fprintf(fil, "%4d %7d %7d %7d %8.1f\n",
(int)filter[domperndx-1].period(),
(int)(inphase[domperndx] [cb]),
(int)(inquad[domperndx] [cb]),
(int)phase, cls [cb]);
}
fclose(fil);
exit(0);
#endif

// используется для отладки сигналов
// #define SIGNALDEBUG
#ifdef SIGNALDEBUG
FILE *fil = fopen("testsig.dat" , "wt");
#endif

// выполняем вычисления для всех данных
AvgTrueRangeS(exitatr,hi,lo,cls,50,nb) ; // средний истинный диапазон для
// выхода
switch (modeltype) [
case 1:
// Ничего не делайте! Место для будущего кода,
break ;
default: nrerror ("Invalid model type");
)

// проходим через дни, чтобы смоделировать реальную торговлю
for(cb = 1; cb <= nb; cb++) {

// не открываем позиций до начала периода выборки
// ... то же самое, что установка MaxBarsBack в TradeStation
if(dt[cb] < IS_DATE) 1 egcls[cb] = 0.0; continue; )
Входы НА ОСНОВЕ циклов 243
ГЛАВА 10




// выполняем ожидающие приказы и сохраняем значение капитала
rc = ts.update (opn [cb] , hi [cb] , lo [cb] , cls [cb] , cb) ;
if(rc != 0) nrerror{"Trade buffer overflow");
eqcls[cb] = ts.currentequity(EQ_CLOSETOTAL);

/ / н е торгуем в последние 30 дней выборки
// оставляем место в массивах для будущих данных
if(cb > nb-30) continue;

// считаем количество контрактов для позиции
// ... мы хотим торговать эквивалентом долларовой волатильности
// ... 2 новых контрактов на S&P-500 от 12/31/98
ncontracts = RoundToInteger(5673 . О / dlrv[cb] ) ;
if (ncontracts < 1) ncontracts = 1;

// избегаем устанавливать приказы на дни с ограниченной торговлей
if(hi[cb+1] == lo[cb+1]} continue;

// генерировать входные сигналы, цены стоп- и лимитных приказов
signal = 0;
switch (modeltype) {
case 1:
// ищем хороший цикл для торговли
domperndx = 0;
peakpower = -1.0;
for(k = 1; k <= fcount; k++) {
if(power[k][cb] > peakpower) {
peakpower = power[k][cb];
domperndx = k;
}
}
goodcycle = FALSE;
if(domperndx > 3 && domperndx < fcount-1) {
peaknoise = 0.0;
for(k = 1; k <= fcount; k++) {
if (abs(k - domperndx) > 2) {
if (power[k] [cb] > peaknoise)
peaknoise - power[k] [cb] ;
}
}
if(peakpower > 1.5 * peaknoise) goodcycle = TRUE;
}
// генерируем торговые сигналы
if (goodcycle) {
domperiod = filter [domperndx-1] .period() ;
phase = (180.0 / PI) *
atan2(inquad[domperndx] [cb],
inphase[domperndx] [cb]);
oldphase = (180.0 / PI) *
atan2(inquad[domperndx] [cb-1],
inphase[domperndx] [cb-1] );
phaseb - (phase<0.0) ? (360.0+phase) : phase;
oldphaseb = (oldphase<0.0)
? (360.0+oldphase) : oldphase;
sellphase = 0.0 - (disp + 180.0 / domperiod);
buyphase = 180.0 + sellphase;
if (phaseb > buyphase && oldphaseb <- buyphase)
signal = 1; // сигнал на покупку
if (phase > sellphase && oldphase <= sellphase)
signal = -1; // сигнал на продажу
}
break;
ИССЛЕДОВАНИЕ входов в РЫНОК
244 ЧАСТЬ II



}
limprice = 0.5 * (hi [cb] + lo [cb] ) ;
stpprice = cls[cb] + 0.5 * signal * exitatr[cb];

// печатаем отладочную информацию
#ifdef SIGNALDEBUG
fprintf(fil, "%8d %8.1f %8d %8d %8d %8d\n",
cb, cls[cb], signal,
(int)filter[domperndx-1].period(),
(int)peakpower, {int)peaknoise);
#endif

// входим в сделку, используя определенный тип приказа
if(ts.position() <= 0 && signal == 1) (
switch(ordertype) { // выбираем нужный вид приказа
case 1: ts.buyopen('1', ncontracts); break;
case 2: ts.buylimit ('2', limprice, ncontracts); break;
case 3: ts.buystop('3', stpprice, ncontracts); break;
default: nrerror("Invalid buy order selected");
}
)
else if (ts.position() >= 0 && signal == -1) {
switch(ordertype} { // выбираем нужный вид приказа
case 1: ts.sellopen('4', ncontracts); break;
case 2: ts.selllimit('5', limprice, ncontracts); break;
case 3: ts.sellstop('6', stpprice, ncontracts); break;
default: nrerror("Invalid sell order selected");
}
}

// симулятор использует стандартную стратегию выхода
tmp = exitatr[cb];
ts.stdexitcls('X', ptlim*tmp, mmstp*tmp, maxhold);

} // обрабатываем следующий день

// закрываем, если в режиме отладки
#ifdef SIGNALDEBUG
fclose(fil);
exit(0);
#endif
}


Вышеприведенный код описывает тестируемую модель. Первый важ-
ный блок кода, принципиальный для циклической модели, инициализи-
рует индивидуальные фильтры, составляющие группу фильтров. Этот код
работает только при первом проходе или при изменении параметра, вли-
яющего на инициализацию группы фильтров, например параметра width.
Если важные параметры остаются без изменений, не имеет смысла пере-
запускать фильтры при каждом вызове функции Model.
Следующий блок применяет к входящему сигналу каждый из фильт-
ров в составе группы. В этом блоке отведены два массива для хранения
выходного сигнала группы фильтров. Первый массив хранит выход с со-
впадающей фазой inphase, а второй — ортогональный выход inquad. Вход-
ной сигнал представляет исходные цены закрытия. Поскольку фильтры
математически оптимальны и рассчитаны на удаление трендов, предва-
Входы НА ОСНОВЕ циклов 245
ГЛАВА 10




рительная обработка данных становится излишней в отличие от менее
продвинутых методик анализа. Каждая строка в массиве представляет
собой выход отдельного фильтра с данной частотой или периодом, каж-
дая колонка представляет собой торговый день. Центральные частоты или
периоды фильтров расположены на равных расстояниях на логарифми-
ческой шкале, т.е. соотношение между центральной частотой данного и
следующего фильтра постоянно. Селективность полосы пропускания
(width) — единственный настраиваемый параметр в расчете группы филь-
тров, и это значение может подбираться путем оптимизации.
Затем запускается обычный цикл перебора точек данных, и генери-
руются собственно торговые сигналы. Сначала проверяется наличие чи-
стого, пригодного для торговли цикла. Для этого определяется мощность
при периоде, имеющем максимальный резонанс с текущей активностью
рынка (peakpower). Также оценивается период, на котором наблюдается
максимальная мощность. Если период не попадает на одно из крайних зна-
чений рассматриваемого диапазона (диапазон составляет от 3 до 30 дней),
то потенциально цикл может быть пригоден для торговли. Затем проверя-
ется максимальная мощность на расстоянии не менее 2 полос пропуска-
ния фильтра от периода пика (peaknoise). Если отношение peakpower/
peaknoise составляет 1,5 или более, то выполняется второе условие при-
годности цикла. На основе пары выходов определяется фазовый угол цик-
ла. Затем код проверяет фазовый угол на соответствие максимуму или
минимуму цены. Кроме того, в эту оценку вводится небольшое значение
смещения (disp). Оно работает подобно смещению в предыдущих моде-
лях, хотя здесь относится к фазовому углу, а не к количеству точек дан-
ных. Между фазовым углом и количеством точек данных существует пря-
мая зависимость: период цикла, умноженный на фазовый угол в градусах
и разделенный затем на 360, дает количество точек данных, соответству-
ющее фазовому углу. Если фаза после смещения такова, что через неко-
торое количество градусов до или после текущего дня можно ожидать
минимума, отдается приказ на покупку. Если фаза такова, что можно ожи-
дать максимума, отдается приказ на продажу. Затем, как обычно, рассчи-
тываются цены для лимитного и стоп-приказов. При поступлении сигна-
лов система исполняет требуемые приказы.
Другие блоки вышеприведенного кода здесь не обсуждаются, посколь-
ку связаны с отладкой и тестированием программы. Их предназначение
описано в комментариях к коду.


РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ

Тестировалась только одна модель с входами по цене открытия (тест 1),
по лимитному приказу (тест 2) и стоп-приказу (тест 3). Правила были про-
стыми: покупать на предсказанных минимумах и продавать на предска-
ИССЛЕДОВАНИЕ входов в РЫНОК
246 ЧАСТЬ II



занных максимумах. Выходы производились при поступлении сигнала к
открытию противоположной позиции или при срабатывании стандарт-
ного выхода. Эта простая торговая система сначала испытывалась на ис-
кусственных ценовых данных, созданных путем добавления шума к иде-
альной синусоиде с периодом от 4 до 20 дней. На этих данных были полу-
чены сигналы покупки и продажи, идеально совпадающие с максимума-
ми и минимумами. Такое совпадение показывает, что при наличии реаль-
ных циклов система способна обнаруживать их с высокой точностью и
использовать в торговле.
В табл. 10-1 приведены лучшие показатели, полученные для данных,
находящихся в пределах выборки, а также эффективность портфеля на
данных в пределах и вне пределов выборки. В таблице: ВЫБ. — вид вы-
борки данных (В — в пределах, ВНЕ — вне пределов выборки); ДОХ% —
доходность в процентах годовых; Р/ПРИБ — соотношение риска/прибы-
ли в годовом исчислении; ВЕР — ассоциированная вероятность статисти-
ческой достоверности; СДЕЛ — число сделок на всех рынках в составе
портфеля; ПРИБ% — процент прибыльных сделок; $СДЕЛ — средняя при-
быль/убыток со сделки; ДНИ — средняя длительность сделки в днях;
ПРИБДЛ — общая прибыль от длинных позиций в тысячах долларов;
ПРИБКР — общая прибыль от коротких позиций в тысячах долларов. Оп-
тимизировались два параметра. Первый (Р1) определяет ширину полосы
пропускания для каждого фильтра в составе группы. Второй (Р2) отобра-
жает фазовое смещение в градусах. Во всех случаях параметры прогоня-
лись в пределах выборки для ширины полосы пропускания от 0,05 до 0,2 с
шагом 0,05 и для фазового сдвига от —20 до +20° с шагом 10°. Показаны
только оптимальные решения.
Интересно отметить, что в общем циклическая модель имела доста-
точно низкую эффективность. По показателю прибыли со сделки эта мо-
дель превосходила многие рассмотренные ранее, но сильно уступала луч-
шим из них. В пределах выборки убыток со сделки оставил $ 1329 при вхо-
де по цене открытия, $1037 при входе по лимитному приказу и $1245 при
входе по стоп-приказу. Вход по лимитному приказу обеспечил максималь-
ный процент прибыльных сделок и минимальный средний убыток. Длин-
ные позиции были слабо прибыльными при входе по цене открытия, бо-
лее прибыльны при входе по лимитному приказу и убыточны при входе
по стоп-приказу. Вне пределов выборки входы по лимитному приказу и
цене открытия работали хуже, чем в пределах выборки. Средний убыток
в сделке составил $3741 при входе по цене открытия и $3551 при входе по
лимитному приказу. Доля прибыльных сделок также снизилась до 34%.
Эффективность циклической модели вне пределов выборки была одной
из худших среди всех моделей, что нельзя отнести на счет избыточной
оптимизации: при других параметрах убытки были еще больше. При ис-
пользовании входа по стоп-приказу эффективность вне пределов выбор-
ки не ухудшалась, средний убыток ($944) в сделке был близок к убытку в
Входы НА ОСНОВЕ циклов
ГЛАВА 1О 247



Таблица 10—1. Эффективность портфеля на данных в пределах и вне
пределов выборки с лучшими параметрами, полученными в
пределах выборки


дох%
Р1 Р2 ВЕР ПРИБ% $СДЕЛ ДНИ ПРИБДЛ ПРИБКР
Р/ПРИБ СДЕЛ
Выб. РЗ

Тест 1. Базовая циклическая модель, вход по цене открытия
В 0,2 20 0 -10,2 -0,66 1312 40 -1329 6 255
0,980 -2000
1,000 547 34 -3741
ВНЕ 0,2 20 0 -23,2 -1,70 6 -693 -1352

Тест 2. Базовая циклическая модель, вход по лимитному приказу
В 0,2 20 0 -9,5 -0,46 1103 41 -1037 7 621 -1764
0,926
ВНЕ 0,2 20 0 -22,8 -1,47 475 34 -3551 7 -652 -1034
0,999

Тест 3. Базовая циклическая модель, вход по стоп-приказу
В 0,1 20 0 -8,3 -0,53 0,951 957 40 -1245 7 -195 -996
ВНЕ 20 0 -15,0 -0,35 403 41 -944 7 -220 -160
0,762
0,1




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

<< Пред. стр.

стр. 29
(общее количество: 46)

ОГЛАВЛЕНИЕ

След. стр. >>