Основы создания хеджирующего эксперта

chayutra sriboonruang | 13 июля, 2007

Введение

Я собираюсь изложить основную идею создания простого хеджирующего эксперта. Вот основные данные о хеджирующем советнике:

  • Хеджирование (англ. hedge) (финансовый термин) (Из англоязычной Википедии - Wikipedia, the free encyclopedia - Перевод MetaQuotes Software Corp.)

    (Перенаправлено с Hedging)

    В области финансов, хедж (или хеджирование) - это инвестиция, которая совершается специально с целью снижения или устранения риска в рамках другой инвестиции. Хеджирование - это стратегия, разработанная для минимизации подверженности нежелательному коммерческому риску, но в то же время позволяющая получить прибыль от инвестиционной деятельности. В типичном случае хеджер может вложить средства в финансовый инструмент, который, по его мнению, оценивается ниже своей "справедливой стоимости" (например, ипотечная ссуда, которую он затем берёт), и сочетает это с короткой продажей соответствующего финансового инструмента или инструментов. Таким образом, хеджер может не беспокоиться, растёт или падает цена на рынке в целом, а лишь о том, повышается ли стоимость недооценённого финансового инструмента по отношению к хеджу. Холбрук Воркинг, пионер теории хеджирования, назвал эту стратегию "размышления на основе," [1], где основой является разница между теоретическим значением хеджирования и его реальным значением (или между ценами спота и фьючерсов во времена Воркинга).

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

  • Всё, что нам требуется от сервера, должно вызываться функцией MarketInfo(символ string, тип int). Данная функция позволяет нам вызывать не только данные из текущего окна графика, но и любые другие данные по символам, присутствующим в MarketWatch. Эти данные помогут нам выставить ордера для любого символа, а не только для того, из окна которого эксперт работает в данный момент. И всё это с легкостью позволит нам хеджировать 2 символа. Слава Богу и разработчикам MT4, это очень полезная возможность.

  • Очень важной для хеджинга является корреляция между двумя рассматриваемыми символами, которую можно установить при помощи небольших функций, описанных ниже.

    Корреляция - в финансовом смысле - это статистическое измерение отношения между двумя финансовыми инструментами. Коэффициент корреляции находится в диапазоне между -1 и +1. Корреляция +1 предполагает, что две пары валют движутся в одинаковом направлении 100% времени. Корреляция -1 означает, что две пары валют 100% времени движутся в противоположных направлениях. Нулевая корреляция подразумевает, что отношение между валютными парами совершенно случайно. подробнее


Всё вышеизложенное - несложные понятия, которые должны быть известны хеджерам на рынке Forex для использования хеджирующего эксперта в MT4. Теперь можно приступить к его созданию.



Кодируем хеджирующий эксперт шаг за шагом

Шаг 1: Входные параметры


До того как начать создание хеджирующего эксперта, мы должны выбрать 2 коррелирующих символа, т.е.:

В данной статье я выбрала свою любимую хедж-пару: EURJPY и GBPJPY. Её символы всегда двигаются в одинаковом направлении, что упрощает установку хеджирующего ордера. Теперь давайте начнём. Для создания хеджирующего эксперта нужно ознакомиться с входными переменными, описанными ниже.

// это нужно для блокировки функции отправки ордера (sending function), но 
// не для блокировки функции close.
extern bool BlockOpening = false; 
 
extern string BaseSymbol = "EURJPY";//первый символ 
 
extern string H_Symbol = "GBPJPY";//второй символ 
 
extern bool MoveSameWay = true;//двигаются они в одном направлении или нет 
 
extern int CorPeriod = 5;//предпочитаемый период корреляции 
 
extern double BaseLotSize = 1.5;//размер лота первого символа 
 
extern double H_LotsSize = 1.0;//размер лота второго символа 
 
extern double ExpectProfit$ = 137;//ожидаемая прибыль в валюте USD 
//допустимый проигрыш в USD в случае возникновения ошибки 
extern double AcceptableLoss$ = -77; 
 
extern string ExpectCor = "______";//ожидаемая корреляция для хеджирования 
//это большее значение ожидаемой корреляции (expect cor), оно должно быть больше, чем "And" 
extern double Between = 1.05; 
 
extern double And = 0.9;//это более низкий уровень expect cor 
 
extern string MISC = "______";//немного больше 
 
extern int MagicNo = 318;//предпочитаемjt магическое число (magic number)
 
extern bool ShowStatus = true;//показать актуальное состояние хеджирования 
//проиграть звук, когда выполнились функции SendH и CloseH 
extern bool PlayAudio = false;
Шаг 2: Объявление переменных


Ниже представлены переменные, используемые в нашем эксперте, и я хочу дать пояснения по переменным, необходимые для понимания работы эксперта.

int BSP       // спред базового символа 
 
    , HSP      // спред хеджирующего символа 
 
    , gsp 
 
    , BOP = -1 // тип ордера базового символа 
 
    , HOP = -1 // тип ордера хеджирующего символа 
 
    , up = 0 
 
    , Hcnt = 0 
 
    , u = 0 
 
    , d = 0 
 
    , day = 0 
 
    , sent=0 
 
    , firstopen 
 
    , expire; 
 
double Lot 
 
       , BaseOpen // цена открытия ордера базового символа 
 
       , HOpen    // цена открытия ордера хеджирующего символа 
 
       , BPt      // значение пункта для базового символа 
 
       , HPt      // значение пункта для хеджирующего символа 
 
       , BSwapL   // значение свопа для покупки базового символа 
 
       , BSwapS   // значение свопа для продажи базового символа 
 
       , HSwapL   // значение свопа для покупки хеджированного символа 
 
       , HSwapS;  // значение свопа для продажи хеджированного символа 
 
 
bool SResult = false, BResult = false, H1.profitswap, 
     H2.profitswap, H3.profitswap; 
 
bool SwapMode = true, allmeetcor = false, BlockOpen = false, 
     buy,sell,cleared = false; 
 
string H1.string = "", H2.string = "", H3.string = "", 
       OrdComment = "", candletxt,tdstxt = "";
Шаг 3: Получение всех необходимых статических параметров


Теперь зададим некоторые статические значения, которые будут объявлены в части init().

//+------------------------------------------------------------------+ 
//| функция инициализации эксперта                                   | 
//+------------------------------------------------------------------+ 
 
int init() 
  { 
    //---- 
    BSP = MarketInfo(BaseSymbol,MODE_SPREAD); 
    HSP = MarketInfo(H_Symbol ,MODE_SPREAD); 
    BPt = MarketInfo(BaseSymbol,MODE_POINT); 
    HPt = MarketInfo(H_Symbol ,MODE_POINT); 
    BSwapL = MarketInfo(BaseSymbol,MODE_SWAPLONG); 
    BSwapS = MarketInfo(BaseSymbol,MODE_SWAPSHORT);
    HSwapL = MarketInfo(H_Symbol,MODE_SWAPLONG); 
    HSwapS = MarketInfo(H_Symbol,MODE_SWAPSHORT); 
//---- 
    return(0); 
  }
Шаг 4: Полезные функции


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


1. Корреляционная функция

Начнём с функций расчёта корреляции. Ниже приведены функции, которые применял разработчик выложенного в свободный доступ корреляционного индикатора (igorad2004@list.ru) и модифицированного нами для более лёгкого использования в данном эксперте. Так что нам больше не придётся вызывать коэффициент корреляции из внешнего индикатора. Неплохо?

//+------------------------------------------------------------------+ 
//|  КОРРЕЛЯЦИЯ                                                      |
//+------------------------------------------------------------------+ 
double symboldif(string symbol, int shift) 
  { 
    return(iClose(symbol, 1440, shift) - 
           iMA(symbol, 1440, CorPeriod, 0, MODE_SMA, 
               PRICE_CLOSE, shift)); 
  } 
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double powdif(double val) 
  { 
    return(MathPow(val, 2)); 
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+ 
double u(double val1,double val2) 
  { 
    return((val1*val2)); 
  }
//+------------------------------------------------------------------+
//|  Вот настоящая корреляционная функция, которую нужно вызвать     |
//+------------------------------------------------------------------+
double Cor(string base, string hedge) 
  { 
    double u1=0,l1=0,s1=0; 
    for(int i = CorPeriod - 1; i >= 0; i--) 
      { 
        u1 += u(symboldif(base, i), symboldif(hedge, i)); 
        l1 += powdif(symboldif(base, i)); 
        s1 += powdif(symboldif(hedge, i)); 
      } 
    if(l1*s1 > 0) 
        return(u1 / MathSqrt(l1*s1)); 
  } 
//+------------------------------------------------------------------+

Переменная CorPeriod является внешней и входной переменной, что позволяет нам настроить её. Когда вам понадобится рассчитать корреляцию между двумя символами, просто вызовите функцию Cor(string base,string hedge), например, так: Cor(EURJPY,GBPJPY). Согласитесь, это несложно.


2. Функция отправки хеджа

На мой взгляд, управлять отправкой хеджа проще путём создания функции SendH, описанной ниже.

//+------------------------------------------------------------------+
//| ОТПРАВКА ХЕДЖА                                                   |
//+------------------------------------------------------------------+
bool SendH(string symbol, int op, double lots,
           double price, int sp, string comment, int magic) 
  { 
    if(OrderSend(symbol 
                 , op 
                 , lots 
                 , price 
                 , sp 
                 , 0 
                 , 0 
                 , comment 
                 , magic 
                 , 0 
                 , CLR_NONE) 
                 > 0) 
      {
        return(true); 
        if(PlayAudio)
            PlaySound("expert.wav"); 
      } 
    else 
      {
        Print(symbol, ": ", magic, " : " 
              , ErrorDescription(GetLastError())); 
        return(false); 
      } 
  } 
//+------------------------------------------------------------------+

Здесь вы можете более подробно ознакомиться с функцией OrderSend.

Функция ErrorDescription(GetLastError()), приведённая выше, заставляет наш эксперт сообщать нам, какая ошибка имела место во время работы торговой функции. Чтобы воспользоваться функцией описания ошибки, необходимо включить файл "stdlib.mqh", поместив следующий код:

//+------------------------------------------------------------------+ 
//|                                                     MyHedge.mq4  | 
//|                                                         myHedge  | 
//|                                     http://dailyh.blogspot.com/  | 
//+------------------------------------------------------------------+ 
#property copyright "myHedge" 
#property link "http://dailyh.blogspot.com/" 
#include <stdlib.mqh>
//+------------------------------------------------------------------+

Чтобы воспользоваться ею, просто вызовите функцию "ErrorDescription()", как показано выше.


3. Функция закрытия хеджа

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

//+------------------------------------------------------------------+
//|  ЗАКРЫТИЕ ХЕДЖА                                                  |
//+------------------------------------------------------------------+
bool CloseHedge(int magic) 
  { 
   for(int i = OrdersTotal() - 1; i >= 0; i--) 
     { 
       if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && 
                      OrderMagicNumber() == magic) 
         { 
           if(OrderClose(OrderTicket() 
              , OrderLots() 
              , OrderClosePrice() 
              , MarketInfo(OrderSymbol(), MODE_SPREAD) 
              , CLR_NONE))
                SResult = true; 
         } 
     } 
   if(SResult)
     {
       return(true);
       if(PlayAudio)
         {
           PlaySound("ok.wav");
         }
     } 
   else 
       Print("CloseHedge Error: ", ErrorDescription(GetLastError())); 
   RefreshRates(); 
   // return(0); 
  } 
//+------------------------------------------------------------------+

Эта функция закроет только ордера с одинаковым магическим числом (magic number), то есть она не будет влиять на хеджирующие ордера с другими магическими числами. Так что беспокоиться на самом деле не о чем. Перед использованием функции закрытия нам нужно определить, "сколько у нас теперь есть" при помощи функции, описанной ниже.


4. Функция нахождения общей прибыли
//+------------------------------------------------------------------+
//|  ОБЩАЯ ПРИБЫЛЬ (TOTAL PROFIT)                                    |
//+------------------------------------------------------------------+ 
double TotalCurProfit(int magic) 
  { 
   double MyCurrentProfit = 0; 
   for(int cnt = 0 ; cnt < OrdersTotal() ; cnt++) 
     { 
       OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES); 
       if(OrderMagicNumber() == magic) 
         { 
           MyCurrentProfit += (OrderProfit() + OrderSwap()); 
         } 
     } 
   return(MyCurrentProfit); 
  } 
//+------------------------------------------------------------------+

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

if(TotalCurProfit(318) > 100) 
    CloseHedge(318);

Все значения прибыли рассчитываются в долларах США. Как следует из вышеуказанной строки, если общая прибыль ордеров с магическим номером 318 превышает $100, они будут закрыты. Для открытия хеджирующего ордера нам нужно знать, что нет никакого другого ордера, установленного по тому же символу и с тем же магическим числом, в тот момент, когда мы хотим отправить хедж. Это можно определить при помощи данной функции.



5. Получение количества существующих позиций
//+------------------------------------------------------------------+
//| СУЩЕСТВУЮЩИЕ ПОЗИЦИИ (EXISTING POSITIONS)                        |
//+------------------------------------------------------------------+
int ExistPositions(string symbol, int magic) 
  { 
    int NumPos = 0; 
    for(int i = 0; i < OrdersTotal(); i++) 
      { 
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) 
                       && OrderSymbol() == symbol 
                       && OrderMagicNumber() == magic) 
          {  
            NumPos++; 
          } 
      }
    return(NumPos); 
 
  } 
//+------------------------------------------------------------------+ 

Эту функцию можно использовать следующим образом:

ExistPositions("GBPJPY",318)

Данная функция вернёт нам, "сколько существующих ордеров по GBPJPY с магическим номером 318 активны" в данный момент. Это ещё одна функция для определения типа выставленного ордера.


6. Определение типа ордера конкретной существующей позиции
//+------------------------------------------------------------------+  
//| СУЩЕСТВУЮЩАЯ ОТКРЫТАЯ ПОЗИЦИЯ                                    |
//+------------------------------------------------------------------+
int ExistOP(string symbol, int magic) 
  { 
    int NumPos = -1; 
    for(int i = 0; i < OrdersTotal(); i++) 
      { 
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) 
                       && OrderSymbol() == symbol 
                       && OrderMagicNumber() == magic) 
          {  
            NumPos = OrderType();
          } 
      } 
    return(NumPos); 
  } 
//+------------------------------------------------------------------+

Данная функция возвращает целое значение типа ордера для указанного символа и заданного магического числа, которые действуют в настоящий момент. Если установленный ордер по GBPJPY является OP_BUY, возвращаемое значение будет "0". Данная функция не только работает вместе с торговой функцией. Она также работает с функцией, показывающей текущий статус хеджирования. This function is called "OP2Str".



7. Показать состояние торговли

//+------------------------------------------------------------------+
//| Преобразовать значение OP в строку                               |
//+------------------------------------------------------------------+ 
string OP2Str(int op) 
  { 
    switch(op) 
      { 
        case OP_BUY : return("BUY"); 
        case OP_SELL: return("SELL"); 
        default : return("~~"); 
      } 
  }
//+------------------------------------------------------------------+

Думаю, особых объяснений не требуется - в коде видно, как это работает.


8. Закрыть все ордера определённого типа

Ещё одна функция для прямого закрытия отдельного ордера в случае ошибки при отправке или закрытии хеджа.

//+------------------------------------------------------------------+
//| ЗАКРЫТЬ ВСЕ ОРДЕРА ОПРЕДЕЛЁННОГО ТИПА (CLOSE SCRAP)              |
//+------------------------------------------------------------------+ 
bool CloseScrap(string sym, int op, int magic) 
  { 
    for(int i = OrdersTotal() - 1; i >= 0; i--) 
      { 
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) 
           && amp; OrderMagicNumber() == magic 
           && OrderSymbol() == sym 
           && OrderType() == op) 
          { 
            if(OrderClose(OrderTicket() 
               , OrderLots() 
               , OrderClosePrice() 
               , MarketInfo(OrderSymbol(), MODE_SPREAD) 
               , CLR_NONE))
                BResult = true; 
          } 
      } 
    if(BResult)
      {
        return(true);
        if(PlayAudio)
          {
            PlaySound("ok.wav");
          }
      } 
    else 
        Print("CloseScrap Error: ", ErrorDescription(GetLastError())); 
    RefreshRates(); 
    // return(0); 
  }

то есть функция CloseScrap("GBPJPY",OP_BUY,318) закроет только установленные длинные позиции по "GBPJPY" с магическим числом 318. Всё просто. Осталось познакомиться с ещё одной функцией.



9. Показать желаемый логический (Boolean) статус
//+------------------------------------------------------------------+
//| Преобразовать bool в string                                      |
//+------------------------------------------------------------------+
string bool2str( bool boolval) 
  { 
    if(boolval == true) 
        return("Yes"); 
    if(boolval == false)
        return("No"); 
  }
//+------------------------------------------------------------------+

Ничего особенного, эта функция просто показывает логический статус некоторых параметров, например, значение BlockOpening. Если установить её на true, эта функция вернёт "Yes". Соответственно, будучи установленной на false, она вернёт "No".

Вот и всё о функциях, которые нам понадобятся. А теперь отдадим должное написанию кода процесса хеджирования.


Шаг 5: Ядро кода эксперта

Начнём со следующего:

//+------------------------------------------------------------------+ 
//| функция start эксперта                                           | 
//+------------------------------------------------------------------+ 
int start() 
  {
Потом зададим диапазон корреляции.
if(Cor(BaseSymbol, H_Symbol) > Between || 
   Cor(BaseSymbol, H_Symbol) < And) 
// Снятие блока, когда корреляция выходит за пределы
// предполагаемого диапазона. 
    BlockOpen = true; 
else 
    BlockOpen = false;

Затем определим направление хеджирования (это только пример). В данной статье я выбрала стиль торговли по значению свопа, а затем я буду торговать только путём накапливания свопов.

// если они движутся в одном направлении, мы откроем длинную и короткую позиции 
if(MoveSameWay) 
  { 
    if(((BSwapL*BaseLotSize) + (HSwapS*H_LotSize)) > 0) 
      {  
        BOP = OP_BUY; 
        HOP = OP_SELL; 
      } 
    else 
        if(((BSwapS*BaseLotSize) + (HSwapL*H_LotSize)) > 0) 
          { 
            BOP = OP_SELL; 
            HOP = OP_BUY; 
          } 
  } // конец MoveSameWay
// если они движутся в разных направлениях, мы откроем две коротких или две длинных позиции
else 
  { 
    if(((BSwapL*BaseLotSize) + (HSwapL*H_LotSize)) > 0) 
      { 
        BOP = OP_BUY; 
        HOP = OP_BUY; 
      } 
    else 
        if(((BSwapS*BaseLotSize) + (HSwapS*H_LotSize)) > 0) 
          { 
            BOP = OP_SELL; 
            HOP = OP_SELL; 
          } 
  }

Теперь установим хедж:

// если они входят в диапазон корреляции и 
// вы их не блокируете
if(!BlockOpen && !BlockOpening)  
  { 
    if(BOP == OP_BUY) 
    // определим цену открытия    
        BaseOpen = MarketInfo(BaseSymbol, MODE_ASK); 
    else 
        BaseOpen = MarketInfo(BaseSymbol, MODE_BID); 
    if(HOP == OP_BUY)
        HOpen = MarketInfo(H_Symbol, MODE_ASK); 
    else 
        HOpen = MarketInfo(H_Symbol, MODE_BID); 
    // В случае отсутствия свопа для накопления 
    // из BOP и HOP будет -1.
    if(BOP >= 0 && HOP >= 0) 
      {
        if(ExistPositions(BaseSymbol, MagicNo) == 0 && 
           ExistPositions(H_Symbol, MagicNo) == 0) 
          { 
            SendH(BaseSymbol, BOP, BaseLotSize, BaseOpen, 
                  BSP, "COR : " +
                  DoubleToStr(Cor(BaseSymbol, H_Symbol), 2), 
                  MagicNo); 
            SendH(H_Symbol, HOP, H_LotsSize, HOpen, HSP, 
                  "COR : " +
                  DoubleToStr(Cor(BaseSymbol, H_Symbol), 2), 
                  MagicNo); 
          } 
        else // в случае неудачного пинга или реквотирования 
          { 
            if(ExistPositions(BaseSymbol, MagicNo) == 1&&
               TotalCurProfit(MagicNo)>AcceptableLoss$) 
              { 
                CloseScrap(BaseSymbol, ExistOP(BaseSymbol, 
                           MagicNo), MagicNo); 
              } 
            else 
                if(ExistPositions(H_Symbol, MagicNo) == 1&&
                   TotalCurProfit(MagicNo) > AcceptableLoss$) 
                  { 
                    CloseScrap(H_Symbol, ExistOP(H_Symbol, 
                               MagicNo), MagicNo); 
                  } 
          } 
 
      }
    else // если BOP или HOP меньше 0
      {
        string swaptxt = "Отсутствует своп для накопления:" + 
                   "пожалуйста, измените один или несколько входных параметров.";
      }
  }


Затем закроем их по достижении ожидаемой прибыли:

    if((TotalCurProfit(MagicNo) > ExpectProfit$)
      {
        CloseHedge(MagicNo);
      }

Теперь рассмотрим нечто более интересное - ShowStatus.

    if(ShowStatus)
      {
        Comment("\nCorrel: " + DoubleToStr(Cor(BaseSymbol
                , H_Symbol), 2)
                , "\nBlockOpen : " + bool2str(BlockOpen 
                || BlockOpening)
                , "\n" + swaptxt
                , "\n~~~~~~~"
                , "\nB/H [sp] : " + BaseSymbol + " [" 
                + BSP + "]" + " / " 
                + H_Symbol+" ["+HSP+"]"
                , "\nCurOp [Lots]: " 
                + OP2Str(ExistOP(BaseSymbol, MagicNo)) 
                + " [" + DoubleToStr(BaseLotSize, 2) + "]"
                + " ~ " + OP2Str(ExistOP(H_Symbol, MagicNo)) 
                + " [" 
                + DoubleToStr(H_LotsRatio*BaseLotSize, 2) + "]"
                , "\nCurPF [Expect]: $" 
                + DoubleToStr(TotalCurProfit(MagicNo), 2) 
                + " [$"+DoubleToStr(ExpectProfit$, 2) + "]");
      }
    else 
        Comment("");

Завершим стандартным для каждого эксперта блоком:

 return(0);
}

Шаг 6: Собираем весь код

Ниже вы можете видеть, как выглядит myHedge.mq4.

//+------------------------------------------------------------------+
//|                                                      MyHedge.mq4 |
//|                                                          myHedge |
//|                                      http://dailyh.blogspot.com/ |
//+------------------------------------------------------------------+
#property copyright "myHedge"
#property link "http://dailyh.blogspot.com/"
//----
#include <stdlib.mqh>
// это - для блокировки функции отправки ордера, но не для блокировки 
// функции закрытия.
extern bool BlockOpening = false;
extern string BaseSymbol = "EURJPY"; // первый символ
extern string H_Symbol = "GBPJPY";   // второй символ
extern bool MoveSameWay = true; // движутся они в одном направлении или нет
extern int CorPeriod = 5; // желаемый период корреляции
extern double BaseLotSize = 1.5; // размер лота первого символа
extern double H_LotSize = 1.0; // размер лота второго символа
extern double ExpectProfit$ = 137; // ожидаемая прибыль в долларах США
// приемлемый проигрыш в долларах США на случай возникновения ошибки
extern double AcceptableLoss$ = -77; 
extern string ExpectCor = "______"; // ожидаемая корреляция, для 
// хеджа это наибольший ожидаемый коэффициент корреляции, который должен быть  
// tбольше, чем "And"
extern double Between = 1.05;
extern double And = 0.9; // это - наименьший ожидаемый коэффициент корреляции
extern string MISC = "______"; // кое-что ещё
extern int MagicNo = 318; // желаемое магическое число
extern bool ShowStatus = true; // показать существующее состояние хеджирования
// проиграть звук, когда функции SendH и CloseH выполнены
extern bool PlayAudio = false; 
//----
int BSP  // спред базового символа
    ,HSP // спред хеджирующего символа
    ,gsp
    ,BOP = -1 // тип ордера базового символа
    ,HOP = -1 // тип ордера хеджирующего символа
    ,up = 0
    ,Hcnt = 0
    ,u = 0
    ,d = 0
    ,day = 0
    ,sent = 0
    ,firstopen
    ,expire;
double Lot
       ,BaseOpen // цена открытия ордера базового символа
       ,HOpen // цена открытия ордера хеджирующего символа
       ,BPt // значение пункта базового символа
       ,HPt // значение пункта хеджирующего символа
       ,BSwapL // значение свопа длинной позиции по базовому символу
       ,BSwapS // значение свопа короткой позиции по базовому символу
       ,HSwapL // значение свопа длинной позиции по хеджирующему символу
       ,HSwapS; // значение свопа короткой позиции по хеджирующему символу
bool SResult = false, BResult = false, H1.profitswap, H2.profitswap, 
     H3.profitswap;
bool SwapMode = true, allmeetcor = false, BlockOpen = false, buy, 
     sell, cleared = false;
string H1.string = "", H2.string = "", H3.string = "", 
       OrdComment = "", candletxt,tdstxt = "";
//+------------------------------------------------------------------+
//| функция инициализации эксперта                                   |
//+------------------------------------------------------------------+
int init()
  {
   BSP = MarketInfo(BaseSymbol, MODE_SPREAD);
   HSP = MarketInfo(H_Symbol, MODE_SPREAD);
//----
   BPt = MarketInfo(BaseSymbol, MODE_POINT);
   HPt = MarketInfo(H_Symbol, MODE_POINT);
//----
   BSwapL = MarketInfo(BaseSymbol, MODE_SWAPLONG);
   BSwapS = MarketInfo(BaseSymbol, MODE_SWAPSHORT);
//----
   HSwapL = MarketInfo(H_Symbol, MODE_SWAPLONG);
   HSwapS = MarketInfo(H_Symbol, MODE_SWAPSHORT);
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| функция start эксперта                                           |
//+------------------------------------------------------------------+
int start()
  {
   if(Cor(BaseSymbol, H_Symbol) > Between || 
      Cor(BaseSymbol, H_Symbol) < And)
   // Блокировать, если корреляция выходит за пределы ожидаемого диапазона.
       BlockOpen = true;
   else 
       BlockOpen = false;
//----
   if(MoveSameWay)
     {
       if((BSwapL*BaseLotSize) + (HSwapS*H_LotSize) > 0)
         {
           BOP = OP_BUY;
           HOP = OP_SELL;
         }
       else 
           if((BSwapS*BaseLotSize) + (HSwapL*H_LotSize) > 0)
             {
               BOP = OP_SELL;
               HOP = OP_BUY;
             }
     }
   else
     {
       if((BSwapL*BaseLotSize) + (HSwapL*H_LotSize) > 0)
         {
           BOP = OP_BUY;
           HOP = OP_BUY;
         }
       else 
           if((BSwapS*BaseLotSize) + (HSwapS*H_LotSize) > 0)
             {
               BOP = OP_SELL;
               HOP = OP_SELL;
             }
     }
   if(!BlockOpen && !BlockOpening)
     {
       if(BOP == OP_BUY) 
           BaseOpen = MarketInfo(BaseSymbol, MODE_ASK);
       else            
           BaseOpen = MarketInfo(BaseSymbol, MODE_BID);
       if(HOP == OP_BUY)
           HOpen = MarketInfo(H_Symbol, MODE_ASK);
       else
           HOpen = MarketInfo(H_Symbol, MODE_BID);
       // В случае отсутствия свопа, который мы могли бы накапливать.
       if(BOP >= 0 && HOP >= 0) 
         {
           if(ExistPositions(BaseSymbol, MagicNo) == 0 && 
              ExistPositions(H_Symbol,MagicNo) == 0)
             {
               SendH(BaseSymbol, BOP, BaseLotSize, BaseOpen, BSP, 
                     "COR : " + DoubleToStr(Cor(BaseSymbol, H_Symbol), 
                     2), MagicNo);
               SendH(H_Symbol, HOP, H_LotSize, HOpen, HSP, "COR : " + 
                     DoubleToStr(Cor(BaseSymbol, H_Symbol), 2), MagicNo);
             }     
           else // в случае неудачного пинга или реквотирования
             {
               if(ExistPositions(BaseSymbol, MagicNo) == 1 && 
                  TotalCurProfit(MagicNo) > AcceptableLoss$)
                 {
                   CloseScrap(BaseSymbol, ExistOP(BaseSymbol, 
                              MagicNo), MagicNo);
                 }
               else 
                   if(ExistPositions(H_Symbol, MagicNo) == 1 && 
                      TotalCurProfit(MagicNo) > AcceptableLoss$)
                     {
                       CloseScrap(H_Symbol, ExistOP(H_Symbol, 
                                  MagicNo), MagicNo);
                     }
             }
         }
       else
         {
           string swaptxt = "Отсутствует своп для накопления: пожалуйста," + 
                            "измените один или несколько параметров.";
         }
     }
   if(TotalCurProfit(MagicNo) > ExpectProfit$)
     {
       CloseHedge(MagicNo);
     }
   if(ShowStatus)
     {
       Comment("\nКорреляция: "+DoubleToStr(Cor(BaseSymbol, H_Symbol), 2)
               , "\nBlockOpen : " + bool2str(BlockOpen || BlockOpening)
               , "\n" + swaptxt
               , "\n~~~~~~~"
               , "\nB/H [sp] : " + BaseSymbol + " [" + BSP + "]" + 
                 " / " + H_Symbol + " [" + HSP + "]"
               , "\nCurOp [лоты]: " + OP2Str(ExistOP(BaseSymbol, 
                 MagicNo)) + " [" + DoubleToStr(BaseLotSize, 2) + "]"
                 + " ~ " + OP2Str(ExistOP(H_Symbol, MagicNo)) + " [" + 
                 DoubleToStr(H_LotSize, 2) + "]"
               , "\nCurPF [Расчётный]: $" + 
                 DoubleToStr(TotalCurProfit(MagicNo), 2) + " [$" + 
                 DoubleToStr(ExpectProfit$, 2) + "]");
     }
   else 
       Comment("");
   return(0);
  }
//+------------------------------------------------------------------+
//| КОРРЕЛЯЦИЯ                                                      |
//+------------------------------------------------------------------+
double symboldif(string symbol, int shift)
  {
   return(iClose(symbol, 1440, shift) - 
          iMA(symbol, 1440, CorPeriod, 0, MODE_SMA, PRICE_CLOSE, shift));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double powdif(double val)
  {
   return(MathPow(val, 2));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double u(double val1, double val2)
  {
   return((val1*val2));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double Cor(string base, string hedge)
  {  
   double u1 = 0, l1 = 0, s1 = 0;
   for(int i = CorPeriod - 1; i >= 0; i--)
     {
       u1 += u(symboldif(base, i), symboldif(hedge, i));
       l1 += powdif(symboldif(base, i));
       s1 += powdif(symboldif(hedge, i));
     }
   if(l1*s1 > 0) 
       return(u1 / MathSqrt(l1*s1));
  }
//+------------------------------------------------------------------+
//| УСТАНОВИТЬ ХЕДЖ                                                  |
//+------------------------------------------------------------------+
bool SendH(string symbol, int op, double lots, double price, int sp, 
           string comment, int magic)
  {
   if(OrderSend(symbol
                ,op
                ,lots
                ,price
                ,sp
                ,0
                ,0
                ,comment
                ,magic
                ,0
                ,CLR_NONE)
                >0)
     {
       return(true);
       if(PlayAudio)
           PlaySound("expert.wav");
     }
   else 
     {
       Print(symbol, ": ", magic, " : "
             ,ErrorDescription(GetLastError()));
       return(false);      
     }      
  }
//+------------------------------------------------------------------+
//| ЗАВЕРШИТЬ ХЕДЖ                                                   |
//+------------------------------------------------------------------+
bool CloseHedge(int magic)
  {  
   for(int i = OrdersTotal() - 1; i >= 0; i--)
     {         
       if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && 
                      OrderMagicNumber() == magic)
         {
           if(OrderClose(OrderTicket()
                         , OrderLots()
                         , OrderClosePrice()
                         , MarketInfo(OrderSymbol(), MODE_SPREAD)
                         , CLR_NONE))
               SResult = true;
         }
     }
   if(SResult)
     {
       return(true);
       if(PlayAudio)
         {
           PlaySound("ok.wav");
         }
     }
   else 
       Print("Ошибка CloseHedge: ", ErrorDescription(GetLastError()));
   RefreshRates();
//  return(0);
  }  
//+------------------------------------------------------------------+
//| ОБЩАЯ ПРИБЫЛЬ                                                    |
//+------------------------------------------------------------------+
double TotalCurProfit(int magic)
  {   
   double MyCurrentProfit = 0;
   for(int cnt = 0; cnt < OrdersTotal(); cnt++)
     {
       OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES);
       if(OrderMagicNumber() == magic)
         {
           MyCurrentProfit += (OrderProfit() + OrderSwap());
         }   
     }
   return(MyCurrentProfit);
  }
//+------------------------------------------------------------------+
//| СУЩЕСТВУЮЩАЯ ПОЗИЦИЯ                                             |
//+------------------------------------------------------------------+
int ExistPositions(string symbol,int magic) 
  {
   int NumPos = 0;
   for(int i = 0; i < OrdersTotal(); i++) 
     {
       if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) 
          && OrderSymbol() == symbol
          && OrderMagicNumber() == magic)
         { 
           NumPos++;
         }
     }
   return(NumPos);
  }
//+------------------------------------------------------------------+
//| СУЩЕСТВУЮЩИЕ ОТКРЫТЫЕ ПОЗИЦИИ (OP)                               |
//+------------------------------------------------------------------+
int ExistOP(string symbol,int magic) 
  {
   int NumPos = -1;
   for(int i = 0; i < OrdersTotal(); i++) 
     {
       if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) 
          && OrderSymbol() == symbol
          && OrderMagicNumber() == magic)
         { 
           NumPos = OrderType();
         }
     }
   return(NumPos);
  }
//+------------------------------------------------------------------+
//| Преобразовать значение OP в строковые данные                     |
//+------------------------------------------------------------------+
string OP2Str(int op)
  {
   switch(op)
     {
       case OP_BUY : return("BUY");
       case OP_SELL: return("SELL");
       default     : return("~~");
     }
  }
//+------------------------------------------------------------------+
//| ЗАКРЫТЬ ВСЕ ОРДЕРА ОПРЕДЕЛЁННОГО ТИПА                            |
//+------------------------------------------------------------------+
bool CloseScrap(string sym,int op,int magic)
  {  
   for(int i = OrdersTotal() - 1; i >= 0; i--)
     {         
       if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) 
          && OrderMagicNumber() == magic
          && OrderSymbol() == sym
          && OrderType() == op)
         {
           if(OrderClose(OrderTicket()
                         , OrderLots()
                         , OrderClosePrice()
                         , MarketInfo(OrderSymbol(), MODE_SPREAD)
                         , CLR_NONE))
               BResult = true;
         }
     }
   if(SResult || BResult)
     {
       return(true);
       if(PlayAudio)
         {
           PlaySound("ok.wav");
         }
     }
   else 
       Print("Ошибка CloseScrap:", ErrorDescription(GetLastError()));
   RefreshRates();
//  return(0);
  }  
//+------------------------------------------------------------------+
//| Преобразовать bool в string                                      |
//+------------------------------------------------------------------+
string bool2str(bool boolval)
  {
   if(boolval == true) 
       return("Yes");
   if(boolval == false)
       return("No");
  }
//+------------------------------------------------------------------+


Заключение

В статье описан пример простого хеджирующего эксперта. Чтобы он соответствовал вашему стилю торговли, вы можете модифицировать его. Уверена, что каждый хеджирует по своему методу. И, пожалуйста, обратите внимание на то, что советник такого типа нельзя протестировать в Тестере Стратегий, поскольку у него есть собственные ограничения. Вам придётся его тестировать только "вживую". Ниже приводится примерный резхультат торговли при помощи хеджирующего советника.


А функция ShowStatus будет выглядеть следующим образом:

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