Все приведенные ниже тесты были осуществлены с помощью осцилляторных моделей на портфеле разнообразных ценных бумаг. Можно ли получать прибыльные сделки с осцилляторными моделями? Как они работают во времени — ухудшается или улучшается их функционирование за последние годы? Целью нашего тестирования был ответ на эти
вопросы.
Во всех тестах использовался стандартный выход, описанный в предыдущих главах. Правила входов будут рассмотрены параллельно с кодом модели и отдельными тестами. Сделки закрывались при поступлении приказа на вход в обратном направлении или при исполнении стандартного выхода. Платформа тестирования тоже была стандартной.
За последние годы мы закодировали на языке C++ ряд осцилляторов, описанных в Technical Analysis of Stocks and Commodities и в ряде других источников. В этой главе мы сравниваем работу версий C++ осцилляторов MACD, RSI и стохастического с их версиями в TradeStation.
В большинстве случаев результаты идеально совпадали, но в одном случае, а именно для Медленного %К, результат разительно отличался. Разбор кода показал, что TradeStation рассчитывает Медленный %К как экспоненциальное скользящее среднее от Быстрого %К. В нашем же коде отдельно рассчитываются простые скользящие средние с периодом 3 дня для числителя и знаменателя формулы Быстрого %К. Согласно уравнениям, приведенным Мейбахом (Meibahr, 1992) и другими источниками, правильной является наша версия на C++. Если читатели захотят повторить наши расчеты в TradeStation и обнаружат расхождения, мы настоятельно рекомендуем проверить функции индикаторов TradeStation.
Кроме того, при попытке закодировать правильную версию Медленного %К для TradeStation на EasyLanguage мы неожиданно обнаружили, что TradeStation без предупреждения может выдать неверные результаты, если одна пользовательская функция вызывает другую. Когда мы изменили код так, чтобы рассчитывалась промежуточная переменная (чтобы избежать совместных вызовов), были получены правильные результаты. В этих тестах использовалась версия TradeStation 4.02 от 29 июля 1996 г.
Нижеследующий код включает большинство использованных в тестах моделей входов на основе осцилляторов. Реальный расчет осцилляторов достигается путем вызова внешних функций.
static void Model (float *parms, float *dt, float *opn, float *hi, float *lo, float *cls, float *vol, float *oi, float *dlrv, int nb, TRDSIM &ts, float *eqcls) (
// Выполнение тестирования моделей на осцилляторах
// File = xllmodOl.c
// parms - набор [1.. MAXPRM] параметров
// dt - набор [l..nb] дат в формате ГГММДД
// орn - набор [l..nb] цен открытия
// hi — набор [l..nb] максимальных цен
// 1о - набор [1..пЬ] минимальных цен
// cls — набор [l..nb] цен закрытия
// vol - набор [l..nb] значений обьема
// oi — набор [l..nb] значений открытого интереса
// dlrv — набор [l..nb] средних долларовой волатильности
// nb — количество дней в наборе данных
// ts — ссылка на класс торгового симулятора
// eqcls — набор [l..nb] уровней капитала при закрытых позициях
// объявляем локальные переменные
static int rc, cb, ncontracts, maxhold, len1, len2, len3;
static int modeltype, ordertype, osctype, signal, i, j, k;
static float mmstp, ptlim, stpprice, limprice, tmp;
static float exitatr[MAXBAR+1];
static float sigline[MAXBAR+1], oscline[MAXBAR+1];
static float upperband[MAXBAR+1] , lowerband [MAXBAR+1] ;
// копируем параметры в локальные переменные для удобного обращения
lenl = parms[l]; // более короткий для первого параметра длины
1еn2 = parms[2] ; // более длинный для второго параметра длины
1епЗ = parms[3]; // длина теста дивергенции
modeltype = parms[7]; // тип осцилляторной модели входа
osctype - parms[8]; // тип осциллятора
ordertype = parms[9]; // тип входного приказа
maxhold =10; // период максимального удержания позиции
ptlim =4; // целевая прибыль в единицах волатильности
mmstp =1; // защитная остановка в единицах волатильности
// пропускаем неправильные комбинации параметров,
if ( (osctype==4 && len1>=len2) ) (
set_vector(eqcls, 1, nb, 0.0);
return;
}
// выполняем вычисления для всех данных, используя процедуры быстрой обработки
// массивов
AvgTrueRangeS{exitatr,hi,lo,cls,50,nb); // средний истинный диапазон для выхода
switch(osctype} { // выбираем осциллятор
case 1: // классические быстрые стохастики
StochOsc(oscline,hi,lo,cls,1,len1,nb); //Быстрый %К
MovAvg(sigline, oscline, 1, 3, nb); //Быстрый %D
set_vector(upperband, 1, nb, 80.0); //верхняя граница
set_vector(lowerband, 1, nb, 20.0); //пробой нижней границы
break;
case 2: // классические медленные стохастики
StochOsc(oscline,hi,lo,cls,2,lenl,nb); //Медленный %К
MovAvg(sigline, oscline, 1, 3, nb); //Медленный %D
set_vector(upperband, 1, nb, 80.0); //верхняя граница
set_vector(lowerband, 1, nb, 20.0); //пробой нижней границы
break;
case 3: // классический RSI
RsiOsc(oscline, cls, 1, lenl, nb); //RSI
MovAvgtsigline, oscline, 1, 3, nb) ; //3- дневное ПСС
set_vector(upperband, 1, nb, 70.0); //верхняя граница
set_vector(lowerband, 1, nb, 30.0); //пробой нижней границы
break;
case 4 : // классический MACD
MacdOsc(oscline,cls,1,lenl,len2,nb); //классический MACD
MovAvg(sigline, oscline, 2, 9, nb) ; //9- дневное ЭСС
for{i=l; i<=nb; i++}
lowerband[i]=1.5*fabs(oscline[i] ) ; //пороги
MovAvg(upperband,lowerband,1,120,nb); //как долгосрочная
for{i=l; i<=nb; i++} //отклонение от среднего
lowerband[i]= - upperband[i]; //полосы
break;
default: nrerror("Invalid moving average selected");
};
// проходим через штрихи (дни), чтобы смоделировать реальную торговлю
for{cb = 1; cb <= nb; cb++} {
// не открываем позиций до начала периода выборки
// ... то же самое, что и установка MaxBarsBack в TradeStation
if(dt[cb] < IS_DATE) { egcls[cb] = 0.0; continue; )
// выполняем ожидающие приказы и считаем накопленный капитал
rс = ts.update{opn[cb], hi[cb], lo[cb], cls[cb], cb);
if(rc = 0) nrerror("Trade buffer overflow"};
eqcls[cb] = ts.currentequity(EQ_CLOSETOTAL);
// считаем количество контрактов для позиции
//... мы хотим торговать эквивалентом долларовой волатильности
//... 2 новых контрактов S&P- 500 от 12/31/98
ncontracts = RoundToInteger(5673.0 / dlrvfcbj);
if(ncontracts < 1) ncontracts = 1;
// избегаем устанавливать приказы на дни с ограниченной торговлей
if(hi[cb+l] == lo[cb+l]) continue;
// генерируем входные сигналы, цены стоп- и лимитных приказов,
// используя определенную модель входа на осцилляторах
#define CrossesAbove (a,b,c) (а[с]>=b[с] && а[с- 1]<b[с- 1])
#define CrossesBelow(a,b, с) (a[c]<b[c] && а [с- 1] >=b [с- 1] )
#define TurnsUp(a,c) (а[с]>=а[с- 1] && а[с- 1]<а[с- 2])
#define TurnsDn(a,c) (a[c]<a[c- l] && а [с- 1] >=а [с- 2] )
signal=0;
switch (modeltype) {
case 1: // модель перекупленности- перепроданности
if(CrossesAbove(oscline, lowerband, cb)}
signal = 1;
else if(CrossesBelow(oscline, upperband, cb))
signal = - 1;
limprice = 0.5 * (hi [cb] + lo [cb] ) ;
stpprice = cls[cb] +0.5 * signal * exitatr[cb];
break;
case 2: // модель сигнальной линии
if(CrossesAbove(oscline, sigline, cb))
signal = 1;
else if(CrossesBelow(oscline, sigline, cb))
signal = - 1;
limprice = 0.5 * (hi [cb] + lo[cb]) ;
stpprice = cls[cb] + 0.5 * signal * exitatr[cb];
break;
case 3: // модель дивергенции
i = LowestBar(cls, len3, cb) ;
j = LowestBar(oscline, len3, cb);
if(i < cb && i > cb- 6 && j > cb- len3+l && i- j > 4
&& TurnsUp(oscline, cb)) signal = 1;
else {
i = HighestBar(cls, len3, cb} ;
j = HighestBar(oscline, len3, cb);
if(i < cb && i > cb- 6 && j > cb- len3+l && i- j > 4
&& TurnsDn(oscline, cb)) signal = - 1;
)
limprice = 0.5 * (hi[cb] + lo[cb]);
stpprice = cls[cb] + 0.5 * signal * exitatr[cb];
break;
default: nrerror("Invalid model selected");
)
#undef CrossesAbove
#undef CrossesBelow
#undef TurnsUp
#undef TurnsDn
// входим в сделку, используя определенный тип приказа
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.position1) >= 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 ('Х', ptlim*tmp, nmstp*tmp, maxhold) ;
} // обрабатываем следующий день
]