static int EventPresent (int *es, int m, int cb) {
// Используется функцией Rules для облегчения кодирования
int i;
for(i=cb-m+l; i<=cb; i++)
if(es[i]) return TRUE;
return FALSE;
}
static void Rules (float *opn, float *hi, float *lo, float *cls,
float *vol, float *oi, float *atr, int nb, int v1, float v2,
float v3, float v4, int *ans) {
// Процедура определяет шаблоны правил, используемых в генетическом процессе эволюции модели, основанной на правилах.
// opn, hi, lo, cls — стандартные ценовые данные [l..nb]
// vol, oi — объем и открытый интерес [l..nb]
// nb — количество дней
// vl, v2, v3, v4 — селектор правил и параметры
// ans — выходные ценовые данные [l..nb]
// локальные макрофункции
#define LinearScale(х,a,b) ((х)* ((b)-(а))/1000.0+(а))
#define BiasedPosScale(х,а) (0.000001*(х)*(х)*(а))
#define Compare(a,b,dir) (((dir)>= 0)?((a)>(b)):(fa)<(b)))
// локальные переменные
static int lb1, lb2, per, cb, maxlb=100;
static float thr, fac, thr2, thr1, tmp, tiny=1.ОЕ-20;
static int IsNewHigh[MAXBAR+1], IsNewLow[MAXBAR+1];
static float Serl[MAXBAR+1] ;
// шаблоны правил
switch(v1} { // выбираем правило
case 1: // сравнение изменения цены с порогом
lb1 = (int)BiasedPosScale(v2, 50.0);
lb2 = (int)BiasedPosScale(v3, 50.0);
fac = LinearScale(v4, -2.5, 2.5) * sqrt(abs(lb1 - lb2));
for(cb-maxlb; cb<=nb; cb++) {
thr = fac * atr [cb];
ans[cb] = cls[cb-lb1] - cls[cb-lb2] > thr;
}
break;
case 2: // сравнение цены с простым скользящим средним
per = 2 + (int)BiasedPosScale(v2, 48.0);
Averages(Ser1, cls, per, nb);
for(cb=maxlb; cb<=nb; cb++)
ans[cb] = Compare(cls[cb], Ser1[cb], V4-500.0);
break;
case 3: // сравнение цены с экспоненциальным скользящим средним
per = 2 + (int)BiasedPosScale(v2, 48.0);
XAverageS(Serl, cls, per, nb) ;
for(cb-maxlb; cb<=nb; cb++)
ans[cb] = Compare(cls[cb], Ser1[cb], V4-500.0);
break;
case 4: // сравнение падения открытого интереса с пороговым значением
lb1 = 2 + (int)BiasedPosScale (v2, 48.0);
thr = LinearScale(v3, 0.01, 0.50);
for{cb=maxlb; cb<=nb; cb++) {
tmp = (oi[cb-lb1] - oi[cb-1]) / (oi [cb-lb1] + tiny);
ans [cb] = tmp > thr;
}
break;
case 5: // сравнение увеличения открытого интереса с пороговым значением
lb1 = 2 + (int) BiasedPosScale(v2, 48.0);
thr = LinearScale(v3, 0.01, 0.99);
for(cb=maxlb; cb<=nb; cb++) {
tmp = (oi[cb-1] - oi[cb-lb1]) / (oi[cb-lb1] + tiny);
ans [cb] = tmp > thr;
}
break;
case 6: // недавние новые максимумы
lb1 = 2 + (int)BiasedPosScale(v2, 48.0);
lb2 = 1 + (int)BiasedPosScale(v3, 8.0);
for(cb=lb1+3; cb<=nb; cb++)
IsNewHigh[cb] = hi [cb] > Highest(hi, lb1, cb-1);
for(cb-maxlb; cb<=nb; cb++)
ans[cb] = EventPresent(IsNewHigh, lb2, cb);
break;
case 7: // недавние новые минимумы
lb1 = 2 + (int)BiasedPosScale(v2, 48.0);
lb2 = 1 + (int)BiasedPosScale(v3, 8.0);
for(cb=lb1+3; cb<=nb; cb++)
IsNewLow[cb] = lo[cb] < Lowest(lo, lb1, cb-1) ;
for(cb=maxlb; cb<=nb; cb++)
ans[cb] = EventPresent(IsNewLow, lb2, cb);
break;
case 8: // среднее направленное движение
thrl = LinearScale(v2, 5.0, 50.0);
thr2 = thr1 + LinearScale(v3, 5.0, 20.0);
AvgDirMov{hi, lo, cls, nb, 14, Ser1);
for(cb=maxlb; cb<=nb; cb++)
ans [cb] = (Ser1[cb] > thr1 && Ser1 [cb] < thr2)
&& Compare (Ser1[cb] , Ser1[cb-l], v4-500.0);
break;
case 9: // Медленный %К
thr = LinearScale(v2, 5.0, 95.0);
fac = LinearScale(v3, 1.0, 20.0);
thr1 = thr - fac;
thr2 = thr + fac;
StochOsc(Serl, hi, lo, cls, 2, 10, nb) ;
for(cb=maxlb; cb<=nb; cb++)
ans [cb] = (Ser1[cb) > thrl && Ser1[cb] < thr2)
&& Compare(Ser1[cb], Ser1[cb-1], V4-500.0);
break ;
case 10: // направление наклона MACD
lb1 = 2 + (int)BiasedPosScale(v2, 18.0);
lb2 = lb1 + 1 + (int)BiasedPosScale(v3, 48.0);
MacdOsc(Ser1, cls, 1, lb1, lb2, nb);
for(cb=maxlb; cb<=nb; cb++)
ans[cb] = Compare(Ser1[cb], Ser1[cb-2], v4-500.0),•
break;
default:
nrerror("Undefined rule template selected");
break;
}
// первые maxlb элементов результата должны иметь значение ЛОЖЬ
memset (&ans [1] , 0, sizeof(*ans) * maxlb);
#undef BiasedPosScale
#undef LinearScale
}
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 = xl6modOl.c
// parms — набор [1..MAXPRM] параметров
// dt — набор [1..nb] дат в формате ГГММДД
// орn - набор [1..nb] цен открытия
// hi — набор [1..nb] максимальных цен
// lо — набор [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, ordertype, signal;
static int disp, k, modeltype;
static float mmstp, ptlim, stpprice, limprice, tmp;
static float exitatr[MAXBAR+1] ;
static int rule1[MAXBAR+1], rule2[MAXBAR+1], rule3[MAXBAR+1];
// копируем параметры в локальные переменные для более удобного обращения к ним
modeltype = parms[14]; // модель: 1=длинная позиция, 2=короткая
ordertype = parms[15]; // вход: 1=на открытии, 2=по лимитному приказу, 3=по стоп-приказу
maxhold =10; // максимальный период удержания позиции
ptlim = 4; // целевая прибыль в единицах волатильности
mmstp = 1; // защитная остановка в единицах волатильности
// выполнение расчетов для всей ценовой информации
AvgTrueRangeS(exitatr,hi,lo,cls,50,nb); // средний истинный диапазон для выхода
switch(modeltype) {
case 1: case 2: // для моделей открытия длинных и коротких позиций
// для каждого дня отдельно оценить три правила
Rules (opn, hi, lo, cls, vol, oi, exitatr, nb,
parms[1], parms[2], parms[3], parms[4], rule1);
Rules (opn, hi, lo, cls, vol, oi, exitatr, nb,
parms[5] , parms[6] , parms[7] , parms[8] , rule2);
Rules (opn, hi, lo, cls, vol, oi, exitatr, nb,
parms[9] , parms[10] , parms[11] , parms[12] , rule3};
break;
default: nrerror("Invalid model type");
}
// проходим через дни, чтобы моделировать настоящую торговлю
or(cb = 1; cb <= nb; cb++) {
// не открываем позиций до начала периода выборки
//... то же самое, что установка MaxBarsBack в TradeStation
if(dt[cb] < IS_DATE) { eqcls[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.О / dlrv[cb]);
if(ncontracts < 1) ncontracts = 1;
// избегаем устанавливать приказы на дни с ограниченной торговлей
if(hi[cb+l] == lo[cb+l]) continue;
// генерируем входные сигналы, цены стоп- и лимитных приказов
signal = 0;
switch(modeltype) {
case 1: // только длинные позиции
if(rule1[cb] && rule2 [cb] && rule3[cb]) signal = 1;
break;
case 2: // только короткие позиции
if(rulel[cb] && rule2 [cb] && rule3[cb]} signal = -1;
break;
}
limprice = 0.5 * (hi[cb] + lo [cb]);
stpprice = cls[cb] + 0.5 * signal * exitatr[cb];
// открываем позицию, используя определенные типы приказов
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);
} // обрабатываем следующий день
}
C++ код описывает шаблоны правил и стратегию торговой системы. Шаблоны правил определяются с помощью функции Rules. Аргументы v1, v2, v3 и v4 (четыре числа, которые содержит каждый ген) несут всю информацию, требуемую для реализации шаблонов правил. Аргумент v1 используется для выбора требуемого шаблона правила из 10 доступных; аргументы v2, v3 и v4 используются для определения требуемых параметров каждого правила (направления сравнения, периоды скользящих средних и т. д. ). Затем правило оценивается на всех днях, и оценки (1 для ИСТИНА, 0 для ЛОЖЬ) помещаются в вектор cms, возвращающий результаты функции.
Макрос BiasedPosScale (x, а) используется для создания соответствия между целыми числами от 0 до 1000 и непрерывным диапазоном от 0 до а. Макрос используется, чтобы вычислить периоды обратного обзора для определения ценовых экстремумов и периоды скользящих средних v2, v3 или v4, значения которых получены из генетического алгоритма и пронумерованы целыми числами от 0 до 1000. Соответствие между номерами от 1 до 1000 и числами из диапазона от 0 до а нелинейно — оно устроено так, чтобы можно было более детально исследовать меньшие значения параметров. Например, предположим, что период скользящего среднего изменяется от 2 до 100 дней. Необходимо с одинаковой точностью производить выбор лучшего решения между периодами 2, 3 и 4 и периодами 30, 50 и 90. Точность поиска должна быть выше для маленьких чисел. Это связано с тем, что изменение периода скользящего среднего от 2 до 5 дней сильнее повлияет на результаты торговли, чем изменение от 50 до 60.
Макрос Linear Scale (x, а, b) выполняет линейную адресацию целочисленного диапазона 0... 1000 к диапазону а.... Макрос обычно используется при вычислении порогов или отклонений. В коде шаблона правила все параметры вычислены внутри функции Rules, а не внутри ГА. Генетическии алгоритм имеет инструкцию генерировать числа в диапазоне от 0 до 1000, за исключением элементов хромосом 1, 5 и 9, которые являются первыми числами в каждом гене и используются в качестве селекторов шаблонов правил. Масштабирование проводится внутри функции Rules, так как шаблоны для различных видов правил имеют различные диапазоны изменения параметров и контрольных значений.
Процесс эволюции торговых систем начинается со случайного выбора значений хромосомы. Генетический оптимизатор выбирает два члена популяции и спаривает их (исходя из определения скрещивания, нормы мутации и размера гена). Затем полученное потомство возвращается как потенциальное решение. Когда компоненту ГА сообщают об эффективности полученного решения, он сравнивает ее с наименее пригодным членом популяции. Если пригодность потомства больше, чем у наименее пригодного члена, то ГА заменяет наименее пригодный член решения полученным потомством. Этот процесс повторяется в течение нескольких поколений и осуществляется с помощью программной оболочки (не приведенной в данной книге), которая, в свою очередь, делает повторные запросы к функции Model для моделирования торговли и оценки пригодности системы.
Код функции Model почти идентичен используемому в более ранних главах. До цикла индексации дней, в котором генерируются приказы для торговли, функция Rules вызывается три раза (один раз для каждого гена), и результаты помещаются во временные ряды rule1, rule2 и rule3. При этом также подсчитывается средний истинный диапазон за последние 50 дней, поскольку это необходимо для стандартного выхода и оценки правил. Внутри цикла оценивание правил производится для текущего дня (rule1[cb], rule2[cb], rule3[cb]), и если все оценки возвращают значение ИСТИНА, то генерируется сигнал на покупку (или продажу, если исследуются входы в
короткую позицию). Входы запрограммированы стандартным способом для каждого из трех тестируемых приказов. В эволюционном процессе используются только данные из выборки.
Выходные данные, полученные от программной оболочки, позволяют выбрать желаемое решение, которое можно использовать в торговле отдельно или в группе моделей. Решения могут быть легко сформулированы как понятные правила для оценки их «физического смысла» и использования их в качестве элементов других систем.
Далее
Вернуться к оглавлению
|