Индикатор спрэда двух инструментов

 

Привет!

Переписываю индикотор на более точный, берется время баров (минутки) по 1 и 2 символам,

а далее смотрится кол-во сделок в пределах этой минуты (если совпали времена баров 1 и 2 символа).

НО! Возникла проблема как считать спрэд, если по первому символу 41 сделка, а по второму 1?


 
prostotrader:

Привет!

Переписываю индикотор на более точный, берется время баров (минутки) по 1 и 2 символам,

а далее смотрится кол-во сделок в пределах этой минуты (если совпали времена баров 1 и 2 символа).

НО! Возникла проблема как считать спрэд, если по первому символу 41 сделка, а по второму 1?


Сделки тут вообще ни причем, надо по биду - аску считать. COPY_TICKS_ALL или COPY_TICKS_INFO
 
Sergey Chalyshev:
Сделки тут вообще ни причем, надо по биду - аску считать. COPY_TICKS_ALL или  COPY_TICKS_INFO

Сереж!

Видишь ли в чем дело. Это индикатор Спрэда между двумя инструментами.

Если нет сделок, то и нет спрэда :)

А усреднять аски и биды - это вообще в корне не правильно, потому что спрэд стакана может быть огромным.

 
prostotrader:

Привет!

Переписываю индикотор на более точный, берется время баров (минутки) по 1 и 2 символам,

а далее смотрится кол-во сделок в пределах этой минуты (если совпали времена баров 1 и 2 символа).

НО! Возникла проблема как считать спрэд, если по первому символу 41 сделка, а по второму 1?


Т.е. по средневзвешанному считать хотите ? Если так, то я либо игнорировал бы свечи с одной сделкой, либо цену брал бы просто равную цене закрытия (мне кажется что более точный второй вариант).

 
Andrey Azatskiy:

Т.е. по средневзвешанному считать хотите ? Если так, то я либо игнорировал бы свечи с одной сделкой, либо цену брал бы просто равную цене закрытия (мне кажется что более точный второй вариант).

Извините, но не в кассу...

 
prostotrader:

Сереж!

Видишь ли в чем дело. Это индикатор Спрэда между двумя инструментами.

Если нет сделок, то и нет спрэда :)

А усреднять аски и биды - это вообще в корне не правильно, потому что спрэд стакана может быть огромным.

Я так и подумал что между инструментами.

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

Покупаем спред по аск1 - бид2, продаем спред бид1 - аск2. Получится две линии покупки и продажи спреда.

Или брать среднюю цену спред = (аск1+бид1)/2 - (аск2+бид2)/2 ;  Тогда получится одна линия.

По другому никак, цена ласт может сильно отличаться от текущих спроса и предложения.

 
Sergey Chalyshev:

Я так и подумал что между инструментами.

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

Покупаем спред по аск1 - бид2, продаем спред бид1 - аск2. Получится две линии покупки и продажи спреда.

Или брать среднюю цену спред = (аск1+бид1)/2 - (аск2+бид2)/2 ;  Тогда получится одна линия.

По другому никак, цена ласт может сильно отличаться от текущих спроса и предложения.

Не согласен.

Цена Ласт не может быть за пределами стакана на минутках

Поробуй сам (только очень долго считает)

//+------------------------------------------------------------------+
//|                                                       Spread.mq5 |
//|                                          Copyright 2019, Mikalas |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Mikalas"
#property link      "http://www.mql5.com"
#property version   "1.000"
//
#property indicator_separate_window

#property indicator_buffers 2
#property indicator_plots   2

//--- plot Label1
#property indicator_label1  "Curr spread"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrYellow
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot Label2
#property indicator_label2  "Midle"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrDeepPink
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- Levels
#property indicator_level1 0
#property indicator_level2 0
#property indicator_level3 0
//---
struct SPREAD_DATA
{
  bool new_bar;
  int bars_cnt;
  datetime bar_time[];
  double value_buff[]; 
};
ENUM_TIMEFRAMES time_frame; //Таймфрейм
//
string tmp_symbol;
string pr_symbol;
string sec_symbol;
//
input int      NMonth    = 3;                      //Разница между фьючерсами (мес)
input int      BrMonth   = 1;                      //Разн. между фьюч.(мес) Brent(RVI, CL)

//--- indicator buffers
double MainBuffer[], MidleBuffer[];
double max_value; 
double min_value;
double mid_spread;
datetime start_time, end_time;
int next_month;
SPREAD_DATA sp_data;
//+------------------------------------------------------------------+
//| Indicator set last expirated symbol function                     |
//+------------------------------------------------------------------+
string SetExpSymbol(const string aSymbol)
{
  int str_tire = StringFind(aSymbol, "-");
  int str_size = StringLen(aSymbol);
  if((str_tire > 0) && (str_size > 0))
  {
    MqlDateTime cur_time;
    datetime c_time = TimeTradeServer();
    TimeToStruct(c_time, cur_time);
    int a_month = cur_time.mon;
    int a_year = cur_time.year;
    string exp_fut = StringSubstr(aSymbol, 0, str_tire + 1) + IntegerToString(a_month) + "." + IntegerToString(a_year - 2000);
    int i = 0;
    while(i < 100)
    {
      ResetLastError();
      SymbolInfoInteger(exp_fut, SYMBOL_SELECT);
      if(GetLastError() != ERR_MARKET_UNKNOWN_SYMBOL)
      {
        datetime exp_date = datetime(SymbolInfoInteger(exp_fut, SYMBOL_EXPIRATION_TIME));
        if(exp_date < StructToTime(cur_time))
        {
          return(exp_fut);
        }
      }
      a_month -= 1;
      if(a_month < 0)
      {
        a_month = 12 - a_month;
        a_year -= 1;
      }
      exp_fut = StringSubstr(aSymbol, 0, str_tire + 1) + IntegerToString(a_month) + "." + IntegerToString(a_year - 2000);  
      i++; 
    }
  }
  return("");
}
//+------------------------------------------------------------------+
//| Indicator Expert set second symbol function                      |
//+------------------------------------------------------------------+
string SetSecSymbol(const string aSymbol)
{
  int str_tire = StringFind(aSymbol, "-");
  int str_tochka = StringFind(aSymbol, ".", str_tire);
  int str_size = StringLen(aSymbol);
  if((str_tire > 0) && (str_tochka > 0) && (str_size > 0))
  {
    string str_month = StringSubstr(aSymbol, str_tire + 1, str_tochka - str_tire - 1);
    string str_year = StringSubstr(aSymbol, str_tochka + 1, str_size - str_tochka - 1);
    long aMonth = StringToInteger(str_month);
    long aYear = StringToInteger(str_year);
    if((aMonth > 0) && (aYear > 0))
    {
      long n_month = aMonth + long(next_month);
      long add_year = (n_month - 1) / 12;
      aYear += add_year;
      str_year = IntegerToString(aYear); 
      aMonth = n_month - add_year * 12;
      str_month = IntegerToString(aMonth);
      return(StringSubstr(aSymbol, 0, str_tire + 1) + str_month + "." + str_year);
    } 
  }
  return("");
}
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
bool CheckOneMonth(const string a_symb)
{
  if((StringFind(a_symb,"BR-") == 0) || (StringFind(a_symb, "CL-") == 0) ||
     (StringFind(a_symb, "GLD-") == 0) || (StringFind(a_symb, "RVI-") == 0) ||
     (StringFind(a_symb, "UINR-") == 0) || (StringFind(a_symb, "Al-") == 0) ||
     (StringFind(a_symb, "Zn-") == 0) || (StringFind(a_symb, "Nl-") == 0) ||
     (StringFind(a_symb, "Co-") == 0))
  {
    if(NMonth != 1) return(false);
  }
  return(true);
}
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
  pr_symbol = Symbol();
  mid_spread = 0.0;
  sp_data.bars_cnt = 0;
  sp_data.new_bar = false;
  time_frame = Period();
  if(time_frame != PERIOD_M1)
  {
    Alert("Период должен быть М1!");
    return(INIT_FAILED);
  }
  next_month = NMonth;
  max_value = -DBL_MAX; 
  min_value = DBL_MAX;
//---
  if(CheckOneMonth(pr_symbol)== false)
  {
    if(next_month > 1) next_month = BrMonth;
  }  
  sec_symbol = SetSecSymbol(pr_symbol);  
  ResetLastError();
  if(!SymbolInfoInteger(sec_symbol, SYMBOL_SELECT))
  {
    if(GetLastError() != ERR_MARKET_UNKNOWN_SYMBOL)
    {
      SymbolSelect(sec_symbol, true);
    }
    else
    {
      Alert(__FUNCTION__, ": Неизвестный символ - ", sec_symbol);
      return(INIT_FAILED);
    }    
  }
//--- 
  tmp_symbol = SetExpSymbol(pr_symbol);
  if(tmp_symbol == "")
  {
    Alert("Экспирируемый симаол не установлен!");
    return(INIT_FAILED);
  }
  else
  {
    if(SymbolSelect(tmp_symbol, true) == false)
    {
      Alert("Экспирируемый симаол не выбран. Или не верное имя символа!");
      return(INIT_FAILED);
    }
    start_time = datetime(SymbolInfoInteger(tmp_symbol, SYMBOL_EXPIRATION_TIME));
    MqlDateTime st_time;
    if(TimeToStruct(start_time, st_time) == true)
    {
      st_time.hour = 19;
      st_time.min = 0;
      st_time.sec = 0;
      start_time = StructToTime(st_time);
    }
    else
    {
      Alert("Не удалось получить время экспирации!");
      return(INIT_FAILED);
    }
  }
//--- Calculate exists soread ----
  datetime pr_t_array[], sec_t_array[];
  int pr_res = 0;
  int sec_res = 0;
  end_time = TimeTradeServer();
  int z = 0;
  while ((pr_res <= 0) || (z == 499))
  {
    pr_res = CopyTime(pr_symbol, time_frame, start_time, end_time, pr_t_array);
    z++;
  }
  z = 0;
  while ((sec_res <= 0) || (z == 499))
  {
    sec_res = CopyTime(sec_symbol, time_frame, start_time, end_time, sec_t_array);
    z++;
  }
  if((pr_res > 0) &&(sec_res > 0))
  {
    sp_data.bars_cnt = 0;
    int cur_pos = 0;
    MqlTick pr_ticks[], sec_ticks[];
    for(int i = 0; i < sec_res; i++)
    {
      for(int j = cur_pos; j < pr_res; j++)
      {
        if(sec_t_array[i] == pr_t_array[j])
        {
          int ticks_pr = CopyTicksRange(pr_symbol, pr_ticks, COPY_TICKS_TRADE, ulong(sec_t_array[i]) * 1000, (ulong(sec_t_array[i]) + 59) * 1000 + 999);
          int ticks_sec = CopyTicksRange(sec_symbol, sec_ticks, COPY_TICKS_TRADE, ulong(sec_t_array[i]) * 1000, (ulong(sec_t_array[i]) + 59) * 1000 + 999);
          if((ticks_pr > 0) && (ticks_sec > 0))
          {
            double pr_price = 0;
            double sec_price = 0;
            for(int k = 0; k < ticks_pr; k++)
            {
              pr_price += pr_ticks[k].last;
            }
            pr_price = pr_price / ticks_pr;
            for(int k = 0; k < ticks_sec; k++)
            {
              sec_price += sec_ticks[k].last;
            }
            sec_price = sec_price / ticks_sec;
            ArrayResize(sp_data.value_buff, sp_data.bars_cnt + 1);
            ArrayResize(sp_data.bar_time, sp_data.bars_cnt + 1);
            sp_data.bar_time[sp_data.bars_cnt] = sec_t_array[i];
            sp_data.value_buff[sp_data.bars_cnt] = (sec_price - pr_price)/Point();
            sp_data.bars_cnt++;
          }
          cur_pos = j + 1;
          break;
        }
      }
    }
  }
  else
  {
    Alert("Не удалось получить время баров!");
      return(INIT_FAILED);
  }
  SymbolSelect(tmp_symbol, false); 
//---  
  IndicatorSetInteger(INDICATOR_DIGITS, 2);
  IndicatorSetString(INDICATOR_SHORTNAME, "Spread");
  SetIndexBuffer(0, MainBuffer, INDICATOR_DATA);
  ArraySetAsSeries(MainBuffer, true);
  PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
  SetIndexBuffer(1, MidleBuffer, INDICATOR_DATA);
  ArraySetAsSeries(MidleBuffer, true);
  PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
  IndicatorSetInteger(INDICATOR_LEVELCOLOR, 0, clrLime);
  IndicatorSetInteger(INDICATOR_LEVELCOLOR, 1, clrAqua);
  IndicatorSetInteger(INDICATOR_LEVELCOLOR, 2, clrLime); 
  IndicatorSetInteger(INDICATOR_LEVELSTYLE, STYLE_DOT);
  IndicatorSetInteger(INDICATOR_LEVELWIDTH, 1);
//---  
  return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
// Custom indicator DeInit function                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
  if(reason == REASON_INITFAILED)
  {
    Print("Индикатор удалён! Причина - ошибка инициализации.");
    ChartIndicatorDelete(0, 1, "Spread"); 
  }
}
//+------------------------------------------------------------------+
//| Custom indicator fill levels function                            |
//+------------------------------------------------------------------+
void FillLevels()
{
  IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, max_value);
  IndicatorSetDouble(INDICATOR_LEVELVALUE, 1, MidleBuffer[0]);
  IndicatorSetDouble(INDICATOR_LEVELVALUE, 2, min_value);
  double a_value = MathAbs(max_value - min_value);
  IndicatorSetDouble(INDICATOR_MAXIMUM, max_value + a_value / 100 * 10);
  IndicatorSetDouble(INDICATOR_MINIMUM, min_value - a_value / 100 * 10);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
  if(prev_calculated == 0)
  {
    ArrayInitialize(MainBuffer, EMPTY_VALUE);
    ArrayInitialize(MidleBuffer, EMPTY_VALUE);
    int a_size = ArraySize(sp_data.value_buff);
    int cur_pos = 0;
    for(int i = 0; i < rates_total; i++)
    {
      for(int j = cur_pos; j < a_size; j++)
      {
        if(time[i] == sp_data.bar_time[j])
        {
          MainBuffer[rates_total - 1 - i] = sp_data.value_buff[j];
          if(sp_data.value_buff[j] > max_value) max_value = sp_data.value_buff[j];
          if(sp_data.value_buff[j] < min_value) min_value = sp_data.value_buff[j];
          cur_pos = j + 1;
          break;
        }
        else
        {
          if(i > 0) MainBuffer[rates_total - 1 - i] = MainBuffer[rates_total - i];
        }
      }  
    }
    FillLevels();
  }
//---
  
  return(rates_total);
}

//+------------------------------------------------------------------+


 

 
prostotrader:

Не согласен.

Цена Ласт не может быть за пределами стакана на минутках

Поробуй сам (только очень долго считает)


 

Не пробовал создавать пользовательский символ? Может тебе подойдет.

Там можно любую нужную тебе формулу задать и будет все автоматически считаться.

Пользовательские финансовые инструменты - Для продвинутых пользователей - MetaTrader 5
Пользовательские финансовые инструменты - Для продвинутых пользователей - MetaTrader 5
  • www.metatrader5.com
Торговая платформа позволяет создавать собственные финансовые инструменты. По ним можно просматривать графики и проводить технический анализ, их можно использовать для проверки торговых роботов и индикаторов в тестере стратегий. Если ваш брокер не предоставляет инструмент, на котором вы хотели бы проверить свою стратегию, или же предоставляет...
 

Попробуй мой индикатор, давно делал, может подойдет

//+------------------------------------------------------------------+
//|                                                 Spread_of_Symbol |
//|                                        Copyright 2016, Serj_Che. |
//|                           https://www.mql5.com/ru/users/serj_che |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Serj_Che."
#property link      "https://www.mql5.com/ru/users/serj_che"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot Label1
#property indicator_label1  "Label1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- input parameters
input string symbol1="SBRF-9.19";
input string symbol2="SBRF-12.19";
input double mass_of_symbol1=1;
input double mass_of_symbol2=1;
input ENUM_APPLIED_PRICE price = PRICE_CLOSE;


int i,r1,r2,j;
double S,prs,k1,k2,d1,d2;
//--- indicator buffers
double        ind1[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,ind1,INDICATOR_DATA);
   string shortname="("+symbol1+" - "+symbol2+")";
   IndicatorSetString(INDICATOR_SHORTNAME,shortname);
   IndicatorSetInteger(INDICATOR_LEVELS,1);
   IndicatorSetInteger(INDICATOR_LEVELSTYLE,STYLE_SOLID);
   IndicatorSetInteger(INDICATOR_LEVELWIDTH,2);
   IndicatorSetInteger(INDICATOR_LEVELCOLOR,LimeGreen);
   IndicatorSetDouble(INDICATOR_LEVELVALUE,0,0.1);
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   double rates1[];
   double rates2[];
   i=0;
   if(i<prev_calculated) i=prev_calculated-1;
   while(i<rates_total && !IsStopped())
        {
         if(price==PRICE_CLOSE)
           {
            if(CopyClose(symbol1,Period(),time[i],1,rates1)<1 || CopyClose(symbol2,Period(),time[i],1,rates2)<1)
              {
               ind1[i]=0.0;
              }
            else
              {
               ind1[i]=mass_of_symbol1*rates1[0]-mass_of_symbol2*rates2[0];
              }
           }
         if(price==PRICE_OPEN)
           {
            if(CopyOpen(symbol1,Period(),time[i],1,rates1)<1 || CopyOpen(symbol2,Period(),time[i],1,rates2)<1)
              {
               ind1[i]=0.0;
              }
            else
              {
               ind1[i]=mass_of_symbol1*rates1[0]-mass_of_symbol2*rates2[0];
              }
           }
         i++;  
        }
   IndicatorSetDouble(INDICATOR_LEVELVALUE,0,ind1[rates_total-1]);
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
 
Sergey Chalyshev:

Попробуй мой индикатор, давно делал, может подойдет

Спасибо, Сереж!

Но такой индикатор, как у тебя, я написал еще в 2013 году.

Вот сейчас, хочу модернизировать, сделать более точным.

Добавлено

Но только время потерял :)


 
prostotrader:

Привет!

Переписываю индикотор на более точный, берется время баров (минутки) по 1 и 2 символам,

а далее смотрится кол-во сделок в пределах этой минуты (если совпали времена баров 1 и 2 символа).

НО! Возникла проблема как считать спрэд, если по первому символу 41 сделка, а по второму 1?


Нужно сымитировать отключение торговли по акциям, когда фьючерсы все еще торгуются?