Стакан и сделки

 

Пишу анализатор стакана, увязанного со сделками и "уткнулся" в прблему получения новых сделок.

Функция RefreshStData() вызывается из класса при каждом срабатывании OnBookEvent

Вопросы:

1.Какой размер массива должен быть, чтобы не пропустить сделки?

2. Как быстро "вытащить" НОВЫЕ сделки, если предыдущие тики храняться в mem_ticks_array такого же размера ?

3. Может быть есть альтернативное решение быстрого получкения новых сделок, после срабатывания OnBookEvent ? 

//+------------------------------------------------------------------+
//|                                                       Stakan.mqh |
//|                                      Copyright 2018 prostotrader |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018 prostotrader"
#property link      "https://www.mql5.com"
//---
#define BOOK_DEPTH 20
#define TICKS_COUNT 100
//---
struct STAKAN_ELEMENT
{
  double price;
  long   volume;
};
struct DEALS
{
  long   sell_cnt;
  long   buy_cnt;
  long   sell_volume;
  long   buy_volume;
};
struct STAKAN_DATA
{
  STAKAN_ELEMENT sell_data[BOOK_DEPTH];
  STAKAN_ELEMENT buy_data[BOOK_DEPTH];
  DEALS          deals;
  int            sell_cnt;
  int            buy_cnt;
  int            max_sell_index;
  int            min_sell_index;
  int            max_buy_index;
  int            min_buy_index;
  long           ses_deals;
  long           ses_sell_orders;
  long           ses_buy_orders;  
  long           symb_vol_high;   
  long           symb_vol_low;
  int            spread;
  double         best_bid;
  double         bid_high;
  double         best_ask;
  double         ask_high;
  double         ask_low;
  double         bid_low; 
  double         last; 
  double         last_high;
  double         last_low;
  double         ses_volume;
  double         ses_turmover;
  double         ses_buy_ord_volume;
  double         ses_sell_ord_volume;
  double         ses_interest;
  double         ses_aw;
};  
//--- class
class CStakan
{
  private:
//--- Variables
    STAKAN_ELEMENT temp_data[BOOK_DEPTH];            
    STAKAN_DATA    stakan_data;
    bool           set_book;
    string         st_symbol;
    MqlBookInfo    book_data[];
    MqlTick        ticks_array[];
    MqlTick        mem_ticks_array[];
    uint           ticks_cnt; 
//--- functions    
    void           GetOtherData();
    
  public:
                   CStakan(void);
                   ~CStakan(void);
//---Functions
    bool           SetStakanSymbol(const string st_symbol);
    bool           RefreshStData();
    STAKAN_DATA    GetStData();
};
//+------------------------------------------------------------------+
// Constructor                                                       |
//+------------------------------------------------------------------+
CStakan::CStakan(void)
{
  ticks_cnt = uint(TICKS_COUNT);
  set_book = false;
  ZeroMemory(stakan_data);
}
//+------------------------------------------------------------------+
// Destructor                                                        |
//+------------------------------------------------------------------+
CStakan::~CStakan(void)
{
  if(set_book == true) MarketBookRelease(st_symbol);
}
//+------------------------------------------------------------------+
//| Set stakan symnol function                                       |
//+------------------------------------------------------------------+
bool CStakan::SetStakanSymbol(const string st_symb)
{
  if(SymbolSelect(st_symb, true) == true)
  {
    st_symbol = st_symb;
    set_book = MarketBookAdd(st_symbol);
//--- Set ticks
    uint result = uint(CopyTicks(st_symbol, mem_ticks_array, COPY_TICKS_TRADE, 0, ticks_cnt));
    if(result < ticks_cnt) return(false);    
  }
  return(set_book);
}
//+------------------------------------------------------------------+
//| Refresh Stakan Data function                                     |
//+------------------------------------------------------------------+
bool CStakan::RefreshStData(void)
{
  if((MarketBookGet(st_symbol, book_data) == true) && (set_book == true))//getBook )
  {
    int size = ArraySize(book_data);
    if(size > 0)
    {
      ZeroMemory(temp_data);
      long max_vol = 0;
      long min_vol = 0;
      stakan_data.max_buy_index = -1;
      stakan_data.min_buy_index = -1;
      stakan_data.max_sell_index = -1;
      stakan_data.min_sell_index = -1;
      stakan_data.buy_cnt = 0;
      stakan_data.sell_cnt = 0;
      for(int i = 0; i < size; i++)
      {
        if((book_data[i].type == BOOK_TYPE_SELL) || (book_data[i].type == BOOK_TYPE_SELL_MARKET))
        {
          temp_data[stakan_data.sell_cnt].price = book_data[i].price;
          temp_data[stakan_data.sell_cnt].volume = book_data[i].volume;
          if(stakan_data.sell_cnt == 0)
          {
            max_vol = book_data[i].volume;
            min_vol = book_data[i].volume;
            stakan_data.max_sell_index = stakan_data.sell_cnt;
            stakan_data.min_sell_index = stakan_data.sell_cnt;
          }
          else
          {
            if(book_data[i].volume > max_vol)
            {
              stakan_data.max_sell_index = stakan_data.sell_cnt;
            }
            else
            if(book_data[i].volume < min_vol)
            {
              stakan_data.min_sell_index = stakan_data.sell_cnt;
            }
          }
          stakan_data.sell_cnt++;
        }
        else
        if((book_data[i].type == BOOK_TYPE_BUY) || (book_data[i].type == BOOK_TYPE_BUY_MARKET))
        {
//--- get buy data        
          stakan_data.buy_data[stakan_data.buy_cnt].price = book_data[i].price;
          stakan_data.buy_data[stakan_data.buy_cnt].volume = book_data[i].volume;
          if(stakan_data.buy_cnt == 0)
          {
            max_vol = book_data[i].volume;
            min_vol = book_data[i].volume;
            stakan_data.max_buy_index = stakan_data.buy_cnt;
            stakan_data.min_buy_index = stakan_data.buy_cnt;  
          }
          else
          {
            if(book_data[i].volume > max_vol)
            {
              stakan_data.max_buy_index = stakan_data.buy_cnt;
            }
            else
            if(book_data[i].volume < min_vol)
            {
              stakan_data.min_buy_index = stakan_data.buy_cnt;
            }
          }
          stakan_data.buy_cnt++;
        }
      }
//--- set sell data
      stakan_data.max_sell_index = stakan_data.sell_cnt - 1 - stakan_data.max_sell_index;
      stakan_data.min_sell_index = stakan_data.sell_cnt - 1 - stakan_data.min_sell_index;
      for(int j = 0; j < stakan_data.sell_cnt; j++)
      {
        stakan_data.sell_data[j].price = temp_data[stakan_data.sell_cnt - 1 - j].price;  
        stakan_data.sell_data[j].volume = temp_data[stakan_data.sell_cnt - 1 - j].volume;
      }
      GetOtherData();
      return(true);
    }
    else ZeroMemory(stakan_data);
  }
 // GetOtherData(); //DEBUG
  return(false);
}
//+------------------------------------------------------------------+
//| Set stakan symnol function                                       |
//+------------------------------------------------------------------+
void CStakan::GetOtherData(void)
{
  stakan_data.ses_deals = SymbolInfoInteger(st_symbol, SYMBOL_SESSION_DEALS);
  stakan_data.ses_sell_orders = SymbolInfoInteger(st_symbol, SYMBOL_SESSION_SELL_ORDERS);
  stakan_data.ses_buy_orders = SymbolInfoInteger(st_symbol, SYMBOL_SESSION_BUY_ORDERS);
  stakan_data.symb_vol_high = SymbolInfoInteger(st_symbol, SYMBOL_VOLUMEHIGH);
  stakan_data.symb_vol_low = SymbolInfoInteger(st_symbol, SYMBOL_VOLUMELOW);
  stakan_data.spread = int(SymbolInfoInteger(st_symbol, SYMBOL_SPREAD));
//---
  stakan_data.best_ask = SymbolInfoDouble(st_symbol, SYMBOL_ASK);
  stakan_data.best_bid = SymbolInfoDouble(st_symbol, SYMBOL_BID);
  stakan_data.ask_high = SymbolInfoDouble(st_symbol, SYMBOL_ASKHIGH);
  stakan_data.bid_high = SymbolInfoDouble(st_symbol, SYMBOL_BIDHIGH);
  stakan_data.ask_low = SymbolInfoDouble(st_symbol, SYMBOL_ASKLOW);
  stakan_data.bid_low = SymbolInfoDouble(st_symbol, SYMBOL_BIDLOW);
  stakan_data.last = SymbolInfoDouble(st_symbol, SYMBOL_LAST);
  stakan_data.last_high = SymbolInfoDouble(st_symbol, SYMBOL_LASTHIGH);
  stakan_data.last_low = SymbolInfoDouble(st_symbol, SYMBOL_LASTLOW);
  stakan_data.ses_volume = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_VOLUME);
  stakan_data.ses_turmover = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_TURNOVER);
  stakan_data.ses_buy_ord_volume = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_BUY_ORDERS_VOLUME); 
  stakan_data.ses_sell_ord_volume = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_SELL_ORDERS_VOLUME);
  stakan_data.ses_interest = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_INTEREST);
  stakan_data.ses_aw = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_AW);
//---Deals  TODO ????????????????????????????????????????????????????????????????????????????
  stakan_data.deals.buy_cnt = 0;
  stakan_data.deals.sell_cnt = 0;
  stakan_data.deals.sell_volume = 0;
  stakan_data.deals.buy_volume = 0;
  int result = CopyTicks(st_symbol, ticks_array, COPY_TICKS_TRADE, 0, ticks_cnt);
  if(result == ticks_cnt)
  {
    for(int i = 0; i < result; i++)
    {
    //Какой размер массива должен быть, чтобы не пропустить сделки?
    //Как быстро "вытащить" НОВЫЕ сделки, если предыдущие тики храняться в mem_ticks_array такого же размера ???????
    /* if((ticks_array[i].flags&TICK_FLAG_SELL)==TICK_FLAG_SELL) 
      { 
        stakan_data.deals.sell_cnt++;
        stakan_data.deals.sell_volume += long(ticks_array[i].volume);
      }
      else
      if((ticks_array[i].flags&TICK_FLAG_BUY)==TICK_FLAG_BUY) 
      { 
        stakan_data.deals.buy_cnt++;
        stakan_data.deals.buy_volume += long(ticks_array[i].volume); 
      } */
    }
    ArrayCopy(mem_ticks_array, ticks_array);
  }
}
//+------------------------------------------------------------------+
//| Get stakan data function                                         |
//+------------------------------------------------------------------+
STAKAN_DATA CStakan::GetStData(void)
{
  return(stakan_data);
}
//+------------------------------------------------------------------+
 

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Загадочный биржевой индикатор

fxsaber, 2016.10.04 11:28

// Индикатор в виде гистограммы показывает проторгованный оборот BUY и SELL

#property indicator_separate_window

#property indicator_buffers 2
#property indicator_plots 2

#property indicator_label1  "TurnOver_BUY"
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_style1 STYLE_SOLID
#property indicator_color1 clrYellow

#property indicator_label2  "TurnOver_SELL"
#property indicator_type2 DRAW_HISTOGRAM
#property indicator_style2 STYLE_SOLID
#property indicator_color2 clrRed

long LastTime = 0; // time_msc-время последнего тика (самого свежего), полученного из истории
int Count = 0;     // Количество тиков в последенем запросе, у которых time_msc == LastTime

double Buffer0[];
double Buffer1[];

// Возвращает свежие тики, пришедшие после предыдущего вызова
int GetFreshTicks( MqlTick &Ticks[], const uint flags = COPY_TICKS_TRADE, const uint count = 100000 )
{
  int Res = 0;

  MqlTick NewTicks[];
  const int NewAmount = CopyTicks(Symbol(), NewTicks, flags, LastTime, count);

  if ((NewAmount > 0) && (Count < NewAmount))
  {
    Res = ArrayCopy(Ticks, NewTicks, 0, Count);

    // Взяли крайнее время из текущей истории
    LastTime = NewTicks[NewAmount - 1].time_msc;
    Count = 1;

    // Находим (Count) в текущей истории количество тиков со временем LastTime
    for (int i = NewAmount - 2; i >= 0; i--)
    {
      if (NewTicks[i].time_msc < LastTime)
        break;

      Count++;
    }
  }

  return(Res);
}

// Ширина гистограммы
void SetScale()
{
  const int Width = 9 / (1 << (5 - (int)ChartGetInteger(0, CHART_SCALE)));

  if (Width != PlotIndexGetInteger(0, PLOT_LINE_WIDTH))
  {
    PlotIndexSetInteger(0, PLOT_LINE_WIDTH, Width);
    PlotIndexSetInteger(1, PLOT_LINE_WIDTH, Width);

    ChartRedraw();
  }
}

void OnInit()
{
  SetIndexBuffer(0, Buffer0);
  SetIndexBuffer(1, Buffer1);
}

void OnChartEvent( const int id, const long &lparam, const double &dparam, const string &sparam )
{
  if (id == CHARTEVENT_CHART_CHANGE)
    SetScale();
}

// Заполняет бар соответствующими ему тиками
void SetBarData( const datetime TimeBar, const MqlTick &Ticks[], int &Pos, double &TurnOverBuy, double &TurnOverSell )
{
  const datetime NextTime = TimeBar + PeriodSeconds();
  const int Amount = ArraySize(Ticks);

  while (Pos < Amount)
  {
    const MqlTick Tick = Ticks[Pos];

    if (Tick.time >= NextTime)
      break;

    if ((bool)(Tick.flags & TICK_FLAG_BUY))
      TurnOverBuy += (double)Tick.volume;
    else if ((bool)(Tick.flags & TICK_FLAG_SELL))
      TurnOverSell -= (double)Tick.volume;

    Pos++;
  }

  return;
}

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)
  {
    // возьмем тики с начала утренней сессии
    LastTime = (TimeCurrent() - (TimeCurrent() % (24 * 3600))) * 1000;

    Count = 0;
  }

  // Зануляем новые бары
  if (rates_total > prev_calculated)
  {
    ArrayFill(Buffer0, prev_calculated, rates_total - prev_calculated, 0);
    ArrayFill(Buffer1, prev_calculated, rates_total - prev_calculated, 0);
  }

  MqlTick Ticks[];

  // Взяли свеженькие тики
  const int Amount = GetFreshTicks(Ticks);

  if (Amount > 0)
  {
    int Pos;

    const datetime LastTime2 = Ticks[0].time - (Ticks[0].time % PeriodSeconds());

    // Находим бар (Pos), включающий первый свежий тик
    for (Pos = rates_total - 1; Pos >= 0; Pos--)
      if (time[Pos] == LastTime2)
        break;

    if (Pos >= 0)
    {
      int i = 0;

      // Заполняем бары своими тиками
      while ((Pos < rates_total) && (i < Amount))
      {
        SetBarData(time[Pos], Ticks, i, Buffer0[Pos], Buffer1[Pos]);

        Pos++;
      }
    }
  }

  return(rates_total);
}
Желтым пометил то, что Вам надо. На момент написания еще не было CopyTicksRange. Конечно, разумнее ее использовать для этой задачи.
 
prostotrader:

Вопросы:

1.Какой размер массива должен быть, чтобы не пропустить сделки?

2. Как быстро "вытащить" НОВЫЕ сделки, если предыдущие тики храняться в mem_ticks_array такого же размера ?

3. Может быть есть альтернативное решение быстрого получкения новых сделок, после срабатывания OnBookEvent ? 

1. Для этого, полагаю, нужно написать тестовый индикатор и оставить его хотя бы на день на интересующем Вас символе. Индикатор как раз должен считать сколько максимально пришло сделок с момента последнего запроса сделок (как сохранить время/считать показал fxsaber);

2 и 3. Какая задача? Просто, по факту размер сделок, полученных с последнего запроса часто будет разным (вероятно, что даже 100 может не всегда хватить).

 
Alexey Kozitsyn:

1. Для этого, полагаю, нужно написать тестовый индикатор и оставить его хотя бы на день на интересующем Вас символе. Индикатор как раз должен считать сколько максимально пришло сделок с момента последнего запроса сделок (как сохранить время/считать показал fxsaber);

2 и 3. Какая задача? Просто, по факту размер сделок, полученных с последнего запроса часто будет разным (вероятно, что даже 100 может не всегда хватить).

2 и 3 Есть два одинаковых массива с предыдущими и текущими тиками.

Как быстро "отсеять" совпадающие данные. (т.е получить свежие сделки)? 

 
prostotrader:

2 и 3 Есть два одинаковых массива с предыдущими и текущими тиками.

Как быстро "отсеять" совпадающие данные. (т.е получить свежие сделки)? 

Свежие сделки, исключительно свежие сделки получать нужно так, как у fxsaber'a. Т.е. делаете запрос, запоминаете время последней сделки (savedTime) в запросе и количество сделок (savedNum) в этот момент (момент последней сделки). Далее снова делаете запрос c момента времени savedTime (т.к. сразу может прийти не вся пачка миллисекунды savedTime). И вычитаете сохраненное (ранее полученное savedNum) количество сделок первой миллисекунды - считаете только новые сделки. И так по кругу.

Правда, я бы еще проверил (на всякий случай) последовательность сделок момента savedTime. Вдруг что..

 
Alexey Kozitsyn:

Свежие сделки, исключительно свежие сделки получать нужно так, как у fxsaber'a. Т.е. делаете запрос, запоминаете время последней сделки (savedTime) в запросе и количество сделок (savedNum) в этот момент (момент последней сделки). Далее снова делаете запрос c момента времени savedTime (т.к. сразу может прийти не вся пачка миллисекунды savedTime). И вычитаете сохраненное (ранее полученное savedNum) количество сделок первой миллисекунды - считаете только новые сделки. И так по кругу.

Правда, я бы еще проверил (на всякий случай) последовательность сделок момента savedTime. Вдруг что..

Думакется, что нельзя полагатьься что в новых данных нет старых.

Допустим, при инициализации мы запомлили стартовое время.

В следующем проходе мы получили, например 10 сделок, а в последующем 8.

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

на высоколиквидных инструментах много сделок с одинаковым временем. 

 
prostotrader:

Думакется, что нельзя полагатьься что в новых данных нет старых.

Допустим, при инициализации мы запомлили стартовое время.

В следующем проходе мы получили, например 10 сделок, а в последующем 8.

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

на высоколиквидных инструментах много сделок с одинаковым временем. 

Можете посмотреть в моей статье алгоритм, как я получаю сделки. Правда у меня конкретно вопрос пачки (когда несколько сделок с одинаковым временем) решен иначе. Я учитываю пачку только тогда, когда она полностью сформирована (т.е. началась следующая пачка). И порядок сделок в пачке мне не важен.

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

 
Alexey Kozitsyn:

Можете посмотреть в моей статье алгоритм, как я получаю сделки. Правда у меня конкретно вопрос пачки (когда несколько сделок с одинаковым временем) решен иначе. Я учитываю пачку только тогда, когда она полностью сформирована (т.е. началась следующая пачка). И порядок сделок в пачке мне не важен.

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

Вспомнил, я же писал Лента всех сделок

Спасибо всем

Работает точно и быстро

Желтым - изменения

//+------------------------------------------------------------------+
//|                                                       Stakan.mqh |
//|                                      Copyright 2018 prostotrader |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018 prostotrader"
#property link      "https://www.mql5.com"
//---
#define BOOK_DEPTH 20
#define TICKS_COUNT 100
//---
struct STAKAN_ELEMENT
{
  double price;
  long   volume;
};
struct DEALS
{
  long   sell_cnt;
  long   buy_cnt;
  long   sell_volume;
  long   buy_volume;
};
struct OPERATION_TIME
{
  ulong start_count;
  ulong end_count;;
  ulong last_tick_time;
};
struct STAKAN_DATA
{
  STAKAN_ELEMENT sell_data[BOOK_DEPTH];
  STAKAN_ELEMENT buy_data[BOOK_DEPTH];
  DEALS          deals;
  int            sell_cnt;
  int            buy_cnt;
  int            max_sell_index;
  int            min_sell_index;
  int            max_buy_index;
  int            min_buy_index;
  long           ses_deals;
  long           ses_sell_orders;
  long           ses_buy_orders;  
  long           symb_vol_high;   
  long           symb_vol_low;
  int            spread;
  double         best_bid;
  double         bid_high;
  double         best_ask;
  double         ask_high;
  double         ask_low;
  double         bid_low; 
  double         last; 
  double         last_high;
  double         last_low;
  double         ses_volume;
  double         ses_turmover;
  double         ses_buy_ord_volume;
  double         ses_sell_ord_volume;
  double         ses_interest;
  double         ses_aw;
};  
//--- class
class CStakan
{
  private:
//--- Variables
    STAKAN_ELEMENT temp_data[BOOK_DEPTH];            
    STAKAN_DATA    stakan_data;
    OPERATION_TIME oper_time;
    bool           set_book;
    string         st_symbol;
    MqlBookInfo    book_data[];
    MqlTick        ticks_array[];
    MqlTick        mem_tick_array[];
    uint           ticks_cnt; 
//--- functions    
    void           GetOtherData();
    
  public:
                   CStakan(void);
                   ~CStakan(void);
//---Functions
    bool           SetStakanSymbol(const string st_symbol);
    bool           RefreshStData();
    STAKAN_DATA    GetStData();
};
//+------------------------------------------------------------------+
// Constructor                                                       |
//+------------------------------------------------------------------+
CStakan::CStakan(void)
{
  ticks_cnt = uint(TICKS_COUNT);
  set_book = false;
  ZeroMemory(stakan_data);
}
//+------------------------------------------------------------------+
// Destructor                                                        |
//+------------------------------------------------------------------+
CStakan::~CStakan(void)
{
  if(set_book == true) MarketBookRelease(st_symbol);
}
//+------------------------------------------------------------------+
//| Set stakan symnol function                                       |
//+------------------------------------------------------------------+
bool CStakan::SetStakanSymbol(const string st_symb)
{
  if(SymbolSelect(st_symb, true) == true)
  {
    st_symbol = st_symb;
    set_book = MarketBookAdd(st_symbol);
//--- Set ticks
    oper_time.start_count = GetMicrosecondCount();
    uint result = uint(CopyTicks(st_symbol, mem_tick_array, COPY_TICKS_TRADE, 0, 1));
    if(result < 1) return(false);
    oper_time.last_tick_time = mem_tick_array[0].time_msc;
        
  }
  return(set_book);
}
//+------------------------------------------------------------------+
//| Refresh Stakan Data function                                     |
//+------------------------------------------------------------------+
bool CStakan::RefreshStData(void)
{
  if((MarketBookGet(st_symbol, book_data) == true) && (set_book == true))//getBook )
  {
    int size = ArraySize(book_data);
    if(size > 0)
    {
      ZeroMemory(temp_data);
      long max_vol = 0;
      long min_vol = 0;
      stakan_data.max_buy_index = -1;
      stakan_data.min_buy_index = -1;
      stakan_data.max_sell_index = -1;
      stakan_data.min_sell_index = -1;
      stakan_data.buy_cnt = 0;
      stakan_data.sell_cnt = 0;
      for(int i = 0; i < size; i++)
      {
        if((book_data[i].type == BOOK_TYPE_SELL) || (book_data[i].type == BOOK_TYPE_SELL_MARKET))
        {
          temp_data[stakan_data.sell_cnt].price = book_data[i].price;
          temp_data[stakan_data.sell_cnt].volume = book_data[i].volume;
          if(stakan_data.sell_cnt == 0)
          {
            max_vol = book_data[i].volume;
            min_vol = book_data[i].volume;
            stakan_data.max_sell_index = stakan_data.sell_cnt;
            stakan_data.min_sell_index = stakan_data.sell_cnt;
          }
          else
          {
            if(book_data[i].volume > max_vol)
            {
              stakan_data.max_sell_index = stakan_data.sell_cnt;
            }
            else
            if(book_data[i].volume < min_vol)
            {
              stakan_data.min_sell_index = stakan_data.sell_cnt;
            }
          }
          stakan_data.sell_cnt++;
        }
        else
        if((book_data[i].type == BOOK_TYPE_BUY) || (book_data[i].type == BOOK_TYPE_BUY_MARKET))
        {
//--- get buy data        
          stakan_data.buy_data[stakan_data.buy_cnt].price = book_data[i].price;
          stakan_data.buy_data[stakan_data.buy_cnt].volume = book_data[i].volume;
          if(stakan_data.buy_cnt == 0)
          {
            max_vol = book_data[i].volume;
            min_vol = book_data[i].volume;
            stakan_data.max_buy_index = stakan_data.buy_cnt;
            stakan_data.min_buy_index = stakan_data.buy_cnt;  
          }
          else
          {
            if(book_data[i].volume > max_vol)
            {
              stakan_data.max_buy_index = stakan_data.buy_cnt;
            }
            else
            if(book_data[i].volume < min_vol)
            {
              stakan_data.min_buy_index = stakan_data.buy_cnt;
            }
          }
          stakan_data.buy_cnt++;
        }
      }
//--- set sell data
      stakan_data.max_sell_index = stakan_data.sell_cnt - 1 - stakan_data.max_sell_index;
      stakan_data.min_sell_index = stakan_data.sell_cnt - 1 - stakan_data.min_sell_index;
      for(int j = 0; j < stakan_data.sell_cnt; j++)
      {
        stakan_data.sell_data[j].price = temp_data[stakan_data.sell_cnt - 1 - j].price;  
        stakan_data.sell_data[j].volume = temp_data[stakan_data.sell_cnt - 1 - j].volume;
      }
      GetOtherData();
      return(true);
    }
    else ZeroMemory(stakan_data);
  }
 // GetOtherData(); //DEBUG
  return(false);
}
//+------------------------------------------------------------------+
//| Get Other Data function                                          |
//+------------------------------------------------------------------+
void CStakan::GetOtherData(void)
{
  stakan_data.ses_deals = SymbolInfoInteger(st_symbol, SYMBOL_SESSION_DEALS);
  stakan_data.ses_sell_orders = SymbolInfoInteger(st_symbol, SYMBOL_SESSION_SELL_ORDERS);
  stakan_data.ses_buy_orders = SymbolInfoInteger(st_symbol, SYMBOL_SESSION_BUY_ORDERS);
  stakan_data.symb_vol_high = SymbolInfoInteger(st_symbol, SYMBOL_VOLUMEHIGH);
  stakan_data.symb_vol_low = SymbolInfoInteger(st_symbol, SYMBOL_VOLUMELOW);
  stakan_data.spread = int(SymbolInfoInteger(st_symbol, SYMBOL_SPREAD));
//---
  stakan_data.best_ask = SymbolInfoDouble(st_symbol, SYMBOL_ASK);
  stakan_data.best_bid = SymbolInfoDouble(st_symbol, SYMBOL_BID);
  stakan_data.ask_high = SymbolInfoDouble(st_symbol, SYMBOL_ASKHIGH);
  stakan_data.bid_high = SymbolInfoDouble(st_symbol, SYMBOL_BIDHIGH);
  stakan_data.ask_low = SymbolInfoDouble(st_symbol, SYMBOL_ASKLOW);
  stakan_data.bid_low = SymbolInfoDouble(st_symbol, SYMBOL_BIDLOW);
  stakan_data.last = SymbolInfoDouble(st_symbol, SYMBOL_LAST);
  stakan_data.last_high = SymbolInfoDouble(st_symbol, SYMBOL_LASTHIGH);
  stakan_data.last_low = SymbolInfoDouble(st_symbol, SYMBOL_LASTLOW);
  stakan_data.ses_volume = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_VOLUME);
  stakan_data.ses_turmover = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_TURNOVER);
  stakan_data.ses_buy_ord_volume = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_BUY_ORDERS_VOLUME); 
  stakan_data.ses_sell_ord_volume = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_SELL_ORDERS_VOLUME);
  stakan_data.ses_interest = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_INTEREST);
  stakan_data.ses_aw = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_AW);
//---Deals  TODO
  stakan_data.deals.buy_cnt = 0;
  stakan_data.deals.sell_cnt = 0;
  stakan_data.deals.sell_volume = 0;
  stakan_data.deals.buy_volume = 0;
  oper_time.end_count = GetMicrosecondCount();
  ulong delta_time = ulong(MathAbs(double(oper_time.end_count - oper_time.start_count)));
  int result = CopyTicksRange(st_symbol, ticks_array, COPY_TICKS_TRADE, oper_time.last_tick_time, oper_time.last_tick_time + delta_time);
  if(result > 0)
  {
    for(int i = 0; i < result; i++)
    {
      if((ticks_array[i].flags&TICK_FLAG_SELL)==TICK_FLAG_SELL) 
      { 
        stakan_data.deals.sell_cnt++;
        stakan_data.deals.sell_volume += long(ticks_array[i].volume);
      }
      else
      if((ticks_array[i].flags&TICK_FLAG_BUY)==TICK_FLAG_BUY) 
      { 
        stakan_data.deals.buy_cnt++;
        stakan_data.deals.buy_volume += long(ticks_array[i].volume); 
      }
    }
    oper_time.start_count = GetMicrosecondCount(); 
    oper_time.last_tick_time = ticks_array[0].time_msc + 1;
  }
}
//+------------------------------------------------------------------+
//| Get stakan data function                                         |
//+------------------------------------------------------------------+
STAKAN_DATA CStakan::GetStData(void)
{
  return(stakan_data);
}
 

Протестировал, всё работает отлично на каждом OnBookEven

получаем полную инфу о стакане, сделках и символе


 
prostotrader:

Вспомнил, я же писал Лента всех сделок

Спасибо всем

Работает точно и быстро

Желтым - изменения

oper_time.last_tick_time = ticks_array[0].time_msc + 1;

Нельзя добавлять +1, иначе будете пропускать сделки! 

 
Alexey Kozitsyn:

Нельзя добавлять +1, иначе будете пропускать сделки! 

Именно так нужно делать, чтобы не дублировать сделки.

Причина обращения: