ТЕСТИРОВАНИЕ ДИНАМИЧЕСКИХ ЗАЩИТНЫХ ОСТАНОВОК
В этой группе тестов мы заменим фиксированную защитную остановку МССВ на динамическую, предположительно более эффективную. Целью является фиксация основной части потенциальной прибыли, избегая при этом превращения потенциально выгодных сделок в убыточные, как это может иметь место в случае слишком близко расположенной фиксированной остановки.
Существует много способов перемещать защитную остановку так, чтобы ее уровень следовал за рынком, фиксируя часть возникающей в течение сделки прибыли. Один из популярных методов состоит в размещении остановки на уровне минимального минимума за несколько предыдущих дней (для длинных позиций). Затем остановка может перемещаться вверх (но не вниз). Для коротких позиций защитная остановка размещается на уровне максимального максимума за несколько предыдущих дней и может двигаться только вниз, но не вверх. Этот простой метод испытывается в первом тесте.
Второй тест динамической защитной остановки похож на методику подгонки фиксированной защитной остановки в МССВ. Уровень смещается вверх (для длинных позиций) или вниз (для коротких позиций) от текущей цены на некоторое количество средних истинных диапазонов. В отличие от фиксированной защитной остановки, использованной в МССВ, в данном случае защитная остановка перемещается в зависимости от текущей рыночной цены. Смещение может происходить только в одну сторону - вверх для длинных позиций, вниз для коротких. Целью является поддержание одинакового статистического расстояния между наилучшей ценой, достигнутой во время удержания позиции, и защитной остановкой. Защитная остановка для длинных позиций рассчитывается следующим образом: (1) Из цены входа вычитается параметр mmstp, умноженный на средний истинный диапазон. Результат будет уровнем защитной остановки для следующего дня. (2) На следующей день из текущей цены вычитается другой параметр stpa, умноженный на средний истинный диапазон. (3) Если цена защитной остановки, полученная на шаге 2, выше ценового уровня текущей защитной остановки, то текущее значение защитной остановки заменяется вычисленным. (4) Стадии 2 и 3 повторяются для каждого последующего дня. При расчете защитной остановки для короткой позиции произведение среднего истинного диапазона на параметр (mmstp или stpa.} прибавляется к рыночной цене, и уровень защитной остановки опускается вниз.
В третьем тесте использован более сложный подход. Для длинных позиций, как обычно, защитная остановка задается на уровне некоторого количества средних истинных диапазонов ниже цены входа в рынок. Затем остановка смещается вверх на величину, зависящую от того, насколько текущая цена выше текущего уровня защитной остановки. Для корот
ких позиций уровень остановки задается выше цены входа и опускается в зависимости от того, насколько текущая цена ниже его. В принципе этот метод основывается на варианте смещенного экспоненциального скользящего среднего (ЭСС), за тем исключением, что величина скользящего среднего инициализируется отдельным условием при входе в сделку и может изменяться только в одном направлении (так как защитная остановка может двигаться только в направлении рыночной цены). Защитная остановка для длинных позиций рассчитывается нижеследующим образом: (I) Начальное значение в день входа в рынок задается вычитанием из цены входа произведения параметра mmstp и среднего истинного диапазона. (2) На следующий день от максимальной цены отнимается произведение параметра stpa и среднего истинного диапазона; затем вычитается текущее значение защитной остановки и результат умножается на параметр stpb. (3) Если результат шага 2 больше нуля, добавляем его к текущему значению защитной остановки, если нет, то уровень не изменяется. (4) Повторяем шаги 2 и 3 для каждого последующего дня. При вычислении защитной остановки для коротких позиций произведения среднего истинного диапазона на параметры прибавляются к рыночной цене. К уровню защитной остановки прибавляются только отрицательные поправки.
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 = x20mod02.c
// parms - набор [1..MAXPRM] параметров
// dt - набор [l..nb] дат в формате ГГММДД
// орn - набор [1..nb] цен открытия
// hi - набор [1..nb] максимальных цен
// 1о - набор [1..nb] минимальных цен
// cls - набор [1..nb] цен закрытия
// vol - набор [1..nb] значений объема
// oi - набор [1..nb] значений открытого интереса
// dlrv - набор [1..nb] средних долларовой волатильности
/ / nb - количество дней в наборе данных
/ / ts - ссылка на класс торгового симулятора
// eqcls - набор [1..nb] уровней капитала по ценам закрытия
// объявляем локальные переменные
static int rc, cb, ncontracts, maxhold, signal, ranseed;
static float stpa, stpb, mmstp, ptlim, limprice, stpprice;
static int entryposted, entrybar, model type;
static float exitatr[MAXBAR+1] , rnum, entryprice, tmp, atr;
static long iseed;
// копируем параметры в локальные переменные для удобного обращения
mmstp = parms[1]; // используется для начальный защитной остановки
stpa - parms[2]; // дополнительный параметр защитной остановки
stpb = parms[3]; // дополнительный параметр защитной остановки
ptlim = parms[6]; // целевая прибыль в единицах среднего истинного
// диапазона
modeltype = parms[7]; // тип используемой динамической защитной
//
maxhold = parms[8] ; //
ranseed = parms[9]; // //
остановки
период максимального удержания позиции используется для инициализации случайной последовательности
// выполняем вычисления по всему объему данных
AvgTrueRangeS{exitatr,hi,lo,cls,50,nb); // средний истинный диапазон
/ / для выхода
// очищаем генератор случайных чисел
// ... используем различные случайные последовательности для каждого рынка
// ... ts.model() возвращает индекс рынка (SP=1, YX-2, ...)
iseed = -{ranseed + 10 * ts.model!));
rnum = ran2(&iseed);
// проходим через дни, чтобы смоделировать реальную торговлю for(cb = 1; cb <= nb; cb++) {
// не открываем позиций до начала периода выборки
// ... то же самое, что установка MaxBarsBack в TradeStatlon
if(dt[cb] < IS_DATE) { eqclstcb] = 0.0; continue; )
// выполняем ожидающие приказы и считаем кумулятивный капитал re = ts .update (opn [cb] , hi [cb] , lo [cb] , els [cb] , cb) ; If(re !- 0) nrerrorl "Trade buffer overflow"); eqclstcb] = ts.currentequlty(EQ_CLOSETOTAL);
// считаем количество контрактов для позиции
// ... мы хотим торговать эквивалентом долларовой волатильности // ... 2 новых контрактов на S&P-500 от 12/31/98 ncontracts = RoundToInteger(5673.0 / dlrvtcb]); If (ncontracts < 1) ncontracts = 1;
// избегаем устанавливать приказы на дни с ограниченной торговлей If (hi[cb+l] == lo[cb+l]} continue;
// генерировать "стандартные" случайные сигналы входа
signal = 0;
rnum = ran2(&lseed) ;
if(rnum < 0,025) signal = -1 // случайный короткий вход
else if(rnum > 0.975) signal = 1 // случайный длинный вход
// входим в сделки по цене открытия
entryposted = 0;
if (ts.position() < = 0 && signal == 1) {
ts.buyopen{'1' , ncontracts) ;
entryposted - 1;
entryprice = opn[cb+l];
entrybar = cb + 1; ) else if(ts.positionU >= 0 && signal == -1) {
ts.sellopent'2', ncontracts);
entryposted = -1;
entryprice = opn[cb+l];
entrybar = cb + 1; )
// выходим из сделок, используя стандартный выход с улучшенными // защитными остановками atr = exitatr[cb] ; if (entryposted > 0) {
// инициализация и выходы для длинных позиций в день входа switch(modeltype) { case 1:
limprice = entryprice + ptlim * atr; stpprice = min (Lowest (lo, 2, cb),
entryprice - mmstp * atr); break; case 2: case 3:
limprice = entryprice + ptlim * atr; stpprice = entryprice - mmstp * atr; break; default: nrerror("Invalid modeltype"); }
ts.exitlonglimit('A' , limprice} ; ts.exitlongstop('B' , stpprice) ; } else if (entryposted < 0) {
// инициализация и выходы для коротких позиций в день входа switch(model type) { case 1:
limprice = entryprice - ptlim * atr; stpprice = max (Highest(hi, 2, cb),
entryprice + mmstp * atr); break; case 2: case 3:
limprice = entryprice - ptlim * atr; stpprice - entryprice + mmstp * atr; break; default: nrerror{"Invalid m o d e l t y p e " ) ; }
ts.exitshortlimit('C' , limprice) ; ts.exitshortstop('D' , stpprice); } else {
// выходы после дня входа if(ts.position() > 0) { // длинные позиции switch{modeltype) { case 1:
stpprice - max(stpprice, Lowest(lo,2,cb)); break; case 2:
stpprice = max(stpprice, cls [cb]-stpa*atr); break; case 3:
tmp = ( h i [ c b ] - stpa * atr) - stpprice; if{tmp > 0.0) stpprice += stpb * tmp; break; } ts.exitlonglimit('F' , limprice); ts.exitlongstop('G' , stpprice); if (cb-entrybar >= maxhold) ts.exitlongclose('E' } ; }
else if (ts.position(} < 0) { // короткие позиции switch(modeltype) { case 1:
stpprice = min(stpprice, Highest(hi,2,cb}) ; break; case 2:
stpprice = min (stpprice, cls [cb]+stpa*atr); break;
сазеЗ:
tmp = {lo[cb] + stpa * atr) - stpprice; if(tmp < 0.0) stpprice += stpb * tmp; break; }
ts.exitshortlimit('I' , limprice) ; ts.exitshortstop('J' , stpprice);
if(cb-entrybar >= maxhold) ts.exitshortclose('H') ; } } } // обрабатываем следующий день }
Назад