English 中文 Español Deutsch 日本語 Português
preview
Возможности Мастера MQL5, которые вам нужно знать (Часть 6): Преобразование Фурье

Возможности Мастера MQL5, которые вам нужно знать (Часть 6): Преобразование Фурье

MetaTrader 5Тестер | 23 июня 2023, 08:05
1 017 1
Stephen Njuki
Stephen Njuki

Введение

Fourier transform - это метод разложения волны точек данных на возможные составные части. Введен Жозефом Фурье.  Интегрирование по всем возможным частотам в преобразовании Фурье дает нам спектр компонентов, так как оно разбивает исходную функцию на составляющие, каждая из которых соответствует своему частотному компоненту.

Преобразование Фурье по определению находится в диапазоне от минус бесконечности до плюс бесконечности:


где F(w) — преобразование Фурье функции f(t), i — мнимая единица, w — угловая частота, e — экспоненциальная функция.

На практике, однако, при использовании преобразования количество эпициклов предопределяется небольшим удобным в работе числом. Чем больше эпициклов (частот компонентов) используется, тем точнее мы можем описать исходную кривую, однако в определенный момент этот процесс становится неэффективным, поскольку большее количество эпициклов не приводит к какой-либо значимой разнице в кривой, части которой описываются.

Таким образом, при преобразовании нам приходится довольствоваться дискретным конечным числом n эпициклов. Это немного меняет нашу формулу выше.

Когда мы складываем все возможные частоты, мы суммируем вклад каждой в исходную функцию. Каждая частотная составляющая представлена комплексным числом, которое включает информацию об амплитуде и фазе. Величина представляет собой амплитуду волны, а фаза представляет собой сдвиг во времени волны. Итак, наше модифицированное выше уравнение суммирует по n компонентам, и каждая из этих n частей имеет волну с различными значениями в моменты времени t.

При решении составных частей ключом является нахождение f(t) каждого компонента, который составляет как действительную, так и мнимую часть. Система Фурье переписывает любую функцию f(t) для интервала от 0 до 2 пи с комплексными числами наподобие "бесконечной суммы".  Выражение взято в кавычки, потому что в сумме дает ноль.


Используя уравнение выше при решении, скажем, коэффициента:  C 2 ,

Мы умножим обе части уравнения на: e-2it

При интегрировании это упрощает наше уравнение до:



Итак, 2 (не 2 в 2 pi!) можно заменить на n, чтобы получить любой другой коэффициент в уравнении f(t) выше. Все эти вычисления выполняются классом CFastFourierTransform, который указан в файле Math\Alglib\fasttransforms.mqh, поэтому все, что нам нужно сделать, это использовать эту библиотеку.


Применение

Прежде чем перейти к конкретным примерам, может быть полезно сначала взглянуть на то, как трейдеры могут извлечь выгоду из возможности разбить волну точек данных на составные части. Возможных сфер применения очень много. Вот возможный краткий список:

  1. Анализ движения цен на акции: если мы соберем данные о ценах на акции и организуем их в формате временных рядов, мы сможем применить преобразование Фурье к данным ряда, чтобы получить его частотные компоненты. Анализ этих отдельных частотных компонентов для выявления закономерностей и тенденций в данных может помочь принимать более обоснованные торговые решения. Например, трейдер может заметить, что у определенной акции есть повторяющийся пик в ее частотном спектре с определенной частотой, что указывает на повторяющийся паттерн в движении цены акции. Это наблюдение можно использовать для покупки акции по низкой цене и продажи ее по высокой цене, исходя из ожидания, что цена будет следовать наблюдаемому паттерну. Однако при таком подходе нужно постоянно убеждаться, что шаблоны остаются применимыми.
  1. Анализ долгосрочных циклов: нам нужно будет выбирать между бизнес-циклом и рыночным циклом. Если мы выберем бизнес-цикл, наши возможные источники данных могут включать:

  • Валовой внутренний продукт (ВВП): один из наиболее важных показателей экономического роста, который измеряет общую стоимость товаров и услуг, произведенных в стране за определенный период, обычно за год.
  • Данные о занятости: данные об уровне безработицы и/или количестве созданных рабочих мест могут дать представление о состоянии рынка труда. Рост безработицы или стагнация роста рабочих мест могут сигнализировать об ослаблении экономики.
  • Данные о промышленном производстве: могут включать в себя индекс промышленного производства, который измеряет объем производства в обрабатывающем, горнодобывающем и коммунальном секторах и может указывать на общее состояние экономики, индекс деловой активности, использование мощностей, заказы на товары длительного пользования, занятость в сфере производства и многие другие.
  • Данные об уровне потребительского доверия: индекс потребительского доверия, индекс потребительских настроений от Мичиганского университета, индекс потребительского доверия Conference Board, объем розничного бизнеса, потребительское кредитование и многие другие.
  • Данные о процентных ставках: процентные ставки могут влиять на инвестиции в бизнес и потребительские расходы, которые могут повлиять на показатели экономики. Повышение процентных ставок, как правило, сдерживает инвестиции и расходы, в то время как падение процентных ставок может стимулировать инвестиции и расходы, которые в конечном итоге влияют на цену акций компаний, котирующихся на бирже.
  • Данные об инфляции: измеряют темпы роста цен на товары и услуги и, таким образом, могут внести некоторую ясность в вопросе о состоянии экономики. Высокая инфляция может указывать на перегретую экономику, а низкая инфляция может сигнализировать о слабой экономике. Это еще один набор данных, который может повлиять на решения трейдера при покупке различных акций, и поэтому является ключевым источником данных.
  • Данные о жилье: включает в себя продажи домов и начало строительства, а также может пролить свет на состояние рынка жилья, который является важным компонентом экономики и может иметь отношение к конкретным акциям, которые есть в портфеле трейдера.

Некоторые источники данных о рыночном цикле могут включать:

  • Соотношение цена/прибыль (Price/Earnings, P/E): этот общий показатель сравнивает цену акций компании с ее прибылью на акцию (earnings per share, EPS) и может использоваться для определения того, переоценивает ли рынок акции (при высоком значении) или недооценивает их, когда значение обычно ниже 15 или даже 10 в зависимости от географии рынка.
  • Дивидендная доходность: измеряет сумму дивидендов, выплачиваемых на каждую акцию. Низкая дивидендная доходность может указывать на переоцененность акций, а высокая - на недооцененность.
  • Технические индикаторы: включает скользящие средние, индикаторы импульса и индикаторы относительной силы. Может помочь обобщить текущее поведение цены акции. Например, когда цена акции торгуется выше своей 200-дневной скользящей средней, это может указывать на бычий тренд, а торговля ниже 200-дневной скользящей средней может сигнализировать о медвежьем тренде, поэтому в этом случае наши данные могут быть спредом между цена и ее 200-дневной скользящей средней.
  • Широта рынка: измеряет количество акций, которые растут по сравнению с теми, которые снижаются на данном рынке. Когда большое количество акций растет, это может указывать на то, что рынок здоров, а уменьшение ширины может указывать на слабость. Такие данные могут быть получены из сторонних источников, которые обычно предоставляют их в связке с другой информацией за единовременную плату или, в некоторых случаях, по подписке.
  • Волатильность: измеряется такими индикаторами, как VIX, и помогает в оценке рыночного риска и настроений инвесторов. Это часто положительно коррелирует с рыночным риском и негативными настроениями.

Как только мы соберем соответствующие финансовые данные из указанных выше возможных источников, мы упорядочим их во временной ряд, а затем применим преобразование Фурье, чтобы разбить их на частотные компоненты. Анализ этих составных частей, скажем, поиск повторяющихся циклов в данных, может улучшить наши торговые решения. Например, трейдер может использовать его, чтобы определить повторяющийся цикл в данных по процентным ставкам, который обычно длится несколько лет и имеет запаздывающую положительную корреляцию с его торгуемыми акциями или ETF. Вооруженный этим знанием, трейдер может инвестировать в долгосрочные позиции, когда рынок находится в нижней точке, и удерживать позиции с просадкой в течение длительных периодов времени и закрывать их только тогда, когда корреляция процентных данных становится отрицательной или предвещает спад.

  1. Обработка сигналов: при рассмотрении сигналов мы могли бы рассмотреть паттерны ценовых волн, которые предшествуют крупным прорывам вверх или вниз. Например, конкретный источник данных может рассматривать только паттерн Гартли в истории цен на конкретную акцию, опуская другие данные. Эти данные о волновой цене будут зарегистрированы как временной ряд (даже несмотря на то, что будут существовать значительные разрывы, поскольку паттерн встречается редко). Преобразование Фурье будет применяться к каждой волне в этой серии. Затем мы могли бы установить частотный порог для составляющих волн Фурье (на основе наших наблюдений с корреляцией с возможными прорывами) и опустить те, которые не соответствуют порогу. Затем мы использовали бы эти несколько отфильтрованных волн, чтобы сделать наши прогнозы. Этот ценовой буфер может быть для акции, валютной пары или даже товара. Принцип будет тот же.
  1. Управление рисками: для нас особенно важными могут оказаться данные о кредитном риске, поскольку другие виды риска, такие как рыночный риск, могут пересекаться с тем, что мы уже рассмотрели выше: 

  • Уровень дефолтов: Квартальные или годовые уровни дефолтов для определенного банковского рынка, скажем, США, можно использовать для анализа кредитного риска для США и, следовательно, показателей S&P 500.
  • Спреды ставок (credit spreads): ежедневные или еженедельные данные о кредитных спредах между корпоративными и казначейскими облигациями могут помочь оценить кредитный риск, связанный с изменениями в рыночном восприятии кредитоспособности.
  • Просрочки по кредиту (не следует путать с уровнем дефолтов): можно получать ежемесячно или ежеквартально для конкретного банка или кредитора и использовать для изучения кредитного риска, связанного с кредитным портфелем этого банка, если трейдер рассматривает возможность покупки акций этого конкретного банка.
  • Кредитные рейтинги: хотя эти данные не так сериализованы, как большинство данных, их все же можно собирать в течение разумного исторического периода ежеквартально или ежегодно для конкретной компании и использовать для оценки ценности коммерческих бумаг компании или даже ее долгосрочных облигаций.
  • Кредитные дефолтные свопы (credit default swaps, CDS): обычно составляют страховку кредитора для его кредита(ов). Данные можно получать ежедневно или еженедельно по ценам контрактов и использоваться для анализа кредитного риска, связанного с изменениями в рыночном восприятии кредитоспособности.

Данные из любого из этих источников будут объединены во временные ряды, а затем к данным временных рядов будет применено преобразование Фурье для получения частотных составляющих. Анализ того, какой компонент лучше всего коррелирует с возможными финансовыми потрясениями или более конкретными колебаниями цен на акции, может помочь в разработке эффективных стратегий управления рисками.

  1. Ценообразование опционов: исторические данные о базовой цене, цене исполнения, времени до экспирации, волатильности, процентных ставках и дивидендах по опционам можно получить из ряда источников, например, из терминала Bloomberg. Обратите внимание, что каждый из этих наборов данных является "частотной составляющей" цены опциона. Эти многочисленные наборы данных позволяют провести анализ многими способами. Имейте в виду, что единого мнения о том, какова цена опциона в любой момент времени, нет. У нас есть "лучшие практические" модели ценообразования, такие как модель Блэка-Шоулза. Но они не всегда надежны. Один из подходов может состоять в том, чтобы просто разложить цену базового актива за фиксированный период до экспирации и посмотреть, какой частотный компонент лучше всего коррелирует с разницей между базовым активом и ценой исполнения на момент экспирации. Этот анализ может быть полезен для европейских опционов, но с американскими возникают сложности, поскольку они могут быть исполнены до истечения срока действия.
  1. Анализ настроений: мы прошли долгий путь в том, что касается поиска данных в Интернете, и сегодня программное обеспечение для управления социальными сетями имеет большое значение. Такие бренды, как Zoho Social, Hootsuite, Khoros Marketing и многие другие, быстро завоевывают популярность. Прошли те времена, когда достаточно было считать лайки и дизлайки. Сегодня анализ текста (в сочетании с искусственным интеллектом) позволяет компаниям лучше количественно оценивать взаимодействие с клиентами и их удовлетворенность продуктами/услугами. Можно собрать много данных. Как трейдер, заинтересованный в двух конкурирующих компаниях, вы можете захотеть узнать, существует ли какая-либо корреляция между продолжительностью участия в обзоре продукта и возможными продажами продукта через 3 или 6 месяцев. Преобразование данных о взаимодействии во временной ряд и разложение его на составные части позволяет определить, какой компонент лучше всего соответствует нашей цели (в данном случае будущим продажам), и эта система затем направляет наше решение о покупке акций и их количестве.
  1. Машинное обучение: преобразование Фурье для этого приложения (и многих других) может помочь векторизовать входные данные, разбивая их на составляющие частоты. Если бы у нас были просто разные волны закрытия в качестве входных данных, каждую из этих волн можно было бы разложить на n волн, где каждая новая волна теперь является частью вектора волн старой неразделенной волны. Это дает нам больше идентифицирующей информации для любой новой точки данных, которую нам нужно оценить, и позволяет более точно сравнивать с уже обученными данными при оценке неизвестной волны, чем если бы мы имели дело только с исходной одиночной волной. Таким образом, обучение этих векторизованных данных и сравнение их с тестовыми данными с использованием, например, евклидова расстояния может помочь в уточнении прогнозов для модели.


Реализация

Чтобы проиллюстрировать реализацию преобразования Фурье в MQL5, мы рассмотрим разложение временного ряда ценового диапазона (максимумы минус минимумы). Затем мы изучим эти составляющие частоты и посмотрим, имеет ли какая-либо конкретная из них, идентифицированная индексом, полезную корреляцию со следующими изменениями в ценовых диапазонах. Мы будем использовать эту информацию для корректировки стоп-лоссов открытых позиций в пользовательской реализации класса CExpertTrailing. Мы будем использовать простой встроенный сигнал, указанный в SignalRSI.mqh, а управление капиталом будет использовать фиксированную маржу.

 

Если мы перечислим наш пользовательский конечный класс, как показано ниже, При получении действительных и мнимых коэффициентов для ‘функции f(t)’ мы используем экземпляр структуры ‘a1_complex’ для хранения этой информации после обработки функцией ‘FFTR1D’. Чтобы использовать эти коэффициенты, нам нужно «интегрировать» их. Для этого я придумал матрицу ‘_output’. Эта матрица имеет коэффициенты для каждого эпицикла в каждой точке данных. Мы используем 6 точек данных и 5 эпициклов. Также используется наш индекс точки данных прогноза ‘m_points-1’, поскольку циклы Фурье повторяются, поэтому следующий этап будет самым старым в цикле. 

//+------------------------------------------------------------------+
//|                                                   TrailingCT.mqh |
//|                   Copyright 2009-2013, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include <Math\Alglib\fasttransforms.mqh>
#include <Expert\ExpertTrailing.mqh>
// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Trailing Stop based on 'Fourier Transform' v3              |
//| Type=Trailing                                                    |
//| Name=CategoryTheory                                              |
//| ShortName=CT                                                     |
//| Class=CTrailingFT                                                |
//| Page=trailing_ct                                                 |
//| Parameter=Step,double,0.5,Trailing Step                          |
//| Parameter=Index,int,0,FT-Index                                   |
//+------------------------------------------------------------------+
// wizard description end
//+------------------------------------------------------------------+
//| Class CTrailingFT.                                               |
//| Appointment: Class traling stops with 'Fourier Transform' v3     |
//|               relative-sets concepts.                            |
//| Derives from class CExpertTrailing.                              |
//+------------------------------------------------------------------+
#define     __PI 245850922/78256779

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CTrailingFT : public CExpertTrailing
  {
protected:
   CFastFourierTransform   FFT;
   
   //--- adjusted parameters
   
   double            m_step;                    // trailing step

   int               m_index;                    // the epicycle index

public:
   //--- methods of setting adjustable parameters
   
   
   
   //--- method of verification of settings
   virtual bool      ValidationSettings(void);
   //--- method of creating the indicator and timeseries
   virtual bool      InitIndicators(CIndicators *indicators);
   //--- methods of checking if the market models are formed
   virtual bool      CheckTrailingStopLong(CPositionInfo *position,double &sl,double &tp);
   virtual bool      CheckTrailingStopShort(CPositionInfo *position,double &sl,double &tp);
   //---
                     CTrailingFT(void);
                    ~CTrailingFT(void);
   //--- methods of setting adjustable parameters
   void              Step(double value)                  { m_step=value;      }
   void              Index(int value)                    { m_index=value;     }

protected:
   
   double            ProcessFT(int Index);

  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CTrailingFT::CTrailingFT(void)
  {
//--- initialization of protected data
   m_used_series=USE_SERIES_TIME+USE_SERIES_SPREAD+USE_SERIES_OPEN+USE_SERIES_HIGH+USE_SERIES_LOW+USE_SERIES_CLOSE;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CTrailingFT::~CTrailingFT(void)
  {
  }
//+------------------------------------------------------------------+
//| Validation settings protected data.                              |
//+------------------------------------------------------------------+
bool CTrailingFT::ValidationSettings(void)
  {
//--- validation settings of additional filters
   if(!CExpertTrailing::ValidationSettings())
      return(false);
//--- initial data checks
   if(m_index<0 || m_index>=5)
     {
      printf(__FUNCTION__+": index must be greater than 0 and less than epicycles");
      return(false);
     }

//--- ok
   return(true);
  }
//+------------------------------------------------------------------+
//| Create indicators.                                               |
//+------------------------------------------------------------------+
bool CTrailingFT::InitIndicators(CIndicators *indicators)
  {
//--- check pointer
   if(indicators==NULL)
      return(false);
//--- initialization of indicators and timeseries of additional filters
   if(!CExpertTrailing::InitIndicators(indicators))
      return(false);
//--- 
//--- ok
   return(true);
  }
//+------------------------------------------------------------------+
//| Checking trailing stop and/or profit for long position.          |
//+------------------------------------------------------------------+
bool CTrailingFT::CheckTrailingStopLong(CPositionInfo *position,double &sl,double &tp)
  {
//--- check
      if(position==NULL)
         return(false);
      
      m_high.Refresh(-1);
      m_low.Refresh(-1);
      
      int _x=StartIndex();
      
      double _ft=ProcessFT(_x);
      double _type=_ft/100.0;
      
      double _atr=fmax(2.0*m_spread.GetData(_x)*m_symbol.Point(),m_high.GetData(_x)-m_low.GetData(_x))*(_type);
      
      double _sl=m_low.GetData(_x)-(m_step*_atr);
      
      double level =NormalizeDouble(m_symbol.Bid()-m_symbol.StopsLevel()*m_symbol.Point(),m_symbol.Digits());
      double new_sl=NormalizeDouble(_sl,m_symbol.Digits());
      double pos_sl=position.StopLoss();
      double base  =(pos_sl==0.0) ? position.PriceOpen() : pos_sl;
      
      sl=EMPTY_VALUE;
      tp=EMPTY_VALUE;
      if(new_sl>base && new_sl<level)
         sl=new_sl;
//---
   return(sl!=EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Checking trailing stop and/or profit for short position.         |
//+------------------------------------------------------------------+
bool CTrailingFT::CheckTrailingStopShort(CPositionInfo *position,double &sl,double &tp)
  {
//--- check
      if(position==NULL)
         return(false);
   
      m_high.Refresh(-1);
      m_low.Refresh(-1);
      
      int _x=StartIndex();
      
      double _ft=ProcessFT(_x);
      double _type=_ft/100.0;
   
      double _atr=fmax(2.0*m_spread.GetData(_x)*m_symbol.Point(),m_high.GetData(_x)-m_low.GetData(_x))*(_type);
      
      double _sl=m_high.GetData(_x)+(m_step*_atr);
      
      double level =NormalizeDouble(m_symbol.Ask()+m_symbol.StopsLevel()*m_symbol.Point(),m_symbol.Digits());
      double new_sl=NormalizeDouble(_sl,m_symbol.Digits());
      double pos_sl=position.StopLoss();
      double base  =(pos_sl==0.0) ? position.PriceOpen() : pos_sl;
      
      sl=EMPTY_VALUE;
      tp=EMPTY_VALUE;
      if(new_sl<base && new_sl>level)
         sl=new_sl;
//---
      return(sl!=EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Fourier Transform                                                |
//| INPUT PARAMETERS                                                 |
//|     Index   -   int, read index within price buffer.             |
//| OUTPUT                                                           |
//|     double  -   forecast change in price                         |
//+------------------------------------------------------------------+
double CTrailingFT::ProcessFT(int Index)
   {
      double _ft=0.0;
      
      int _index=Index;//+StartIndex();
      
      m_close.Refresh(-1);
      
      double _a[];
      matrix _output;
      al_complex _f[];
      
      //6 data points, 5 epicycles
   
      ArrayResize(_a,6);ArrayInitialize(_a,0.0);
      _output.Init(6,5);_output.Fill(0.0);
      
      for(int p=0;p<6;p++)
      {
         _a[p]=m_close.GetData(_index+p)-m_close.GetData(_index+p+1);
      }
      
      FFT.FFTR1D(_a,5,_f);
       
      for(int p=0;p<6;p++)
      {
         for(int s=0;s<5;s++)
         {
            double _divisor=(1.0/5),_angle=(p);_angle/=6;
            _output[p][s]=(_divisor*_a[p]*MathExp(-2.0*__PI*(_f[s].im/_f[s].re)*_angle));
         }
      }
      
      double _close=m_close.GetData(_index)>m_close.GetData(_index+1);
      
      _ft=(_output[5][m_index]/fmax(m_symbol.Point(),fabs(_output[5][m_index])+fabs(_close)))*100.0;
      
      return(_ft);
   }
//+------------------------------------------------------------------+


При компиляции со встроенным классом сигналов RSI и встроенным управлением капиталом с фиксированной маржой мы получаем следующие результаты для EURJPY за период с 2022.01.01 по 2023.01.01 на H4. При выполнении этого теста мы не устанавливаем целевую прибыль и не используем стоп-лосс по умолчанию, поэтому оба входных параметра для них равны нулю. Мы хотим, чтобы выходы полностью определялись разворотом сигнала или срабатыванием стоп-лосса, установленного нашим трейлинг-стопом.


r_1


Для второй/сравнительной реализации мы рассмотрим корреляцию составляющей частоты с наибольшей амплитудой с изменениями в ценовых диапазонах, как указано выше.

 

Эта реализация указана ниже:

//+------------------------------------------------------------------+
//|                                                   TrailingCT.mqh |
//|                   Copyright 2009-2013, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include <Math\Alglib\fasttransforms.mqh>
#include <Expert\ExpertTrailing.mqh>
// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Trailing Stop based on 'Fourier Transform' v3              |
//| Type=Trailing                                                    |
//| Name=CategoryTheory                                              |
//| ShortName=CT                                                     |
//| Class=CTrailingFT                                                |
//| Page=trailing_ct                                                 |
//| Parameter=Points,int,6,FT-Points                                 |
//| Parameter=Epicycles,int,5,FT-Epicycles                           | 
//| Parameter=Step,double,0.5,Trailing Step                          |
//+------------------------------------------------------------------+
// wizard description end
//+------------------------------------------------------------------+
//| Class CTrailingFT.                                               |
//| Appointment: Class traling stops with 'Fourier Transform' v3     |
//|               relative-sets concepts.                            |
//| Derives from class CExpertTrailing.                              |
//+------------------------------------------------------------------+
#define     __PI 245850922/78256779

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CTrailingFT : public CExpertTrailing
  {
protected:
   CFastFourierTransform   FFT;
   
   //--- adjusted parameters
   
   double            m_step;                    // trailing step

public:
   //--- methods of setting adjustable parameters
   
   
   
   //--- method of verification of settings
   virtual bool      ValidationSettings(void);
   //--- method of creating the indicator and timeseries
   virtual bool      InitIndicators(CIndicators *indicators);
   //--- methods of checking if the market models are formed
   virtual bool      CheckTrailingStopLong(CPositionInfo *position,double &sl,double &tp);
   virtual bool      CheckTrailingStopShort(CPositionInfo *position,double &sl,double &tp);
   //---
                     CTrailingFT(void);
                    ~CTrailingFT(void);
   //--- methods of setting adjustable parameters
   void              Step(double value)                  { m_step=value;      }

protected:
   
   double            ProcessFT(int Index);

  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CTrailingFT::CTrailingFT(void)
  {
//--- initialization of protected data
   m_used_series=USE_SERIES_TIME+USE_SERIES_SPREAD+USE_SERIES_OPEN+USE_SERIES_HIGH+USE_SERIES_LOW+USE_SERIES_CLOSE;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CTrailingFT::~CTrailingFT(void)
  {
  }
//+------------------------------------------------------------------+
//| Validation settings protected data.                              |
//+------------------------------------------------------------------+
bool CTrailingFT::ValidationSettings(void)
  {
//--- validation settings of additional filters
   if(!CExpertTrailing::ValidationSettings())
      return(false);
//--- initial data checks

//--- ok
   return(true);
  }
//+------------------------------------------------------------------+
//| Create indicators.                                               |
//+------------------------------------------------------------------+
bool CTrailingFT::InitIndicators(CIndicators *indicators)
  {
//--- check pointer
   if(indicators==NULL)
      return(false);
//--- initialization of indicators and timeseries of additional filters
   if(!CExpertTrailing::InitIndicators(indicators))
      return(false);
//--- 
//--- ok
   return(true);
  }
//+------------------------------------------------------------------+
//| Checking trailing stop and/or profit for long position.          |
//+------------------------------------------------------------------+
bool CTrailingFT::CheckTrailingStopLong(CPositionInfo *position,double &sl,double &tp)
  {
//--- check
      if(position==NULL)
         return(false);
      
      m_high.Refresh(-1);
      m_low.Refresh(-1);
      
      int _x=StartIndex();
      
      double _ft=ProcessFT(_x);
      double _type=_ft/100.0;
      
      double _atr=fmax(2.0*m_spread.GetData(_x)*m_symbol.Point(),m_high.GetData(_x)-m_low.GetData(_x))*(_type);
      
      double _sl=m_low.GetData(_x)-(m_step*_atr);
      
      double level =NormalizeDouble(m_symbol.Bid()-m_symbol.StopsLevel()*m_symbol.Point(),m_symbol.Digits());
      double new_sl=NormalizeDouble(_sl,m_symbol.Digits());
      double pos_sl=position.StopLoss();
      double base  =(pos_sl==0.0) ? position.PriceOpen() : pos_sl;
      
      sl=EMPTY_VALUE;
      tp=EMPTY_VALUE;
      if(new_sl>base && new_sl<level)
         sl=new_sl;
//---
   return(sl!=EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Checking trailing stop and/or profit for short position.         |
//+------------------------------------------------------------------+
bool CTrailingFT::CheckTrailingStopShort(CPositionInfo *position,double &sl,double &tp)
  {
//--- check
      if(position==NULL)
         return(false);
   
      m_high.Refresh(-1);
      m_low.Refresh(-1);
      
      int _x=StartIndex();
      
      double _ft=ProcessFT(_x);
      double _type=_ft/100.0;
   
      double _atr=fmax(2.0*m_spread.GetData(_x)*m_symbol.Point(),m_high.GetData(_x)-m_low.GetData(_x))*(_type);
      
      double _sl=m_high.GetData(_x)+(m_step*_atr);
      
      double level =NormalizeDouble(m_symbol.Ask()+m_symbol.StopsLevel()*m_symbol.Point(),m_symbol.Digits());
      double new_sl=NormalizeDouble(_sl,m_symbol.Digits());
      double pos_sl=position.StopLoss();
      double base  =(pos_sl==0.0) ? position.PriceOpen() : pos_sl;
      
      sl=EMPTY_VALUE;
      tp=EMPTY_VALUE;
      if(new_sl<base && new_sl>level)
         sl=new_sl;
//---
      return(sl!=EMPTY_VALUE);
  }
//+------------------------------------------------------------------+
//| Fourier Transform                                                |
//| INPUT PARAMETERS                                                 |
//|     Index   -   int, read index within price buffer.             |
//| OUTPUT                                                           |
//|     double  -   forecast change in price                         |
//+------------------------------------------------------------------+
double CTrailingFT::ProcessFT(int Index)
   {
      double _ft=0.0;
      
      int _index=Index;//+StartIndex();
      
      m_close.Refresh(-1);
      
      double _a[];
      matrix _output;
      al_complex _f[];
      
      //6 data points, 5 epicycles
   
      ArrayResize(_a,6);ArrayInitialize(_a,0.0);
      _output.Init(6,5);_output.Fill(0.0);
      
      for(int p=0;p<6;p++)
      {
         _a[p]=m_close.GetData(_index+p)-m_close.GetData(_index+p+1);
      }
      
      FFT.FFTR1D(_a,5,_f);
       
      for(int p=0;p<6;p++)
      {
         for(int s=0;s<5;s++)
         {
            double _divisor=(1.0/5),_angle=(p);_angle/=6;
            _output[p][s]=(_divisor*_a[p]*MathExp(-2.0*__PI*(_f[s].im/_f[s].re)*_angle));
         }
      }
      
      double _close=m_close.GetData(_index)>m_close.GetData(_index+1);
      
      int _max_index=0;
      double _max=fabs(_output[5][_max_index]);
      for(int s=0;s<5;s++)
      {
         if(_max<fabs(_output[5][s]))
         {
            _max_index=s;
            _max=fabs(_output[5][s]);
         }
      }
      
      _ft=(_output[5][_max_index]/fmax(m_symbol.Point(),fabs(_output[5][_max_index])+fabs(_close)))*100.0;
      
      return(_ft);
   }
//+------------------------------------------------------------------+


При компиляции с теми же файлами сигналов и управления капиталом выше тестовый прогон дает следующие результаты:


r_2


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


r_3


Из наших трех отчетов видно, что небольшое изменение нашей системы трейлинг-стопа с идентичными входными сигналами существенно отражается на результатах.


Заключение

Мы рассмотрели, что такое преобразование Фурье с точки зрения неспециалиста. Также мы описали некоторые из обширных возможных применений этого преобразования для трейдеров. Затем мы продемонстрировали несколько простых реализаций этого преобразования на MQL5, сосредоточившись на использовании прогнозов волатильности для управления закрытием открытых позиций по стоп-лоссу. У этого преобразования есть много других приложений, которые я не перечислил здесь. Вы можете изучить их самостоятельно. Кроме того, приведенный здесь код не является граалем и его нельзя рассматривать в качестве инструмента, готового к работе на реальном счете в виде советника. Будьте осмотрительны и в первую очередь определите, что подходит именно вам.

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/12599

Прикрепленные файлы |
TrailingFT_3_.mqh (8.72 KB)
TrailingFT_2_.mqh (8.72 KB)
TrailingFT_1_.mqh (8.84 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Alexey Volchanskiy
Alexey Volchanskiy | 21 июл. 2023 в 09:11

Я так понял, тут автор даже не стал изучать быстрое преобразование Фурье :) И то верно, чего заморачиваться, FT и FFT это пустышки и в трейдинге ничего не дают. Я эти преобразования еще лет 15 назад в Матлабе крутил и так и сяк, и спектры резал, потом делал обратное преобразование во временную форму - полный пшик. FFT хороши в других областях, например радиолокации.

А вот вейвлеты гораздо интереснее. Если вырезать все высокочастотные полосы и сделать обратное преобразование во временную область, получим отличный НЧ фильтр без задержек. Впрочем, это можно сделать гораздо проще — через 2-х проходные фильтры..

Нейросети — это просто (Часть 47): Непрерывное пространство действий Нейросети — это просто (Часть 47): Непрерывное пространство действий
В данной статье мы расширяем спектр задач нашего агента. В процесс обучения будут включены некоторые аспекты мани- и риск-менеджмента, которые являются неотъемлемой частью любой торговой стратегии.
Реализация алгоритма обучения ARIMA на MQL5 Реализация алгоритма обучения ARIMA на MQL5
В этой статье мы реализуем алгоритм, который применяет интегрированную модель авторегрессии скользящей средней (модель Бокса-Дженкинса) с использованием метода минимизации функции Пауэллса. Бокс и Дженкинс утверждали, что большинство временных рядов можно смоделировать с помощью одной или обеих из двух структур.
DoEasy. Элементы управления (Часть 32):  горизонтальный "ScrollBar", прокрутка колесиком мышки DoEasy. Элементы управления (Часть 32): горизонтальный "ScrollBar", прокрутка колесиком мышки
В статье завершим разработку функционала объекта-горизонтальной полосы прокрутки. Сделаем возможность прокрутки содержимого контейнера перемещением ползунка полосы прокрутки и вращением колёсика мышки. Также внесём дополнения в библиотеку с учётом появившейся в терминале новой политики исполнения ордеров и новых кодов ошибок времени выполнения в MQL5.
Разработка системы репликации - Моделирование рынка (Часть 02): Первые эксперименты (II) Разработка системы репликации - Моделирование рынка (Часть 02): Первые эксперименты (II)
В этот раз попробуем другой подход для достижения цели в 1 минуту. Однако эта задача не так проста, как можно подумать.