Блог им. stanislav_g_9yc

Перевод книги "Хакер фондового рынка". Доминирующий цикл

Перевод книги "Хакер фондового рынка". Доминирующий цикл
Ранее:
1. Предисловие.
2. Торговля деньгами.
3. Биржевая цена.
4. Золотоискатели и ломбарды.
5. Тики, бары, свечи.
6. Как работают торговые системы?
7. Технический анализ — смысл и бессмыслица.
8. Трехчасовой курс программирования.
9. Первый урок: Переменные.
10. Разновидность калькулятора.
11. Второй час: Функции. 
12. Функции с возвращаемым значением.
13. Третий час: ветвление.
14. Циклы.
15. Следуйте за тенденцией.
16. Торговля с помощью фильтра низких частот.
17. Покупка и продажа.
18. Тестирование стратегии.
19. Распределение прибыли.
20. Индекс подлости.
21. Измерение результативности.
22. Метод Монте-Карло.
23. Против тенденции.
24. Визуализация сигналов

Доминирующий цикл

Алиса извлекла циклический торговый сигнал из кривой цен с помощью полосового фильтра. Это, очевидно, работает, когда кривая имеет соответствующий цикл, но не так хорошо, когда цены ведут себя по-другому. Логично было бы снова использовать MMI в качестве фильтра, но с обратным знаком — не торговать в трендовых ситуациях и торговать, когда рынок не показывает тренда. К сожалению, это не сработает. Это связано с тем, что циклическое поведение, которое хочет использовать Алиса, не связано с наличием или отсутствием долгосрочной тенденции.

У Алисы есть другая идея. Полосовой фильтр настроен на цикл примерно в одну неделю (30 баров). Поэтому необходимо проверить, действительно ли кривая цен имеет такой цикл, и подавить торговые сигналы, если это не так. Для этого Алиса использует функцию DominantPeriod для введения дополнительного торгового условия (сценарий Alice2b.c):

var Cycle = DominantPeriod(Price,10);
if(between(Cycle,20,40)) {
  if(crossUnder(Signal,-Threshold))
    reverseLong(1);
  else if(crossOver(Signal,Threshold))
    reverseShort(1);
}

DominantPeriod определяет основной цикл кривой и возвращает соответствующее количество баров. На следующем графике вы можете увидеть, как эта функция реагирует на сигнал с переменной частотой:

 Перевод книги "Хакер фондового рынка". Доминирующий цикл

Рис. 26 — Тестовая кривая и ее доминирующий цикл

Выше показана кривая цен, смоделированная тестовым сигналом. Он снова имеет уменьшающуюся частоту и, соответственно, увеличивающийся период от 10 до 60 тактов. Ниже показано возвращаемое значение функции DominantPeriod, которой подается эта кривая. Он возвращает значение между 10 и 60, что практически соответствует текущему периоду сигнала. Вы можете создать такие тестовые кривые с помощью скрипта Zorro «Filter.c».

Алиса применяет функцию DominantPeriod к серии Price с параметром фильтра 10, чтобы функция распознавала и очень короткие циклы. Результат сохраняется в переменной Cycle.

var Cycle = DominantPeriod(Price,10);

Теперь эта переменная содержит доминирующий цикл кривой цен. С помощью функции between Алиса проверяет, лежит ли этот цикл между 20 и 40 барами, т.е. может ли он быть отфильтрован полосовым фильтром, установленным на 30 баров:

if(between(Cycle,20,40)) {

  ...

Фильтрация по доминирующему циклу удваивает результат с примерно 40% до 80% годовой доходности.

При таких результатах всегда возникает вопрос: достигнет ли система таких же показателей в реальной торговле? Или результат — это просто удачный выбор параметров, которые случайно совпали с исторической кривой цен?

Чтобы определить, насколько надежна система, то есть как она реагирует на изменение условий на реальном рынке, простого бэктеста недостаточно. Алисе необходимо обучить стратегию.

Продолжение следует...
★9
6 комментариев
«Алиса извлекла циклический торговый сигнал из кривой цен с помощью полосового фильтра» — как можно вытащить циклический сигнал из не циклического источника? его же там нет.
«Это, очевидно, работает, когда кривая имеет соответствующий цикл, но не так хорошо, когда цены ведут себя по-другому» — по другому, это как?
Вы когда нибудь видели случайный сигнал?





а апериодический сигнал вы когда-нибудь видели?

Василий Федорович, да вот же у тебя на картинке!
avatar
Василий Федорович, даже для подобного графика можно посчитать доминантный цикл, там в общем не особо сложно,
у Джона Эллерса последняя версия, такая
Запускаете для своего графика и считаете, это для WealthLab 6.9
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;

namespace WealthLab.Strategies
{
	
	public class TASC201609 : WealthScript
	{
		private StrategyParameter paramEnhance;

		public TASC201609()
		{
			paramEnhance = CreateParameter("Enhance Resolution", 0, 0, 1, 1);     
		}
		
		protected override void Execute()
		{
			bool EnhanceResolution = paramEnhance.ValueInt == 0 ? false : true;
			DataSeries HP = new DataSeries(Bars, "HP");
			DataSeries Filt = new DataSeries(Bars, "Filt");
			DataSeries DominantCycle = new DataSeries(Bars, "DominantCycle");

			double Deg2Rad = Math.PI / 180.0;
			double cosInDegrees = Math.Cos((.707 * 360 / 48d) * Deg2Rad);
			double sinInDegrees = Math.Sin((.707 * 360 / 48d) * Deg2Rad);
			double alpha1 = (cosInDegrees + sinInDegrees - 1) / cosInDegrees;
			double a1 = Math.Exp(-1.414 * Math.PI / 8.0);
			double b1 = 2.0 * a1 * Math.Cos((1.414 * 180d / 8.0) * Deg2Rad);
			double c2 = b1;
			double c3 = -a1 * a1;
			double c1 = 1 - c2 - c3;

			for (int bar = 2; bar < Bars.Count; bar++)
			{
				HP[bar] = 0.5*(1 + alpha1)*(Close[bar] - Close[bar-1]) + alpha1*HP[bar-1];
				//Smooth with a SuperSmoother Filter
				Filt[bar] = c1*(HP[bar] + HP[bar-1]) / 2 + c2*Filt[bar-1] + c3*Filt[bar-2];
			}
			
			DataSeries[] ds = new DataSeries[48];
			ChartPane tp = CreatePane(50,false,false);
			for( int n = 0; n < 48; n++ ) 
			{
				ds[n] = new DataSeries(Bars,n.ToString());
				for( int bar = 0; bar < Bars.Count; bar++ )
					ds[n][bar] = n;
				PlotSeries(tp, ds[n], Color.Black, LineStyle.Solid, 16);
			}
			HideVolume();
			
			int dominantCycle = 0; 
			int sumDominantCycle = 0; 
			int countDominantCycle = 0; 
			
			for( int bar = 0; bar < Bars.Count; bar++)
			{
				SetPaneBackgroundColor(PricePane,bar,Color.Black);
				int AvgLength = 3, M = 0;
				double X = 0, Y = 0, Sx = 0, Sy = 0, Sxx = 0, Syy = 0, Sxy = 0;
				double[] Corr = new double[70];
				double[] CosinePart = new double[70];
				double[] SinePart = new double[70];
				double[] SqSum = new double[70];
				double[,] R = new double[70,2];
				double[] Pwr = new double[70];
			
				//Pearson correlation for each value of lag
				for(int Lag = 0; Lag <= 48; Lag++)
				{
					//Set the averaging length as M
					M = AvgLength;
					if( AvgLength == 0 )
						M = Lag;
					Sx = 0; Sy = 0; Sxx = 0; Syy = 0; Sxy = 0;

					for(int count = 0; count <= M-1; count++)
					{
						X = bar-count < 0 ? 0 : Filt[bar-count];
						//Y = bar-Lag+count < 0 ? 0 : Filt[bar-Lag+count];
						Y = bar-(Lag+count) < 0 ? 0 : Filt[bar-(Lag+count)]; // Rev.A
						Sx = Sx + X;
						Sy = Sy + Y;
						Sxx = Sxx + X*X;
						Sxy = Sxy + X*Y;
						Syy = Syy + Y*Y;
					}				

					if( (M*Sxx - Sx*Sx)*(M*Syy - Sy*Sy) > 0 )
						Corr[Lag] = (M*Sxy-Sx*Sy)/Math.Sqrt((M*Sxx - Sx*Sx)*(M*Syy - Sy*Sy));
				}

				//Compute the Fourier Transform for each Correlation
				for(int Period = 0; Period <= 48; Period++)
				{
					CosinePart[Period] = 0;
					SinePart[Period] = 0;

					for(int N = 3; N <= 48; N++)
					{
						double _cosInDegrees = Math.Cos(((double)N * 360 / (double)Period) * Deg2Rad);
						double _sinInDegrees = Math.Sin(((double)N * 360 / (double)Period) * Deg2Rad);
					
						CosinePart[Period] = CosinePart[Period] + Corr[N]*_cosInDegrees;
						SinePart[Period] = SinePart[Period] + Corr[N]*_sinInDegrees;
					}

					SqSum[Period] = CosinePart[Period]*CosinePart[Period] + SinePart[Period]*SinePart[Period];
				}
			
				for(int Period = 8; Period <= 48; Period++)
				{
					R[Period, 1] = R[Period, 0];
					R[Period, 0] = 0.2*SqSum[Period]*SqSum[Period] + 0.8*R[Period,1];					
				}

				//Find Maximum Power Level for Normalization
				double MaxPwr = 0;
				for(int Period = 8; Period <= 48; Period++)
				{
					if( R[Period, 0] > MaxPwr )
						MaxPwr = R[Period, 0];
				}
				for(int Period = 8; Period <= 48; Period++)
				{
					Pwr[Period] = R[Period, 0] / MaxPwr;
				}
			
				//Optionally increase Display Resolution by raising the NormPwr to
				//a higher mathematically power (since the maximum amplitude is
				//unity, cubing all amplitudes further reduces the smaller ones)
				if( EnhanceResolution )
				{
					for(int Period = 8; Period <= 48; Period++)
					{
						Pwr[Period] = Math.Pow(Pwr[Period], 3);
					}
				}

				//Compute the dominant cycle using the CG of the spectrum
				double PeakPwr = 0, Spx = 0, Sp = 0;
				for(int Period = 8; Period <= 48; Period++)
				{
					if( Pwr[Period] > PeakPwr )
						PeakPwr = Pwr[Period];
				}				

				
				double spx = 0, sp = 0;
				for (int period = 8; period <= 48; period++) 
					if (Pwr[period] >= 0.25) 
					{
						spx += period * Pwr[period]; 
						sp += Pwr[period]; 
					}

				if (sp >= 0.25) 
				{
					dominantCycle = Convert.ToInt32(spx / sp); 
					sumDominantCycle += dominantCycle; 
					countDominantCycle++; 
				}
				// PrintDebug(bar + " - " + dominantCycle);

				
				//Plot as a Heatmap
				double Color1 = 255, Color2 = 0, Color3 = 0;
				for(int Period = 8; Period < 48; Period++)
				{
					if( Pwr[Period] > 0.5 )
					{
						Color1 = 255;
						Color2 = 255*(2*Pwr[Period] - 1);
					}
					else
					{
						Color1 = 2*255*Pwr[Period];
						Color2 = 0;
					}
					
					Color1 = Math.Min((int)Color1,255); Color1 = Math.Max((int)Color1,0);
					Color2 = Math.Min((int)Color2,255); Color2 = Math.Max((int)Color2,0);
					Color3 = Math.Min((int)Color3,255); Color3 = Math.Max((int)Color3,0);
					
					SetSeriesBarColor(bar, ds[Period], Color.FromArgb(100,(int)Color1,(int)Color2,(int)Color3) );
				}
			}
			if (countDominantCycle > 20) // Если получили статистически значимую выборку доминантных циклов
				PrintDebug("Dom "+ Bars.Symbol +" = "+(1d * sumDominantCycle / countDominantCycle));

		}
	}
}
avatar
Интересно написано, красиво!
цикл примерно в одну неделю (30 баров)

Но опять всё завязано на фреймы, интервалы...
Нельзя построить качественную торговую стратегию, пользуясь переменными времени…
avatar
Eugene Bright, 
Добавлю твой комментарий в избранное))
Интересно, кто из нас прав окажется. Я в своей разработке конкретно с временем не испытывал каких особых трудностей, которых не было бы во всех остальных аспектах.
Может потому, что я не пользуюсь какими-то сложными формулами… у меня арифметика на уровне 2го класса школы
Леха Майтрейд,
"… не испытывал каких особых трудностей, которых не было бы во всех остальных аспектах."

Я и не выделяю твой опыт из общего потока и «остальных аспектов!.)))
Увы и ах, но данная проблематика редко поднимается, но это как раз и свидетельствует о том, что она не проработана.
Я ею вплотную занялся 7 лет назад. До этого — эпизодически, ибо некогда было.
Кстати, математика там ваще „1+2“ максимум.
avatar

теги блога Stanislav Gribanov

....все тэги



UPDONW
Новый дизайн