Расчёт интегральных характеристик излучений индикаторов
Введение
Излучения индикаторов - это новое и достаточно перспективное направление исследования временных рядов. Оно характеризуется тем, что анализируются не сами индикаторы, а их излучения в будущее или прошлое время. Фактически строится прогноз рыночного окружения:
- области поддержки и сопротивления в будущем.
- направление тренда (движения цены).
- какая сила движения была накоплена в прошлом.
В предыдущей моей статье "Построение излучений индикаторов в MQL5" был рассмотрен алгоритм построения излучений и перечислены основные его свойства. Напомню:
Излучение - это множество точек, расположенных в местах пересечений характерных линий исследуемых индикаторов.
При этом точки излучения имеют некоторые особенности:
- Однотипные точки излучения стремятся объединиться в скопления.
- Плотные скопления точек способны притянуть к себе или, наоборот, оттолкнуть от себя цену.
Галерея излучений:
Рис. 1. Образцы построения излучений. Слева: излучение индикатора DCMV. Справа: излучение индикаторов iMA и iEnvelopes.
В качестве примера расчёта интегральных характеристик излучений, возьмём конверты скользящей средней (Envelopes) и сами скользящие (Moving Average) вот с такими входными данными:
//--- внешняя переменная для хранения периода усреднения индикатора iEnvelopes input int ma_period=140; // период усреднения индикатора iEnvelopes //--- массив для хранения отклонений индикатора iEnvelopes double ENV[]={0.01,0.0165,0.0273,0.0452,0.0747,01234,0.204,0.3373,0.5576,0.9217,1.5237}; //--- массив для хранения периодов индикатора iMA int MA[]={4,7,11,19,31,51,85};
Итак, будем искать пересечения характерных линий выбранных индикаторов. Количество линий и их характеристики (периоды усреднения и отклонения) выбраны случайно. На самом деле, излучение можно построить на любом наборе параметров этих индикаторов (лишь бы они пересекались в пространстве).
Индикаторы выбраны, теперь приступим к созданию советника, который и будет базовой программой для анализа излучения. Нам потребуется получить рассчитанные данные от технических индикаторов iMA и iEnvelopes. Предлагаю воспользоваться вариантом рассмотренным в статье "Получение значений технических индикаторов в своих экспертах".
Для создания линий, пересечения которых мы и будем искать, достаточно задать по две точки для каждой. Следовательно, значения индикаторов достаточно получить только для двух баров (например, текущего и предыдущего). На предыдущем баре цена статична, а на текущем - динамична, поэтому с каждым тиком могут создаваться всё новые и новые точки. А вот и сам код:
//+------------------------------------------------------------------+ //| emission_of_MA_envelope.mq5 | //| Copyright 2013, DC2008 | //| https://www.mql5.com/ru/users/DC2008 | //+------------------------------------------------------------------+ #property copyright "Copyright 2013, DC2008" #property link "https://www.mql5.com/ru/users/DC2008" #property version "1.00" //--- #include <GetIndicatorBuffers.mqh> #include <Emission.mqh> //--- внешняя переменная для хранения периода усреднения индикатора iEnvelopes input int ma_period=140; // период усреднения индикатора iEnvelopes //--- массив для хранения отклонений индикатора iEnvelopes double ENV[]={0.01,0.0165,0.0273,0.0452,0.0747,01234,0.204,0.3373,0.5576,0.9217,1.5237}; //--- массив для хранения периодов индикатора iMA int MA[]={4,7,11,19,31,51,85}; //--- массив для хранения указателей индикатора iMA и iEnvelopes int handle_MA[]; int handle_Envelopes[]; //--- рыночная информация datetime T[],prevTimeBar=0; double H[],L[]; #define HL(a, b) (a+b)/2 //--- экземпляры классов CEmission EnvMa(0,300); PointEmission pEmission; //--- стили рисования точек излучения #define COLOR_UPPER C'51,255,255' #define COLOR_LOWER C'0,51,255' #define COLOR_MA C'255,51,255' color colorPoint[]={COLOR_UPPER,COLOR_LOWER,COLOR_MA}; CodeColor styleUpper={158,COLOR_UPPER,SMALL}; CodeColor styleLower={158,COLOR_LOWER,SMALL}; CodeColor styleMA={158,COLOR_MA,SMALL}; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { ArraySetAsSeries(T,true); ArraySetAsSeries(H,true); ArraySetAsSeries(L,true); //--- int size=ArraySize(MA); ArrayResize(handle_MA,size); //--- создание указателя на объект - индикатор iMA for(int i=0; i<size; i++) { handle_MA[i]=iMA(NULL,0,MA[i],0,MODE_SMA,PRICE_MEDIAN); //--- если произошла ошибка при создании объекта, то выводим сообщение if(handle_MA[i]<0) { Print("Объект iMA[",MA[i],"] не создан: Ошибка исполнения = ",GetLastError()); //--- принудительное завершение программы return(-1); } } //--- size=ArraySize(ENV); ArrayResize(handle_Envelopes,size); //--- создание указателя на объект - индикатор iEnvelopes for(int i=0; i<size; i++) { handle_Envelopes[i]=iEnvelopes(NULL,0,ma_period,0,MODE_SMA,PRICE_MEDIAN,ENV[i]); //--- если произошла ошибка при создании объекта, то выводим сообщение if(handle_Envelopes[i]<0) { Print("Объект iEnvelopes[",ENV[i],"] не создан: Ошибка исполнения = ",GetLastError()); //--- принудительное завершение программы return(-1); } } //--- return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- рыночная информация CopyTime(NULL,0,0,2,T); CopyHigh(NULL,0,0,2,H); CopyLow(NULL,0,0,2,L); //--- заполнение объявленных массивов текущими значениями из всех индикаторных буферов string name; uint GTC=GetTickCount(); //---- indicator buffers double ibMA[],ibMA1[]; // массивы для индикатора iMA double ibEnvelopesUpper[]; // массив для индикатора iEnvelopes (UPPER_LINE) double ibEnvelopesLower[]; // массив для индикатора iEnvelopes (LOWER_LINE) for(int i=ArraySize(handle_MA)-1; i>=0; i--) { if(!CopyBufferAsSeries(handle_MA[i],0,0,2,true,ibMA)) return; //--- for(int j=ArraySize(handle_Envelopes)-1; j>=0; j--) { if(!GetEnvelopesBuffers(handle_Envelopes[j],0,2,ibEnvelopesUpper,ibEnvelopesLower,true)) return; //--- найдём точку пересечения двух индикаторов iEnvelopes(UPPER_LINE) и iMA pEmission=EnvMa.CalcPoint(ibEnvelopesUpper[1],ibEnvelopesUpper[0],ibMA[1],ibMA[0],T[0]); if(pEmission.real) // если такая точка существует, то рисуем её на графике { name="iEnvelopes(UPPER_LINE)"+(string)j+"=iMA"+(string)i+(string)GTC; EnvMa.CreatePoint(name,pEmission,styleUpper); } //--- найдём точку пересечения двух индикаторов iEnvelopes(LOWER_LINE) и iMA pEmission=EnvMa.CalcPoint(ibEnvelopesLower[1],ibEnvelopesLower[0],ibMA[1],ibMA[0],T[0]); if(pEmission.real) // если такая точка существует, то рисуем её на графике { name="iEnvelopes(LOWER_LINE)"+(string)j+"=iMA"+(string)i+(string)GTC; EnvMa.CreatePoint(name,pEmission,styleLower); } } //--- for(int j=ArraySize(handle_MA)-1; j>=0; j--) { if(i!=j) { if(!CopyBufferAsSeries(handle_MA[j],0,0,2,true,ibMA1)) return; //--- найдём точку пересечения двух индикаторов iMA и iMA pEmission=EnvMa.CalcPoint(ibMA1[1],ibMA1[0],ibMA[1],ibMA[0],T[0]); if(pEmission.real) // если такая точка существует, то рисуем её на графике { name="iMA"+(string)j+"=iMA"+(string)i+(string)GTC; EnvMa.CreatePoint(name,pEmission,styleMA); } } } } //--- удаление графических объектов излучения: чтобы не засорять график if(T[0]>prevTimeBar) // удаляем один раз в бар { int total=ObjectsTotal(0,0,-1); prevTimeBar=T[0]; for(int obj=total-1;obj>=0;obj--) { string obj_name=ObjectName(0,obj,0,OBJ_TEXT); datetime obj_time=(datetime)ObjectGetInteger(0,obj_name,OBJPROP_TIME); if(obj_time<T[0]) ObjectDelete(0,obj_name); } Comment("Излучение © DC2008 Объектов = ",total); } //--- }
Не буду подробно останавливаться на рассмотрении всех деталей данного эксперта, отмечу лишь главное: для построения излучения использован экземпляр класса CEmission, который отвечает за расчёт и отображение точек пересечения любых двух линий.
//+------------------------------------------------------------------+ //| Emission.mqh | //| Copyright 2013, DC2008 | //| https://www.mql5.com/ru/users/DC2008 | //+------------------------------------------------------------------+ #property copyright "Copyright 2013, DC2008" #property link "https://www.mql5.com/ru/users/DC2008" #property version "1.00" #define BIG 7 // размер точки #define SMALL 3 // размер точки //+------------------------------------------------------------------+ //| Структура pMABB | //+------------------------------------------------------------------+ struct PointEmission { double x; // X-координата точки time double y; // Y-координата точки price datetime t; // t-координата времени точки bool real; // существует ли такая точка }; //+------------------------------------------------------------------+ //| Структура CodeColor | //+------------------------------------------------------------------+ struct CodeColor { long Code; // код символа точки color Color; // цвет точки int Width; // размер точки }; //+------------------------------------------------------------------+ //| Базовый клас для излучений | //+------------------------------------------------------------------+ class CEmission { private: int sec; int lim_Left; // предельная дальность видимости в барах int lim_Right; // предельная дальность видимости в барах public: PointEmission CalcPoint(double y1, // Y-координата 1 прямой на [1] баре double y0, // Y-координата 1 прямой на [0] баре double yy1, // Y-координата 2 прямой на [1] баре double yy0, // Y-координата 2 прямой на [0] баре datetime t0 // t-координата текущего бара Time[0] ); bool CreatePoint(string name, // имя точки PointEmission &point, // координаты точки CodeColor &style); // стиль отображения точки CEmission(int limitLeft,int limitRight); ~CEmission(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CEmission::CEmission(int limitLeft,int limitRight) { sec=PeriodSeconds(); lim_Left=limitLeft; lim_Right=limitRight; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CEmission::~CEmission() { } //+------------------------------------------------------------------+ //| Метод CalcPoint класса CEmission | //+------------------------------------------------------------------+ PointEmission CEmission::CalcPoint(double y1, // Y-координата 1 прямой на [1] баре double y0, // Y-координата 1 прямой на [0] баре double yy1,// Y-координата 2 прямой на [1] баре double yy0,// Y-координата 2 прямой на [0] баре datetime t0 // t-координата текущего бара Time[0] ) { PointEmission point={NULL,NULL,NULL,false}; double y0y1=y0-y1; double y1yy1=y1-yy1; double yy0yy1=yy0-yy1; double del0=yy0yy1-y0y1; if(MathAbs(del0)>0) { point.x=y1yy1/del0; if(point.x<lim_Left || point.x>lim_Right) return(point); point.y=y1+y0y1*y1yy1/del0; if(point.y<0) return(point); point.t=t0+(int)(point.x*sec); point.real=true; return(point); } return(point); } //+------------------------------------------------------------------+ //| Метод CreatePoint класса CEmission | //+------------------------------------------------------------------+ bool CEmission::CreatePoint(string name, // имя точки PointEmission &point, // координаты точки CodeColor &style) // стиль отображения точки { if(ObjectCreate(0,name,OBJ_TEXT,0,0,0)) { ObjectSetString(0,name,OBJPROP_FONT,"Wingdings"); ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_CENTER); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,style.Width); ObjectSetString(0,name,OBJPROP_TEXT,CharToString((uchar)style.Code)); ObjectSetDouble(0,name,OBJPROP_PRICE,point.y); ObjectSetInteger(0,name,OBJPROP_TIME,point.t); ObjectSetInteger(0,name,OBJPROP_COLOR,style.Color); return(true); } return(false); }
Следует отметить, что точки излучения изображаются с помощью графических объектов типа "Текст". Во-первых, это объясняется тем, что привязка объектов необходима по центру символа, а во-вторых, размер объектов можно изменять в очень широких пределах. Такие свойства точек открывают большие возможности для получения сложных излучений.
Рис. 2. Исходное излучение индикаторов iMA и iEnvelopes
Интегральные характеристики излучений
Итак, запустив на графике предложенный советник, мы получили множество точек разного цвета (см. Рис.2):
- Голубой цвет - пересечения iMA и iEnvelopes, буфер UPPER_LINE.
- Синий цвет - пересечения iMA и iEnvelopes, буфер LOWER_LINE.
- Фиолетовый цвет - пересечения iMA и iMA.
Использовать в автоматическом трейдинге этот хаос невозможно: нужны сигналы, уровни и прочие количественные характеристики рынка. А здесь только одни визуальные образы для медитации и хиромантии. Мир цифр отсутствует.
Интегральные характеристики излучений - это такие характеристики, которые обобщают данные полученные в результате излучений индикаторов.
Необходимость создания интегральных характеристик излучений обусловлена ещё и тем, что открывается перспектива исследования рынка с помощью новых видов индикаторов: интегральные каналы, линии, уровни, сигналы и т.п. Для определения наиболее типичных значений излучения, начнём с малого - рассчитаем среднюю цену для каждого типа точек и проведём через них горизонтальные линии как показано на рисунке:
Рис. 3. Горизонтальные линии средней цены для каждого типа точек
Для этого внесём в имеющийся код несколько дополнительных блоков. В раздел данных:
//--- массивы для расчёта и отображения интегральных характеристик излучения #define NUMBER_TYPES_POINT 3 double sum[NUMBER_TYPES_POINT],sumprev[NUMBER_TYPES_POINT]; datetime sum_time[NUMBER_TYPES_POINT]; int n[NUMBER_TYPES_POINT],W[NUMBER_TYPES_POINT]; color colorLine[]={clrAqua,clrBlue,clrMagenta};
В модуль OnTick():
//--- расчёт интегральных характеристик излучения ArrayInitialize(n,0); ArrayInitialize(sum,0.0); ArrayInitialize(sum_time,0.0); for(int obj=total-1;obj>=0;obj--) { string obj_name=ObjectName(0,obj,0,OBJ_TEXT); datetime obj_time=(datetime)ObjectGetInteger(0,obj_name,OBJPROP_TIME); if(obj_time>T[0]) { color obj_color=(color)ObjectGetInteger(0,obj_name,OBJPROP_COLOR); double obj_price=ObjectGetDouble(0,obj_name,OBJPROP_PRICE); for(int i=ArraySize(n)-1; i>=0; i--) if(obj_color==colorPoint[i]) { n[i]++; sum[i]+=obj_price; sum_time[i]+=obj_time; } } } //--- отображение интегральных характеристик излучения for(int i=ArraySize(n)-1; i>=0; i--) { if(n[i]>0) { name="H.line."+(string)i; ObjectCreate(0,name,OBJ_HLINE,0,0,0,0); ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]); ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DASHDOT); ObjectSetInteger(0,name,OBJPROP_WIDTH,1); ObjectSetDouble(0,name,OBJPROP_PRICE,sum[i]/n[i]); } }
Идем дальше. Теперь рассчитаем среднее значение времени для каждого из наборов точек, и пометим его маркером на соответствующей прямой средней цены (см. Рис.4). Таким образом, мы получили первые количественные характеристики излучений, которые, в свою очередь, не стоят на месте, а перемещаются в пространстве.
На графике мы видим только мгновенное их местоположение. Нам же нужно каким-либо образом зафиксировать их в истории и затем исследовать. Пока непонятно как это сделать, надо подумать... А тем временем, добавим небольшое усовершенствование - отобразим количество точек принимавших участие в расчёте около маркеров на графике. Это своего рода веса полученных характеристик, которые нам так же пригодятся для дальнейшего анализа.
Рис. 4. Маркеры в точках пересечения средней цены и среднего времени
Хотя нет, лучше использовать их процентное соотношение - так будет удобней анализировать. Поскольку главными точками излучения являются те, которые получились в результате пересечения индикаторов iMA и iEnvelopes, то вот их сумму и возьмём за 100%. Посмотрите что получилось:
Рис. 5. Процентное соотношение для каждого типа точек излучения
Если сложить все три значения, то 100% не получится, а будет больше. Значение 34.4 фиолетового цвета на рисунке - это характеристика точек пересечения iMA и iMA в конкретный момент времени, т.е. индикатор пересекался сам с собой, но с разными входными данными. В данном случае, это значение носит справочный характер, и может быть в дальнейшем мы придумаем как его использовать для анализа рынка.
Однако, при получении процентных соотношений количества точек, возникает новая проблема: как зафиксировать в истории ещё и процентные значения характеристик излучения? Ведь они тоже изменяются!
Графический анализ
Получив интегральные характеристики излучения, мы пока не приблизились к анализу и проектированию на их основе торговой стратегии. Однако, внимательный читатель наверное уже заметил решение этой проблемы (см. Рис.1). А решение следующее: предлагаю рисовать интегральные кривые разной толщины, которые будут пропорциональны процентному соотношению главных точек излучения.
Текущую часть кривой будем строить вдоль линии средней цены между текущим и предыдущим баром, а так же будем иметь ввиду, что эти координаты на самом деле взяты из будущего. Это своего рода опережающий интегральный канал излучения индикаторов. Как всё запутано... И стоит ли читать дальше? Надеюсь, дальше будет интересней.
Рис. 6. Интегральный канал излучения индикаторов
Ну вот, вроде и нашли применение излучению "iMA & iMA" (фиолетовый цвет на графике). Мы получили новый индикатор - интегральная скользящая средняя.
Давайте вернёмся к коду советника и посмотрим какие там произошли изменения в модуле OnTick():
//--- отображение интегральных характеристик излучения ArrayInitialize(W,10); W[ArrayMaximum(n)]=20; W[ArrayMinimum(n)]=3; for(int i=ArraySize(n)-1; i>=0; i--) { if(n[i]>0) { //--- горизонтальные линии средних цен name="H.line."+(string)i; ObjectCreate(0,name,OBJ_HLINE,0,0,0,0); ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]); ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DASHDOT); ObjectSetInteger(0,name,OBJPROP_WIDTH,1); ObjectSetDouble(0,name,OBJPROP_PRICE,sum[i]/n[i]); //--- маркеры name="P."+(string)i; ObjectCreate(0,name,OBJ_TEXT,0,0,0); ObjectSetString(0,name,OBJPROP_FONT,"Wingdings"); ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_CENTER); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,17); ObjectSetString(0,name,OBJPROP_TEXT,CharToString(163)); ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]); ObjectSetDouble(0,name,OBJPROP_PRICE,sum[i]/n[i]); ObjectSetInteger(0,name,OBJPROP_TIME,sum_time[i]/n[i]); //--- интегральные кривые name="T"+(string)i+".line"+(string)T[1]; ObjectCreate(0,name,OBJ_TREND,0,0,0); ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]); ObjectSetInteger(0,name,OBJPROP_WIDTH,W[i]); if(sumprev[i]>0) { ObjectSetDouble(0,name,OBJPROP_PRICE,0,sumprev[i]); ObjectSetInteger(0,name,OBJPROP_TIME,0,T[1]); ObjectSetDouble(0,name,OBJPROP_PRICE,1,(sum[i]/n[i])); ObjectSetInteger(0,name,OBJPROP_TIME,1,T[0]); } //--- численные значения интегральных характеристик name="Text"+(string)i+".control"; ObjectCreate(0,name,OBJ_TEXT,0,0,0); ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,30); ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]); string str=DoubleToString((double)n[i]/(double)(n[0]+n[1])*100,1); ObjectSetString(0,name,OBJPROP_TEXT,str); ObjectSetDouble(0,name,OBJPROP_PRICE,0,(sum[i]/n[i])); ObjectSetInteger(0,name,OBJPROP_TIME,0,sum_time[i]/n[i]); } }
Продолжим наш графический анализ. Но чего-то не хватает... Дело в том, что мы пропустили ещё одну важную характеристику излучения. Интегральные кривые были построены только по средним ценам, а как же учесть другую координату - среднее время? Посмотрите на рисунок ниже и обратите внимание на границы канала:
- Голубая линия - верхняя граница канала.
- Синяя линия - нижняя граница канала.
Нас интересует, какой маркер находился ближе по времени к нулевому бару.
Рис. 7. Опережение по времени интегральных характеристик. Слева: опережение на верхней границе канала. Справа: опережение на нижней границе канала.
Решить эту задачу можно следующим образом: наложить на график цен линию по ценам (PRICE_MEDIAN), которая меняла бы цвет в зависимости от того, какой маркер (голубой или синий) ближе к последнему бару (см. Рис.7). А в код добавим вот этот блок:
//--- if(n[ArrayMinimum(n)]>0) { datetime d[2]; for(int j=0;j<2;j++) { d[j]=sum_time[j]/n[j]; } int i=ArrayMinimum(d); name="Price.line"+(string)T[1]; ObjectCreate(0,name,OBJ_TREND,0,0,0); ObjectSetInteger(0,name,OBJPROP_WIDTH,8); ObjectSetDouble(0,name,OBJPROP_PRICE,0,HL(H[1],L[1])); ObjectSetInteger(0,name,OBJPROP_TIME,0,T[1]); ObjectSetDouble(0,name,OBJPROP_PRICE,1,HL(H[0],L[0])); ObjectSetInteger(0,name,OBJPROP_TIME,1,T[0]); ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine1[i]); } //---
Внимание, сейчас будет "контрольный выстрел". А что если нам построить излучение от интегральных характеристик исходного излучения, что-то вроде излучения второго порядка? Ведь эти линии так же пересекаются между собой и, следовательно, должны иметь точки излучения. Давайте посмотрим, что из этого может получиться. Дополним код в предыдущем блоке вот такими строками:
//--- излучение от интегральных характеристик исходного излучения pEmission=EnvMa.CalcPoint(sumprev[0],sum[0]/n[0],sumprev[2],sum[2]/n[2],T[0]); if(pEmission.real) // если такая точка существует, то рисуем её на графике { name="test/up"+(string)GTC; EnvMa.CreatePoint(name,pEmission,styleUpper2); } pEmission=EnvMa.CalcPoint(sumprev[1],sum[1]/n[1],sumprev[2],sum[2]/n[2],T[0]); if(pEmission.real) // если такая точка существует, то рисуем её на графике { name="test/dn"+(string)GTC; EnvMa.CreatePoint(name,pEmission,styleLower2); }
А в раздел данных добавим следующее:
#define COLOR_2_UPPER C'102,255,255' #define COLOR_2_LOWER C'51,102,255' CodeColor styleUpper2={178,COLOR_2_UPPER,BIG}; CodeColor styleLower2={178,COLOR_2_LOWER,BIG};
Результат можно оценить на рисунке ниже. Появились новые точки, которые пока ни о чём не говорят.
Рис. 8. Излучение интегральных линий
Естественно, для новых точек так же можно построить интегральные характеристики (см. Рис.9), а от них новое излучение и так до тех пор, пока оно не выродится!
Рис. 9. Интегральные характеристики излучения
Итак, мы сделали необходимые построения на графике и получили интегральные характеристики излучения. Теперь их можно анализировать и приступить к созданию торговой стратегии. Однако, это по-прежнему невозможно! А теперь-то что мешает?
Таймсерии излучений
Графический анализ позволяет исследовать интегральные характеристики излучения, но он слишком ресурсоёмкий. Если запустить предложенный код в тестере в режиме визуализации, то через какое-то время скорость тестирования упадёт до нуля! Это объясняется наличием большого количества графических объектов на графике.
Естественным образом возникает желание избавиться от такого обилия точек, а оставить только интегральные кривые. Для решения этой задачи воспользуемся специальными массивами (буферами).
Таймсерии излучений - это специальным образом организованные массивы, в которых накапливается информация об излучении.
Причём от привычных таймсерий они отличаются тем, что данные в них не упорядочены по времени, хотя время является ключевым полем.
Рис. 10. Таймсерии характеристик излучения
Организация в этих массивах устроена таким образом, что новые элементы записываются в свободные или занятые устаревшими значениями ячейки. Для этого использован класс CTimeEmission. Вот как это выглядит в коде:
//+------------------------------------------------------------------+ //| TimeEmission.mqh | //| Copyright 2013, DC2008 | //| https://www.mql5.com/ru/users/DC2008 | //+------------------------------------------------------------------+ #property copyright "Copyright 2013, DC2008" #property link "https://www.mql5.com/ru/users/DC2008" #property version "1.00" //--- #include <Emission.mqh> #define ARRMAX 64 #define ARRDELTA 8 //+------------------------------------------------------------------+ //| Структура pIntegral | //+------------------------------------------------------------------+ struct pIntegral { double y; // Y-координата точки price (средняя цена точек с одним временем) datetime t; // t-координата времени точки int n; // n-количество точек с одним временем }; //+------------------------------------------------------------------+ //| Базовый клас для таймсерий излучений | //+------------------------------------------------------------------+ class CTimeEmission { private: pIntegral time_series_Emission[]; // таймсерия излучения int size_ts; // количество элементов в таймсерии datetime t[1]; public: //--- метод записи новых элементов в таймсерию излучения void Write(PointEmission &point); //--- метод чтения интегральных характеристик излучения pIntegral Read(); CTimeEmission(); ~CTimeEmission(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CTimeEmission::CTimeEmission() { ArrayResize(time_series_Emission,ARRMAX,ARRMAX); size_ts=ArraySize(time_series_Emission); for(int i=size_ts-1; i>=0; i--) time_series_Emission[i].t=0; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CTimeEmission::~CTimeEmission() { } //+------------------------------------------------------------------+ //| Метод Write класса CTimeEmission | //+------------------------------------------------------------------+ void CTimeEmission::Write(PointEmission &point) { CopyTime(NULL,0,0,1,t); size_ts=ArraySize(time_series_Emission); for(int k=0;k<size_ts;k++) { if(time_series_Emission[k].t<t[0]) // находим первую свободную ячейку { if(k>size_ts-ARRDELTA) { // если необходимо, то увеличиваем размер массива int narr=ArrayResize(time_series_Emission,size_ts+ARRMAX,ARRMAX); for(int l=size_ts-1;l<narr;l++) time_series_Emission[l].t=0; } time_series_Emission[k].y=point.y; time_series_Emission[k].t=point.t; time_series_Emission[k].n=1; return; } if(time_series_Emission[k].t==point.t) // находим первую похожую ячейку { time_series_Emission[k].y=(time_series_Emission[k].y*time_series_Emission[k].n+point.y)/(time_series_Emission[k].n+1); time_series_Emission[k].n++; return; } } } //+------------------------------------------------------------------+ //| Метод Read класса CTimeEmission | //+------------------------------------------------------------------+ pIntegral CTimeEmission::Read() { CopyTime(NULL,0,0,1,t); pIntegral property_Emission={0.0,0,0}; size_ts=ArraySize(time_series_Emission); for(int k=0;k<size_ts;k++) { if(time_series_Emission[k].t>=t[0]) { property_Emission.y+=time_series_Emission[k].y*time_series_Emission[k].n; property_Emission.t+=(time_series_Emission[k].t-t[0])*time_series_Emission[k].n; property_Emission.n+=time_series_Emission[k].n; } } if(property_Emission.n>0) { property_Emission.y=property_Emission.y/property_Emission.n; property_Emission.t=property_Emission.t/property_Emission.n+t[0]; } return(property_Emission); }
Здесь реализуются два метода класса: запись точки излучения в массив-таймсерию и чтение значений интегральных характеристик излучения.
Экономный расчёт интегральных характеристик
Имея таймсерии излучений, можно приступить к созданию экономного алгоритма расчёта интегральных характеристик, а затем и к созданию торговой стратегии. Переработаем исходный советник:
//+------------------------------------------------------------------+ //| emission_of_MA_envelope_ts.mq5 | //| Copyright 2013, DC2008 | //| https://www.mql5.com/ru/users/DC2008 | //+------------------------------------------------------------------+ #property copyright "Copyright 2013, DC2008" #property link "https://www.mql5.com/ru/users/DC2008" #property version "1.00" //--- #include <GetIndicatorBuffers.mqh> #include <Emission.mqh> #include <TimeEmission.mqh> //--- количество типов точек #define NUMBER_TYPES_POINT 3 //--- массив для хранения периодов индикатора iMA int MA[]={4,7,11,19,31,51,85}; //--- внешняя переменная для хранения периода усреднения индикатора iEnvelopes input int ma_period=140; // период усреднения индикатора iEnvelopes //--- массив для хранения отклонений индикатора iEnvelopes double ENV[]={0.01,0.0165,0.0273,0.0452,0.0747,01234,0.204,0.3373,0.5576,0.9217,1.5237}; //--- массив для хранения указателей индикатора iMA int handle_MA[]; //--- массив для хранения указателей индикатора iEnvelopes int handle_Envelopes[]; //--- рыночная информация datetime T[],prevTimeBar=0; double H[],L[]; #define HL(a, b) (a+b)/2 //--- экземпляры классов CEmission EnvMa(0,200); PointEmission pEmission; CTimeEmission tsMA[NUMBER_TYPES_POINT]; pIntegral integral[NUMBER_TYPES_POINT]; //--- стили рисования точек излучения #define DEL 500 //--- массивы для расчёта и отображения интегральных характеристик излучения double sumprev[NUMBER_TYPES_POINT]; int n[NUMBER_TYPES_POINT],W[NUMBER_TYPES_POINT]; color colorLine[]={clrAqua,clrBlue,clrMagenta}; int fontPoint[]={30,30,30}; int fontMarker[]={16,16,16}; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { ArraySetAsSeries(T,true); ArraySetAsSeries(H,true); ArraySetAsSeries(L,true); ArrayInitialize(sumprev,0.0); //--- int size=ArraySize(MA); ArrayResize(handle_MA,size); //--- создание указателя на объект - индикатор iMA for(int i=0; i<size; i++) { handle_MA[i]=iMA(NULL,0,MA[i],0,MODE_SMA,PRICE_MEDIAN); //--- если произошла ошибка при создании объекта, то выводим сообщение if(handle_MA[i]<0) { Print("Объект iMA[",MA[i],"] не создан: Ошибка исполнения = ",GetLastError()); //--- принудительное завершение программы return(-1); } } //+------------------------------------------------------------------+ size=ArraySize(ENV); ArrayResize(handle_Envelopes,size); //--- создание указателя на объект - индикатор iEnvelopes for(int i=0; i<size; i++) { handle_Envelopes[i]=iEnvelopes(NULL,0,ma_period,0,MODE_SMA,PRICE_MEDIAN,ENV[i]); //--- если произошла ошибка при создании объекта, то выводим сообщение if(handle_Envelopes[i]<0) { Print("Объект iEnvelopes[",ENV[i],"] не создан: Ошибка исполнения = ",GetLastError()); //--- принудительное завершение программы return(-1); } } //--- return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- рыночная информация CopyTime(NULL,0,0,2,T); CopyHigh(NULL,0,0,2,H); CopyLow(NULL,0,0,2,L); //--- заполнение объявленных массивов текущими значениями из всех индикаторных буферов string name; uint GTC=GetTickCount(); //---- indicator buffers double ibMA[],ibMA1[]; // массивы для индикатора iMA double ibEnvelopesUpper[]; // массив для индикатора iEnvelopes (UPPER_LINE) double ibEnvelopesLower[]; // массив для индикатора iEnvelopes (LOWER_LINE) for(int i=ArraySize(handle_MA)-1; i>=0; i--) { if(!CopyBufferAsSeries(handle_MA[i],0,0,2,true,ibMA)) return; //--- for(int j=ArraySize(handle_Envelopes)-1; j>=0; j--) { if(!GetEnvelopesBuffers(handle_Envelopes[j],0,2,ibEnvelopesUpper,ibEnvelopesLower,true)) return; //--- найдём точку пересечения двух индикаторов iEnvelopes(UPPER_LINE) и iMA pEmission=EnvMa.CalcPoint(ibEnvelopesUpper[1],ibEnvelopesUpper[0],ibMA[1],ibMA[0],T[0]); if(pEmission.real) // если такая точка существует, то добавляем её в таймсерию излучения tsMA[0].Write(pEmission); //--- найдём точку пересечения двух индикаторов iEnvelopes(LOWER_LINE) и iMA pEmission=EnvMa.CalcPoint(ibEnvelopesLower[1],ibEnvelopesLower[0],ibMA[1],ibMA[0],T[0]); if(pEmission.real) // если такая точка существует, то добавляем её в таймсерию излучения tsMA[1].Write(pEmission); } //--- for(int j=ArraySize(handle_MA)-1; j>=0; j--) { if(i!=j) { if(!CopyBufferAsSeries(handle_MA[j],0,0,2,true,ibMA1)) return; //--- найдём точку пересечения двух индикаторов iMA и iMA pEmission=EnvMa.CalcPoint(ibMA1[1],ibMA1[0],ibMA[1],ibMA[0],T[0]); if(pEmission.real) // если такая точка существует, то добавляем её в таймсерию излучения tsMA[2].Write(pEmission); } } } //--- удаление графических объектов излучения: чтобы не засорять график if(T[0]>prevTimeBar) { prevTimeBar=T[0]; //--- for(int i=ArraySize(n)-1; i>=0; i--) sumprev[i]=integral[i].y; //--- for(int obj=ObjectsTotal(0,0,-1)-1;obj>=0;obj--) { string obj_name=ObjectName(0,obj,0,OBJ_TREND); datetime obj_time=(datetime)ObjectGetInteger(0,obj_name,OBJPROP_TIME); if(obj_time<T[0]-DEL*PeriodSeconds()) ObjectDelete(0,obj_name); } Comment("Излучение © DC2008 Графических объектов = ",ObjectsTotal(0,0,-1)); } //--- расчёт интегральных характеристик излучения for(int i=ArraySize(n)-1; i>=0; i--) integral[i]=tsMA[i].Read(); //--- отображение интегральных характеристик излучения ArrayInitialize(W,5); if(integral[0].n>integral[1].n) { W[0]=20; W[1]=10; } else { W[0]=10; W[1]=20; } for(int i=ArraySize(n)-1; i>=0; i--) { //--- горизонтальные линии средних цен name="H.line."+(string)i; ObjectCreate(0,name,OBJ_HLINE,0,0,0,0); ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]); ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DASHDOT); ObjectSetInteger(0,name,OBJPROP_WIDTH,1); ObjectSetDouble(0,name,OBJPROP_PRICE,integral[i].y); //--- маркеры name="P."+(string)i; ObjectCreate(0,name,OBJ_TEXT,0,0,0); ObjectSetString(0,name,OBJPROP_FONT,"Wingdings"); ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_CENTER); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,fontMarker[i]); ObjectSetString(0,name,OBJPROP_TEXT,CharToString(163)); ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]); ObjectSetDouble(0,name,OBJPROP_PRICE,integral[i].y); ObjectSetInteger(0,name,OBJPROP_TIME,integral[i].t); //--- интегральные кривые name="T"+(string)i+".line"+(string)T[1]; ObjectCreate(0,name,OBJ_TREND,0,0,0); ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]); ObjectSetInteger(0,name,OBJPROP_WIDTH,W[i]); if(sumprev[i]>0) { ObjectSetDouble(0,name,OBJPROP_PRICE,0,sumprev[i]); ObjectSetInteger(0,name,OBJPROP_TIME,0,T[1]); ObjectSetDouble(0,name,OBJPROP_PRICE,1,integral[i].y); ObjectSetInteger(0,name,OBJPROP_TIME,1,T[0]); } //--- численные значения интегральных характеристик if(integral[0].n+integral[1].n>0) { name="Text"+(string)i+".control"; ObjectCreate(0,name,OBJ_TEXT,0,0,0); ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,fontPoint[i]); ObjectSetInteger(0,name,OBJPROP_COLOR,colorLine[i]); string str=DoubleToString((double)integral[i].n/(double)(integral[0].n+integral[1].n)*100,1); ObjectSetString(0,name,OBJPROP_TEXT,str); ObjectSetDouble(0,name,OBJPROP_PRICE,0,integral[i].y); ObjectSetInteger(0,name,OBJPROP_TIME,0,integral[i].t); } } }
Код сократился, а скорость расчётов выросла. Вот теперь можно тестировать и оптимизировать своих роботов без визуализации!
Как использовать интегральные характеристики в торговле
Интегральные характеристики излучений можно использовать как генератор сигналов на:
- пробой канала,
- пересечение между собой или с ценой,
- смену направления движения.
Рис. 11. Торговые сигналы на пересечении интегральных характеристик излучения
Заключение
- Расчёт интегральных характеристик излучений индикаторов позволяет использовать новые инструменты и методы для анализа рынка (временных рядов).
- Применение таймсерий излучений увеличило скорость расчёта интегральных характеристик.
- Появилась возможность создания автоматизированных торговых стратегий использующих излучения.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Итить!.... Какое верное прогнозирование! Кто то кажись уже выкладывал подобное, не помню уже в какой ветке, что то про экстраполяцию.
Но этот ролик ваще жирок! С музычкой и главное сама экстраполяция не зависит от средней, как у всяких там новичьков, видимо тахионное поле используется.
Вот ещё того же автора.
А вот...
Видео по теме
DC2008, скажите пожалуйста, что за дополнительные линии (голубая со значением 0.8 и синяя со значением 1.1)?