int LunarEventDates (int n) {
// подсчитывает дату лунной фазы, начиная с января 1900.
// n - ввод: номер фазы луны
// 0,4,8... новолуния
// 1,5,9... луна в первом квартале
// 2,6,10... полнолуние
// 3,7,11... луна во втором квартале
//возвращает - вывод: дата события по юлианскому календарю
static long ndate;
static float timzon = -5.0 / 24.0; // восточное стандартное время
static float fгас;
flmoon (n >> 2, n & 3, &ndate, &frac);
frac = 24.0 * {frac + timzon);
if(fгас < 0.0) { // корректировка времени
ndate—;
frac += 24.0;
}
if(frac > 12.0) {
ndate++;
frac -= 12.0;
}
else frac += 12.0;
return ndate; // юлианская дата события
}
int LunarEquivDate (int date, int n) {
// рассчитываем дату предыдущего n-го (n < 0) или будущего (n > 0) случая фазы луны, равной сегодняшней фазе
// date - ввод: текущая дата в формате ГГММДД
// n - ввод: лунные циклы назад{-) или вперед (+)
// return - вывод: дату предыдущего или будущего цикла в формате ГГММДД
static long nstar, ndatel, ndate2, curdate, ntarg, nans;
static int mm, dd, yyyy;
curdate = julday((date/100)%100, date%10O, 1900+date/lOOOO);
while(curdate >= ndate2) {
ndatel = LunarEventDates(++nstar);
ndate2 = LunarEventDates(nstar + 1) ;
}
while(curdate < ndatel) {
ndatel - LunarEventDates{—nstar);
ndate2 = LunarEventDates(nstar + 1);
}
if(curdate < ndatel || curdate >= ndate2
|| abs (ndate2 - ndatel - 7) > 2)
nrerror("LunarEquivDate: calculation error");
nans = LunarEventDates(nstar +4 * n);
nans += (curdate - ndatel);
caldat (nans, &mm, &dd, &yyyy) ;
return 10000*(yyyy-1900) + 100*mm + dd;
}
void LunarAvg (float *a, float *v, float *dt, int mode, int m, int n) {
// Подсчитываем лунное (в зависимости от даты и фазы) скользящее среднее для каждого дня, основанное на предыдущих днях и (в некоторых случаях) на последующих днях с эквивалентной лунной фазой.
// Работаем на всех имеющихся данных.
//а - вывод: значения [1..n] лунного среднего
//v - ввод: исходный ценовой ряд данных [1..n]
// dt - ввод: соответствующие [1..n] данные
// mode - ввод: метод анализа:
// 1 = «складной нож» IS, все прошлые циклы 008
// 2 - фиксированный период в лунных циклах
//m - ввод: дата (для режима - 1) или период анализа (для режима = 2)
// n - ввод: количество дней во всех рядах
static int i, j, cnt;
static unsigned long k;
static float sum, sdate, tiny=l.OE-20;
if(mode == 1) { // режим «складного ножа»
for(i = 1; i <= n; i++) { // для каждого текущего дня
sum = 0.0; cnt = 0;
for(j = 2; j < 1000; j++) { // двигаемся назад
sdate - LunarEquivDate (dt[i] , -j) ; //к исходной дате
if(sdate < dt[3]) break; // переход к началу
hunt(dt, n, sdate, &k) ; // находим индекс
if (sdate > dt[k]) k++;
cnt++; sum += v[k] ; // накапливаем среднюю
}
for(j = 2; j < 1000; j ++) { // двигаемся вперед
sdate = LunarEquivDate (dt[i], j); //к исходной дате
if(sdate > m) break; // избегаем данных oos
hunt(dt, n, sdate, &k); // находим индекс
if(sdate > dt[k]) k++;
cnt++; sum +- v[k]; // накапливаем среднюю
}
a[i] = sum / (cnt + tiny); // заканчиваем среднюю
} // следующий день
}
else if(mode == 2) ( // режим фиксированного периода анализа
for(i = 1; i <= n; i ++) { // для каждого текущего дня
sum =0.0; cnt = 0;
for(j =2;j<1000;j++) { // двигаемся назад
if(cnt >= m) break; // выполняем достаточные условия
sdate = LunarEquivDate(dt[i] , -j); // исходная дата
if (sdate < dt[3]) break; // идем к началу
hunt(dt, n, sdate, &k); // находим индекс
if{sdate > dt[k]} k++;
cnt++; sum +- v[k]; // накапливаем среднюю
}
for(j = 2; -j < 1000; j++) { // двигаемся вперед
if (cnt >= m) break; // выполняем достаточные условия
sdate = LunarEquivDate(dt[i] , j ) // исходная дата
hunt(dt, n, sdate, &k) ; // находим индекс
if (sdate > dt[k]} k++;
cnt++; sum += v[k]; // накапливаем среднюю
}
a[i] = sum / (cnt + tiny) // заканчиваем среднюю
} // следующий день
}
}
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 = xl3modOl.c
// parms — набор [1..MAXPRM] параметров
// dt — набор [l..nb] дат в формате ГГММДД
// орn — набор [1..nb] цен открытия
// hi - набор [l..nb] максимальных цен
// 1о — набор [1..nb] минимальных цен
// cls — набор [l..nb] цен закрытия
// vol - набор [l..nb] значений объема
// oi — набор [1..nb] значений открытого интереса
// dlrv — набор [1..nb] средних долларовых волатильностей
// nb — количество дней в наборе данных
// ts — ссылка на класс торгового симулятора
// eqcls — набор [1..nb] уровней капитала по ценам закрытия
// объявляем локальные переменные
static int rc, cb, ncontracts, maxhold, ordertype, signal;
static int avglen, disp, k, modeltype, matype, mktindx;
static float mmstp, ptlim, stpprice, limprice, tmp, thresh;
static float exitatr[MAXBAR+1] , savg[MAXBAR+1] ;
static float mal[MAXBAR+1], ma2[MAXBAR+1], stoch[MAXBAR+1];
static float *exitatrtab[MAXMKT+1], *savgtab[MAXMKT+1] ;
//копируем параметры в локальные переменные
avglen = parms[1]; // период скользящей средней
disp = parms[2]; // множитель смещения
thresh = parms[3]; // порог для импульсных моделей
matype = parms[7]; // тип средней:
// 1=простое скользящее среднее
// 2=экспоненциальное
// 3=треугольное с переднем взвешиванием
// 4=треугольное
// 5=простое центрованное
// 6=экспоненциальное центрованное
// 7=треугольное центрованное
modeltype = parms[8]; // тип модели:
// 1=импульс
// 2=пересечение
// 3-пересечение с подтверждением
// 4=пересечение с подтверждением и инверсией
ordertype = parms[9]; // вход: 1=на открытии, 2=по лимитному приказу, 3=по стоп-приказу
maxhold = 10; // период максимального удержания позиции
ptlim = 4; // целевая прибыль в единицах волатильности
mmstp = 1; // защитная остановка в единицах волатильности
// Выполняем вычисления по всему объему данных, которые не подвержены воздействию каких-либо параметров. Выполняется один раз для каждого рынка, результаты сохраняются в таблицах для повторного использования. Таким образом, значительно снижается время выполнения программы.
mktindx = ts.modelf) ; // индекс рынка
if (exitatrtab[mktindx] == NOLL) { // размещен?
exitatrtab[mktindx] = vector(1, nb); // таблица exitatr
savgtab[mktindx] = vector{1, nb); // таблица savg
AvgTrueRangeS(exitatrtab[mktindx],
hi, lo, cls, 50, nb); //50-дневный средний истинный диапазон
float *pchg = vector(1, nb); // вспомогательный вектор
pchg[l] = 0.0;
for(cb =2; cb < = nb; cb++) {
tmp = cls [cb] - els [cb-1]; // изменение цены
tmp /= exitatrtab[mktindx] [cb]; // нормирование
pchg [cb] = clip(tmp, -2.0, 2.0); // обрезание
}
LunarAvg(savgtab[mktindx],
pchg, dt, 2, 60, nb); // лунная сезонность
free_vector(pchg, 1, nb);
printf {"Mkt: %d\n", mktindx}; // показывать прогресс
}
// выполняем вычисления для всех данных
memcpy(exitatr, exitatrtab[mktindx] , sizeof (float)*nb);
memcpy(savg, savgtab[mktindx], sizeof(float)*nb);
switch(modeltype) {
case 1: // данные для импульсной модели
MovAvg{savg,savg,matype,avglen,nb) ; // сглаживающее среднее
for(cb = 1; cb <= nb; cb++)
ma2 [cb] = fabs(savg[cb]} ;
MovAvg (mal, ma2, 1, 100, nb) ; // среднее отклонение
break;
case 2: case 3: case 4: // данные для моделей пересечения
for(cb = 2; cb <= nb; cb++)
savg[cb] += savg [cb-1]; // интеграция
MovAvg(mal,savg,matype,avglen,nb); // сглаж. средн.
MovAvg(ma2,mal,matype,avglen,nb); // пересеч. средн.
if (modeltype == 3 || modeltype == 4) // стохастический осц.
StochOsc(stoch,hi,lo,cls,1,9,nb); // 9-дневный Быстрый %К
break;
default: nrerror("TRAPSMOD: invalid modeltype");
}
// проходим через дни, чтобы смоделировать реальную торговлю
for(cb = I; cb <= nb; cb++) {
// не открываем позиций до периода выборки
// ... то же самое, что установка MaxBarsBack в TradeStation
if(dt[cb] < IS_DATE) { egcls[cb] = 0.0; continue; }
// выполняем ожидающие приказы и считаем кумулятивный капитал
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: // основная модель входа на основе импульса с порогом
k = cb + disp;
tmp = thresh * mal[k] ;
if(savg[k] > tmp && savg[k-l] <= tmp)
signal = 1;
else if(savg[k] < -tmp && savg[k-l] >= -tmp)
signal = -1;
break;
case 2 : // основная модель пересечения
k = cb + disp;
if (CrossesAbove (mal, ma2 , k)) signal = 1 ;
else if (CrossesBelow{mal, ma2, k)) signal = -1;
break;
case 3: // пересечение с подтверждением
k = cb + disp;
if(CrossesAbove{mal, ma2, k)) {
if(stoch[cb] < 25.0) signal = 1;
}
else if {CrossesBelow(mal, ma2, k)) {
if(stoch[cb] > 75.0) signal = -1;
}
break;
case 4 : // пересечение с подтверждением и инверсией
k = cb + disp;
if(CrossesAbove(mal, ma2, k)) {
if (stoch[cb] < 25.0) signal = 1;
else if (stoch[cb] > 75.0) signal = -1;
}
else if (CrossesBelow (rnal, ma2 , k)) {
if (stoch[cb] > 75.0) signal = -1;
else if (stoch[cb] < 25.0) 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) { // select desired order type
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);
} // обрабатываем следующий день
}
Собственно коду предшествует ряд функций, необходимых для расчета лунных циклов на любом рынке с адаптивным подходом. Функция Model следует стандартным принципам: после объявления параметры копируются в местные переменные для простоты обращения. Комментарии указывают, что контролируют параметры. В следующем блоке рассчитывается средний истинный интервал за 50 дней (exitatrtab), используемый в выходах и при нормализации, а также лунные сезонные последовательности (savgtab) — прогнозируемые изменения цены для каждого дня. Эти ряды рассчитываются один раз для каждого рынка и заносятся в таблицы; это допустимо, поскольку при повторных вызовах Model в последующих тестах никакие важные параметры не изменяются. Второй блок рассчитывает специфические для моделей временные последовательности, необходимые для получения сигналов входа. Если modeltype=1, используется простая импульсная модель; если modeltype=2, то модель на основе пересечения; если modeltype=3, то модель на основе пересечения с подтверждением, и если modeltype=4, то модель на основе пересечения с подтверждением и инверсией. Среди возможных серий есть такие варианты, как сглаженная последовательность лунных импульсов, интегрированные импульсы (ценоподобный ряд), скользящие средние для моделей на пересечении и Медленный %К для подтверждений и инверсий. В зависимости от modeltype могут приобретать значение некоторые другие параметры. Один из них, avglen, управляет периодом всех скользящих средних: в модели на основе импульса он управляет длиной центрированного треугольного скользящего среднего, а в моделях на пересечении — длиной необходимых там средних. Другой параметр, disp, выставляет смещение, т. е. степень сдвига вперед для компенсации запаздывания скользящих средних. Параметр thresh означает величину порога, используемого в импульсной модели для длинных и коротких позиций (короткие используют отрицательное значение thresh). Переменная matype управляет видом скользящего среднего: 1 — простое, 2 — экспоненциальное, 6 — центрированное экспоненциальное, 7 — центрированное треугольное; существуют и другие виды средних, не использованные в анализе. После расчета всех рядов данных запускается цикл, который перебирает рыночные цены день за днем для моделирования торговли. Этот цикл содержит код для обновления симулятора, определения количества контрактов, избежания дней с ограниченной торговлей и т. п. В следующем блоке, расположенном внутри блока перебора текущих дней, происходит генерация сигналов входа. Правила определяются параметром modeltype. Последний блок управляет отдачей соответствующих приказов согласно параметру ordertype: 1 — вход по цене открытия, 2 — по лимитному приказу, 3 — по стоп-приказу.
Далее
Вернуться к оглавлению
|