Общие вопросы по разработке > Как в индикаторе из List убрать повторяющиеся числа

Общие вопросы по разработке в Альфа-Директ 4. Обсуждение разработки пользовательских индикаторов, стратегий.
Владимир
Сообщения: 84
Зарегистрирован: 14 ноя 2016, 02:17
Благодарил (а): 3 раза
Поблагодарили: 5 раз

Как в индикаторе из List убрать повторяющиеся числа

Непрочитанное сообщение Владимир » 31 мар 2019, 18:20

При написании индикатора (рисование уровней) столкнулся с проблемой как из List удалить повторяющиеся данные типа double. При сохранение в файл получаем (sber акции)
ListDouble [27 ] 208,2
ListDouble [26 ] 208,2
ListDouble [25 ] 208,2
ListDouble [24 ] 208,8
ListDouble [23 ] 208,96
ListDouble [22 ] 209,09
ListDouble [21 ] 208,51
ListDouble [20 ] 208,82
ListDouble [19 ] 209,04
ListDouble [18 ] 209,15
ListDouble [17 ] 208,96
ListDouble [16 ] 208,96
ListDouble [15 ] 209,15
ListDouble [14 ] 209,15
ListDouble [13 ] 209,19
ListDouble [12 ] 209,06
ListDouble [11 ] 209,3
ListDouble [10 ] 210,4
ListDouble [9 ] 210,4
ListDouble [8 ] 210,4
ListDouble [7 ] 210,1
ListDouble [6 ] 210
ListDouble [5 ] 210,66
ListDouble [4 ] 210,84
ListDouble [3 ] 211,2
ListDouble [2 ] 211,36
ListDouble [1 ] 211,36
ListDouble [0 ] 211,36

Код: Выделить всё

function Initialize()
{
// Обязательные параметры:
    IndicatorName = "Search_level";   // Задайте название индикатора и сохраните с данным именем
   PriceStudy = true;   // Рисовать в области цены (true – да, false – нет)
   AddInput("Input", Inputs.Candle);   // Input - входной ряд (Inputs.Price) или свечи (Inputs.Candle)

   AddSeries("Last1", DrawAs.Line, Color.Red);// Задаем вид линии индикатора с именем ряда Last
    AddLevel(0, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(1, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(2, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(3, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(4, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(5, Color.Red, LineStyles.Dot, 2, "Last");
   
   AddParameter("Period", 200);         // Период построения уровней
   AddParameter("min_number_of_repetitions", 2);         // Минимальное число повторения совпадений

   AddGlobalVariable("path", Types.String, @"c:\\dell\\Search_level.txt");
   AddGlobalVariable("ListDouble", Types.DoubleList);

}

function Evaluate()
//Владимир
{
    int Period_int =  Period-0;
    int min_number_of_repetitions_int =  min_number_of_repetitions-0;
    //int Xlene=0;
    double High;      // Переменная хранения значения хая для сравнения
    double Low;      // Переменная хранения значения хая для сравнения
    double Hx=0;
    double Lx=0;

if ((MaxIndex - CurrentIndex) > Period_int) return;

if (CurrentIndex == MaxIndex)
   {
using (System.IO.StreamWriter sw = System.IO.File.CreateText(path))
{
   

  for (var x= Period_int; x>0; x--)
   {
        High=Input.High[x];
      Low=Input.Low[x];
      int repetitions_high_int = 0;
      int repetitions_low_int = 0;

     for (var Scan=x-1; Scan>1; Scan--)
      {
         //Проверяем совпадения с хаем свечи
         if ((High==Input.High[Scan])||(High==Input.Low[Scan]) )
         {
            repetitions_high_int++;
            if (repetitions_high_int>=min_number_of_repetitions_int)
            {
            //sw.WriteLine("Патерн high  "  + Convert.ToString(x)+ " - " + Convert.ToString(Scan)+"  "+ Convert.ToString(High) );
            Hx=High;
               if(High>0)
               {
               ListDouble.Add(Hx);
               }
            }

         }
         //Проверяем совпадения с лоу свечи
         if ((Low==Input.High[Scan])||(Low==Input.Low[Scan]) )
         {
            repetitions_low_int++;
            if (repetitions_low_int>=min_number_of_repetitions_int)
            {
            //sw.WriteLine("Патерн  low " + Convert.ToString(x)+ " - " + Convert.ToString(Scan)+ "  " + Convert.ToString(Low));
            Lx=Low;
               if( Low>0)
               {
               ListDouble.Add(Lx);
               }
            }
         }
         
      } //for (var Scan=x-1; Scan>1; Scan--)
    }
   
   //Выполняем проверку на совпадение данных, заполняем новыми данными лист
/*foreach (int n in ListDouble)
{
    sw.WriteLine("ListDouble [" + Convert.ToString(n) + " ] "+ Convert.ToString(ListDouble[n]));
}
*/
            
            int n = (ListDouble.Count-1);
            while (n >-1)
                 { sw.WriteLine("ListDouble [" + Convert.ToString(n) + " ] " + Convert.ToString(ListDouble[n]));
                  n--;
               }
   
   sw.Close(); sw.Dispose();

}
Levels[0].Level = ListDouble[ListDouble.Count-6];
Levels[1].Level = ListDouble[ListDouble.Count-5];
Levels[2].Level = ListDouble[ListDouble.Count-4];
Levels[3].Level = ListDouble[ListDouble.Count-3];
Levels[4].Level = ListDouble[ListDouble.Count-2];
Levels[5].Level = ListDouble[ListDouble.Count-1];
}//off if (CurrentIndex == MaxIndex)

}



Подскажите как правильно удалять, повторяющиеся значения цены!

Владимир
Сообщения: 84
Зарегистрирован: 14 ноя 2016, 02:17
Благодарил (а): 3 раза
Поблагодарили: 5 раз

Re: Как в индикаторе из List убрать повторяющиеся числа

Непрочитанное сообщение Владимир » 31 мар 2019, 18:45

Суть индикатора поиск лимитного уровня по цене на графике. Согласно лекций Герчика цена образует некую формацию: БСУ (бар сформировавший уровень), БПУ1 (бар подтвердивший уровень), БПУ2 - они должны бить точно пункт в пункт. на графике выделил жёлтыми овалами.
Вложения
2.jpg

AP_Bor
Сообщения: 170
Зарегистрирован: 18 дек 2017, 08:18
Благодарил (а): 12 раз
Поблагодарили: 8 раз

Re: Как в индикаторе из List убрать повторяющиеся числа

Непрочитанное сообщение AP_Bor » 31 мар 2019, 20:05

Доброго времени суток, Владимир
Владимир » 31.03.2019, 18:20 писал(а):... List удалить повторяющиеся данные типа double ...
Примерно так.
► Показать
С наилучшими пожеланиями
Последний раз редактировалось AP_Bor 02 апр 2019, 18:30, всего редактировалось 1 раз.

Владимир
Сообщения: 84
Зарегистрирован: 14 ноя 2016, 02:17
Благодарил (а): 3 раза
Поблагодарили: 5 раз

Re: Как в индикаторе из List убрать повторяющиеся числа

Непрочитанное сообщение Владимир » 01 апр 2019, 14:43

Спасибо за совет! Переписал код.

Код: Выделить всё

function Initialize()
{
// Обязательные параметры:
    IndicatorName = "Search_level";   // Задайте название индикатора и сохраните с данным именем
   PriceStudy = true;   // Рисовать в области цены (true – да, false – нет)
   AddInput("Input", Inputs.Candle);   // Input - входной ряд (Inputs.Price) или свечи (Inputs.Candle)

   AddSeries("Last1", DrawAs.Line, Color.Red);// Задаем вид линии индикатора с именем ряда Last
    AddLevel(0, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(1, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(2, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(3, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(4, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(5, Color.Red, LineStyles.Dot, 2, "Last");
   
   AddParameter("Period", 200);         // Период построения уровней
   AddParameter("min_number_of_repetitions", 2);         // Минимальное число повторения совпадений

   AddGlobalVariable("path", Types.String, @"c:\\dell\\Search_level.txt");
   AddGlobalVariable("path2", Types.String, @"c:\\dell\\Search_level2.txt");
   
   AddGlobalVariable("ListDouble", Types.DoubleList);

}

function Evaluate()
//Владимир
{
    int Period_int =  Period-0;
    int min_number_of_repetitions_int =  min_number_of_repetitions-0;
    //int Xlene=0;
    double High;      // Переменная хранения значения хая для сравнения
    double Low;      // Переменная хранения значения хая для сравнения
    double Hx=0;
    double Lx=0;

if ((MaxIndex - CurrentIndex) > Period_int) return;

if (CurrentIndex == MaxIndex)
   {
   

  for (var x= Period_int; x>0; x--)
   {
        High=Input.High[x];
      Low=Input.Low[x];
      int repetitions_high_int = 0;
      int repetitions_low_int = 0;

     for (var Scan=x-1; Scan>1; Scan--)
      {
         //Проверяем совпадения с хаем свечи
         if ((High==Input.High[Scan])||(High==Input.Low[Scan]) )
         {
            repetitions_high_int++;
            if (repetitions_high_int>=min_number_of_repetitions_int)
            {
            Hx=High;
               if(High>0)
               {
               ListDouble.Add(Hx);
               }
            }

         }
         //Проверяем совпадения с лоу свечи
         if ((Low==Input.High[Scan])||(Low==Input.Low[Scan]) )
         {
            repetitions_low_int++;
            if (repetitions_low_int>=min_number_of_repetitions_int)
            {
            Lx=Low;
               if( Low>0)
               {
               ListDouble.Add(Lx);
               }
            }
         }
         
      } //for (var Scan=x-1; Scan>1; Scan--)
    }
   
   //Выполняем проверку на совпадение данных, заполняем новыми данными лист
    using (System.IO.StreamWriter sw = System.IO.File.CreateText(path))
      {

           int n = (ListDouble.Count-1);
           while (n >-1)
            { sw.WriteLine("ListDouble [" + Convert.ToString(n) + " ] " +Convert.ToString(ListDouble[n]));
                  n--;
            }
   
   sw.Close(); sw.Dispose();

      }
      
IEnumerable<double> ListDoubleDistinct = new List<double>(ListDouble).Distinct();           
      
    using (System.IO.StreamWriter sw = System.IO.File.CreateText(path2))
      {
          sw.WriteLine(string.Join("\n", ListDoubleDistinct ));
         sw.Close(); sw.Dispose();
      }
      
      
int n2 = (ListDoubleDistinct.Count()-1);      
      
Levels[0].Level = ListDoubleDistinct[n2-4];
Levels[1].Level = ListDouble[ListDouble.Count-4];
Levels[2].Level = ListDouble[ListDouble.Count-3];
Levels[3].Level = ListDouble[ListDouble.Count-2];
Levels[4].Level = ListDouble[ListDouble.Count-1];
Levels[5].Level = ListDouble[ListDouble.Count-0];
   
   }//off if (CurrentIndex == MaxIndex)
   
}




Но возникает другая проблема ошибка с обращением к элементу ListDoubleDistinct[n2-4];
Невозможность применить индексирование через [] к выражению типа System.Collections.Generic.IEnumerable<double> в строка
115
Поэтому нарисовать линию невозможно.

AP_Bor
Сообщения: 170
Зарегистрирован: 18 дек 2017, 08:18
Благодарил (а): 12 раз
Поблагодарили: 8 раз

Re: Как в индикаторе из List убрать повторяющиеся числа

Непрочитанное сообщение AP_Bor » 01 апр 2019, 16:17

Доброго времени суток, Владимир
Владимир » 01.04.2019, 14:43 писал(а):Невозможность применить индексирование через [] к выражению типа System.Collections.Generic.IEnumerable<double>
А если так.
► Показать
С наилучшими пожеланиями
Последний раз редактировалось AP_Bor 02 апр 2019, 18:29, всего редактировалось 1 раз.

Владимир
Сообщения: 84
Зарегистрирован: 14 ноя 2016, 02:17
Благодарил (а): 3 раза
Поблагодарили: 5 раз

Re: Как в индикаторе из List убрать повторяющиеся числа

Непрочитанное сообщение Владимир » 01 апр 2019, 19:33

Спасибо AP_Bor получилось в индикаторе удалить повторяющиеся числа. Теперь встаёт другой вопрос выбор нужных данных, удаление ненужных данных. И самое главное реальность автоматического построение адекватных линий поддержки сопротивления (уровней) от которых можно открывать сделки?

Код: Выделить всё

function Initialize()
{
// Обязательные параметры:
    IndicatorName = "Search_level";   // Задайте название индикатора и сохраните с данным именем
   PriceStudy = true;   // Рисовать в области цены (true – да, false – нет)
   AddInput("Input", Inputs.Candle);   // Input - входной ряд (Inputs.Price) или свечи (Inputs.Candle)

   AddSeries("Last1", DrawAs.Line, Color.Red);// Задаем вид линии индикатора с именем ряда Last
    AddLevel(0, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(1, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(2, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(3, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(4, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(5, Color.Red, LineStyles.Dot, 2, "Last");
   
   AddParameter("Period", 200);                     // Период построения уровней
   AddParameter("min_number_of_repetitions", 2);      // Минимальное число повторения совпадений

  // AddGlobalVariable("path", Types.String, @"c:\\dell\\Search_level.txt");
   AddGlobalVariable("ListDouble", Types.DoubleList);

}

function Evaluate()
//Владимир
{
    int Period_int =  Period-0;
    int min_number_of_repetitions_int =  min_number_of_repetitions-0;
    //int Xlene=0;
    double High;      // Переменная хранения значения хая для сравнения
    double Low;         // Переменная хранения значения хая для сравнения
    double Hx=0;
    double Lx=0;

if ((MaxIndex - CurrentIndex) > Period_int) return;

if (CurrentIndex == MaxIndex)
   {
   

  for (var x= Period_int; x>0; x--)
   {
        High=Input.High[x];
      Low=Input.Low[x];
      int repetitions_high_int = 0;
      int repetitions_low_int = 0;

     for (var Scan=x-1; Scan>1; Scan--)
      {
         //Проверяем совпадения с хаем свечи
         if ((High==Input.High[Scan])||(High==Input.Low[Scan]) )
         {
            repetitions_high_int++;
            if (repetitions_high_int>=min_number_of_repetitions_int)
            {
            Hx=High;
               if(High>0)
               {
               ListDouble.Add(Hx);
               }
            }

         }
         //Проверяем совпадения с лоу свечи
         if ((Low==Input.High[Scan])||(Low==Input.Low[Scan]) )
         {
            repetitions_low_int++;
            if (repetitions_low_int>=min_number_of_repetitions_int)
            {
            Lx=Low;
               if( Low>0)
               {
               ListDouble.Add(Lx);
               }
            }
         }
         
      } //for (var Scan=x-1; Scan>1; Scan--)
    }
   
   //Выполняем проверку на совпадение данных, заполняем новыми данными лист
   List<double> ListDoubleDistinct = new List<double>(ListDouble).Distinct().ToList();           
/*    using (System.IO.StreamWriter sw = System.IO.File.CreateText(path))
      {
          sw.WriteLine(string.Join("\n", ListDoubleDistinct ));
         sw.Close(); sw.Dispose();
      }*/
      
int n = (ListDoubleDistinct.Count()-1);      
Levels[0].Level = ListDoubleDistinct[n];
Levels[1].Level = ListDoubleDistinct[n-1];
Levels[2].Level = ListDoubleDistinct[n-2];
Levels[3].Level = ListDoubleDistinct[n-3];
Levels[4].Level = ListDoubleDistinct[n-4];
Levels[5].Level = ListDoubleDistinct[n-5];
   
   }//off if (CurrentIndex == MaxIndex)
   
}




AP_Bor
Сообщения: 170
Зарегистрирован: 18 дек 2017, 08:18
Благодарил (а): 12 раз
Поблагодарили: 8 раз

Re: Как в индикаторе из List убрать повторяющиеся числа

Непрочитанное сообщение AP_Bor » 01 апр 2019, 20:48

Доброго времени суток, Владимир
Владимир » 01.04.2019, 19:33 писал(а):... выбор нужных данных, удаление ненужных данных ...
Примерно так.
► Показать
С наилучшими пожеланиями

Владимир
Сообщения: 84
Зарегистрирован: 14 ноя 2016, 02:17
Благодарил (а): 3 раза
Поблагодарили: 5 раз

Re: Как в индикаторе из List убрать повторяющиеся числа

Непрочитанное сообщение Владимир » 02 апр 2019, 22:09

Доброго времени суток AP_Bor! Дело в том, что такая концепция брать значения (больше чем средне арифметическое значение) просто неприемлема для торговли. Торговля это не математическая формула, а поиск смещения вероятностей в ту или иную сторону. Как мне кажется таким подходом можно выкинуть нужный уровень и просто не заметить нужный патэрн вовремя. Но при этом ждать события в другом месте. Автоматизированный поиск точек (цены), за период 200 бар на 5 минутках по сберу, с фильтром менее 2 совпадений по цены - даёт 32 совпадения. А с фильтром менее 2 совпадений по цены - даёт 17. Это с учётом, что на акциях в дне 103 бара то получается 2 дня. По методике торговли А. Герчик говорил говорил на 5 минутке берём для анализа 2-3 дня. Обычно на глаз определяя получается 2-3 линии! Торговому роботу нужны конкретные цифры. Тут как вариант использовать все значения и фильтровать их по следующему алгоритму:

1 Нас интересует в принципе последние 3-5 свечей в зависимости от торгуемого патэрна (хотя воздушный уровень можно отбросить тогда 3 последних бара), 0 - бар который рисуется не считаем.
2 БСУ лежит на одном из найденных значений
3 БПУ1 - свеча[3] (останавливается точно на уровне чай лоу), БПУ2 свеч[2] (может не добивать на размер люфта, но при этом не пересекать уровень), БПУ3 свеч[1] (останавливается точно на уровне чай лоу).
4 Решение на открытие позиции.
Поэтому может нужен обратный алгоритм в индикаторе.
Надо подумать над алгоритмом поиск совпадений БПУ1 - свеча[3] и БПУ3 - свеча[1] встречалось или раньше, потом проверить свечку БПУ2 - свеча[2].
Но тут косяк может выплыть другой запаздывание робота на открытие позиции.

AP_Bor
Сообщения: 170
Зарегистрирован: 18 дек 2017, 08:18
Благодарил (а): 12 раз
Поблагодарили: 8 раз

Re: Как в индикаторе из List убрать повторяющиеся числа

Непрочитанное сообщение AP_Bor » 03 апр 2019, 07:37

Доброго времени суток, Владимир
Владимир » 02.04.2019, 22:09 писал(а):... такая концепция ... просто неприемлема для торговли ...
Уважаемый, Владимир, возможно, у вас сложилось ложное мнение о моём интересе к алгоритму Герчика, моя помощь ограничивалась примерами синтаксиса C#.
Владимир » 02.04.2019, 22:09 писал(а):... Торговля это не математическая формула, а поиск смещения вероятностей ...
У меня, немного, другое мнение.
Торговля - процесс обеспечение оборота, для получения прибыли.
Поиск - математическая задача.
Спасибо, за развернутое описание вашего понимания алгоритма, но на его программную реализацию, извините, нет времени.
С наилучшими пожеланиями

Владимир
Сообщения: 84
Зарегистрирован: 14 ноя 2016, 02:17
Благодарил (а): 3 раза
Поблагодарили: 5 раз

Re: Как в индикаторе из List убрать повторяющиеся числа

Непрочитанное сообщение Владимир » 28 апр 2019, 22:58

Добавил сортировку и перерисовку уровней рядом с ценой.

Код: Выделить всё

function Initialize()
{
// Обязательные параметры:
    IndicatorName = "Search_level";   // Задайте название индикатора и сохраните с данным именем
   PriceStudy = true;   // Рисовать в области цены (true – да, false – нет)
   AddInput("Input", Inputs.Candle);   // Input - входной ряд (Inputs.Price) или свечи (Inputs.Candle)

   AddSeries("Last1", DrawAs.Line, Color.Red);// Задаем вид линии индикатора с именем ряда Last
    AddLevel(0, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(1, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(2, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(3, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(4, Color.Red, LineStyles.Dot, 2, "Last");
    AddLevel(5, Color.Red, LineStyles.Dot, 2, "Last");
   
   AddParameter("Period", 200);                     // Период построения уровней
   AddParameter("min_number_of_repetitions", 2);      // Минимальное число повторения совпадений

   AddGlobalVariable("path", Types.String, @"c:\\dell\\Search_level.txt");
   AddGlobalVariable("ListDouble", Types.DoubleList);

}

function Evaluate()
//Владимир
{
    int Period_int =  Period-0;
    int min_number_of_repetitions_int =  min_number_of_repetitions-0;
    //int Xlene=0;
    double High;      // Переменная хранения значения хая для сравнения
    double Low;         // Переменная хранения значения хая для сравнения
    double Hx=0;
    double Lx=0;

if ((MaxIndex - CurrentIndex) > Period_int) return;

if (CurrentIndex == MaxIndex)
   {
   

  for (var x= Period_int; x>0; x--)
   {
        High=Input.High[x];
      Low=Input.Low[x];
      int repetitions_high_int = 0;
      int repetitions_low_int = 0;

     for (var Scan=x-1; Scan>1; Scan--)
      {
         //Проверяем совпадения с хаем свечи
         if ((High==Input.High[Scan])||(High==Input.Low[Scan]) )
         {
            repetitions_high_int++;
            if (repetitions_high_int>=min_number_of_repetitions_int)
            {
            Hx=High;
               if(High>0)
               {
               ListDouble.Add(Hx);
               }
            }

         }
         //Проверяем совпадения с лоу свечи
         if ((Low==Input.High[Scan])||(Low==Input.Low[Scan]) )
         {
            repetitions_low_int++;
            if (repetitions_low_int>=min_number_of_repetitions_int)
            {
            Lx=Low;
               if( Low>0)
               {
               ListDouble.Add(Lx);
               }
            }
         }
         
      } //for (var Scan=x-1; Scan>1; Scan--)
    }
   
   //Выполняем проверку на совпадение данных, заполняем новыми данными лист
   List<double> ListDoubleDistinct = new List<double>(ListDouble).Distinct().ToList();   
   //Выполняем сортировку по возрастанию
   ListDoubleDistinct.Sort();
   //Выполняем поиск ближайших значений к цене
      int i = 0, i2=0, i3=0;
         while (Input.Open[0]>=ListDoubleDistinct[i])
            {
                i2 = i;
                i++;
            }
      //Определяем ближайшие линии (3 с верху и 3 снизу, если они есть)
            if(i2>=3 && i2<= ListDoubleDistinct.Count-4)
            { i3 = i2; }
            else
            {
                if (i2 < 3) { i3 = 3; }
                if (i2 > ListDoubleDistinct.Count - 4) { i3 = ListDoubleDistinct.Count - 3; }
            }
         
    using (System.IO.StreamWriter sw = System.IO.File.CreateText(path))
      {
          sw.WriteLine(string.Join("\r\n", ListDoubleDistinct ));
         sw.Close(); sw.Dispose();
      }
     
Levels[0].Level = ListDoubleDistinct[i3-3];
Levels[1].Level = ListDoubleDistinct[i3-2];
Levels[2].Level = ListDoubleDistinct[i3-1];
Levels[3].Level = ListDoubleDistinct[i3];
Levels[4].Level = ListDoubleDistinct[i3+1];
Levels[5].Level = ListDoubleDistinct[i3+2];   
   }//off if (CurrentIndex == MaxIndex)
   
}





Вернуться в «Общие вопросы по разработке»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 14 гостей