Поток сделок драматически отстает от соответствующего движения в стакане. Почему?


Поток сделок драматически отстает от соответствующего движения в стакане. Почему?
Atom
10/21/2012


Помогите разобраться с очень странным эффектом.
Веду записи с помощью гидры потоков сделок и изменений стаканов по нескольким инструментам.

Затем решил построить график во времени, наложив лучшие Ask-Bid и фактические сделки.

Я обнаружил, что поток сделок (по таблице всех сделок) очень сильно запаздывает по отношению к потоку стаканов.
Получилось, что для сбербанка запаздывание потока сделок составляет 15-20 секунд, для индекса RTS –примерно 35 секунд!
Смотрите сами прикрепленные картинки. Я постарался подобрать наиболее наглядные фрагменты, но общая картина почти не меняется во времени.
(Синий – лучший Bid, Зеленый – лучший Ask, Красный – фактические сделки)

1. Сбербанк за 2012-10-18, фрагмент


2. Фьючерс на индекс РТС за 2012-10-19, фрагмент


Все данные я получал от своего брокера.

Стаканы, очевидно, маркируются локальным временем моего компьютера, которое я перед сессией синхронизирую через интернет-тайм. Т.е. записывается компьютерное время изменения стакана в момент его изменения, когда информация об изменении УЖЕ дошла до моего терминала.

Сделки построены по времени Time в структуре Trade, как я понимаю, это должно быть время по часам биржи.
Первая мысль была – мой брокер шалит и выдает поток сделок с запаздыванием. Но я проверил поток сделок, сравнив их с данными с www.finam.ru. Поток моего брокера не отличается абсолютно. Если построить графики по сделкам по данным от финама и от моего брокера, то они совпадут пиксель-в-пиксель (что само по себе меня несколько удивило – я думал, что незначительная разница может быть)

И так, наблюдается абсолютно стабильный эффект, поток сделок отстает на несколько десятков секунд от соответствующего движения в стаканах.

Вопрос к знатокам – как такое может быть и с чем это может быть связано?


Tags:


Thanks:


Mikhail Sukhov

Avatar
Date: 10/22/2012
Reply


DrChemist Go to

Я обнаружил, что поток сделок (по таблице всех сделок) очень сильно запаздывает по отношению к потоку стаканов.
Получилось, что для сбербанка запаздывание потока сделок составляет 15-20 секунд, для индекса RTS –примерно 35 секунд!


Думаю тут причины в:

1. Неправильном расчете задержки.
2. Сильно прореженных стаканах от Квика.
Thanks:

DrChemist

Avatar
Date: 10/23/2012
Reply


Попробую по пунктам:
1. Очень может быть. Но где?

1.1. Как я уже писал, график сделок совпадает пиксель-в-пиксель с графиком, построенным по данным Финам. Причем график по данным Финам я строю, не используя вообще средства S# (напрямую читаю данные из текстового файла в Матлабе). Таким образом, тиковому графику по сделкам можно верить.

1.2. Часы моего компьютера устанавливаются по интернет-тайм непосредственно перед торговой сессией, т.е. можно рассчитывать на точностью в несколько десятков – единицы сотен миллисекунд. На всякий случай проверяю по телефону 100 – все ОК.

1.3. Я сделал свое собственное протоколирование потока данных. Для сделок, помимо Trade.Time и Trade.Latency записывается время по компьютерным часам на момент поступления информации о сделке. Результат: в устоявшемся режиме время по компьютерным часам примерно на 0.5 секунды МЕНЬШЕ времени Trade.Time. Объясняется это, скорее всего, тем, что Trade.Time квик или биржа округляет до секунды, плюс еще погрешность установки компьютерных часов. В общем, все в порядке.

1.4. Поток стаканов также протоколирую собственными средствами. Записываю время по компьютерным часам на момент поступления информации (в моей собственной функции) и время MarketDepth .LastChangeTime. Результат: эти два времени совпадают абсолютно. Так, собственно, и должно быть. Таким образом, со входящими потоками проблем вроде бы нет.

1.5. Для построения графиков данные беру из хранилища Гидры (закачку из своих собственных протоколов не делал – руки не дошли). Наблюдаем картинки, показанные выше.

1.6. Я могу представить себе следующие варианты объяснения (в порядке от наиболее вероятной к наименее вероятной)

1.6.1. Неправильно записывается или читается время MarketDepth .LastChangeTime. Т.е. в результате цикла сохранение – чтение LastChangeTime получает отрицательное смещение секунд в 10. Вопрос к разработчикам – может ли такое быть. Ведь LastChangeTime это – свойство, с некоторой реализацией “get”. Не может ли оказаться, что именно эта реализация дает такое смещение? Проверить это предположение можно, но очень муторно – фактически, нужно организовывать альтернативное хранилище по отношению к гидре. Поэтому хотелось бы сначала получить комментарии от разработчика.

1.6.2. Прочие мыслимые «программные» глюки вроде бы проверил и исключил, как в Матлабе так и в компоненте, использующем S#.

1.6.3. Технология формирования сделки на бирже. Сначала изымаются встречные заявки из стаканов. Затем происходят внутренние транзакции по оформлению сделки, которые занимают прядка 10 секунд. И только затем уведомление о заключенной сделке рассылается по биржевым каналам. Я про такие, гигантские по сути, биржевые задержки нигде не слышал. Может ли такое быть?

1.6.4. Мой брокер использует временной портал секунд в 10 в будущее, и берет через него будущие стаканы и милостиво рассылает их своим клиентам. Поток сделок же идет нормальным образом. В таком случае цены нет моему брокеру.

2. Честно говоря, не понял, как разреженность стакана может быть связана с эффектом отставания тиковых данных от стакана.
Thanks:

Mikhail Sukhov

Avatar
Date: 10/24/2012
Reply


DrChemist Go to

1.6.1. Неправильно записывается или читается время MarketDepth .LastChangeTime. Т.е. в результате цикла сохранение – чтение LastChangeTime получает отрицательное смещение секунд в 10. Вопрос к разработчикам – может ли такое быть. Ведь LastChangeTime это – свойство, с некоторой реализацией “get”. Не может ли оказаться, что именно эта реализация дает такое смещение? Проверить это предположение можно, но очень муторно – фактически, нужно организовывать альтернативное хранилище по отношению к гидре. Поэтому хотелось бы сначала получить комментарии от разработчика.


http://stocksharp.com/do...0-a7c7-0979dba1ad0a.htm

DrChemist Go to
2. Честно говоря, не понял, как разреженность стакана может быть связана с эффектом отставания тиковых данных от стакана.


Не разреженность, а прореженность... В секунду происходит около сотни регистраций и снятий. Естественно, квик, да еще и в агрегированном состоянии, такое количество изменений просто не будет транслировать. Может быть такая ситуация, что матчинговый снимок стакана просто не был передан в программу, и последующий рематчинг произошел со сдвигом только за счет принципа mean reversion
Thanks:

DrChemist

Avatar
Date: 10/25/2012
Reply


В общем , повозился и выяснил.
Это – БАГ.
То ли в гидре то ли еще где в недрах C#
Проверял Так.
Модифицировал Гидру, а именно ф-ию OnMarketDepthsChanged. Смысл в том, что я создаю свой текстовый файл, в который пишу стаканы для "SBER@EQBR" и RIZ2@RTS". При этом запоминаю момент, когда пришло управление в ф-ию OnMarketDepthsChanged() и записываю это время в свой файл вместе со временем MarketDepth.LastChangeTime.
Как видно из дальнейших логов эти два времени в точности совпадают.
Прочая функциональность гидры осталась без изменений:

Code

		private void OnMarketDepthsChanged(IEnumerable<MarketDepth> depths)
		{
            DateTime dtCur = DateTime.Now;
            depths.ForEach(d =>
			{
				//Trace.WriteLine("MDA " +dc.Security+":"+dc._DebugId+":"+ dc.LastChangeTime.ToString("HHmmss.fff"));
				if (d.Bids.Length > 0 || d.Asks.Length > 0)
				{
					_depthsBuffer.Add(d.Security, d.Clone());
				}
			});

            if (null != sm_FlogDepth)
            {
                var testDepth = depths.Where(dtst => ((dtst.Security.Id == "SBER@EQBR") || (dtst.Security.Id == "RIZ2@RTS")));
                foreach (MarketDepth myD in testDepth)
                {
                    if (myD.Bids.Length > 0 || myD.Asks.Length > 0)
                    {
                        sm_FlogDepth.Write(myD.Security.Id);
                        sm_FlogDepth.Write(",");

                        sm_FlogDepth.Write(dtCur.ToString());
                        sm_FlogDepth.Write(".");
                        sm_FlogDepth.Write(dtCur.Millisecond);
                        sm_FlogDepth.Write(",");

                        sm_FlogDepth.Write(myD.LastChangeTime.ToString());
                        sm_FlogDepth.Write(".");
                        sm_FlogDepth.Write(myD.LastChangeTime.Millisecond);
                        sm_FlogDepth.Write(",");

                        sm_FlogDepth.Write(myD.Latency.ToString());
                        sm_FlogDepth.Write(",");

                        sm_FlogDepth.Write(myD.Asks[0].Price);
                        sm_FlogDepth.Write("(");
                        sm_FlogDepth.Write(myD.Asks[0].Volume);
                        sm_FlogDepth.Write(")-");
                        sm_FlogDepth.Write(myD.Bids[0].Price);
                        sm_FlogDepth.Write("(");
                        sm_FlogDepth.Write(myD.Bids[0].Volume);
                        sm_FlogDepth.Write(")");
                        sm_FlogDepth.WriteLine();
                    }
                }

                if (testDepth.Any())
                    sm_FlogDepth.Flush();
            }

		}


Далее читаю записанные данные, сохраненные гидрой. Для этого использовал доработанный вариант SampleStorage. Вот полный код:

Code

namespace SampleStorage
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using Ecng.Common;
    using StockSharp.Algo.Storages;
    using StockSharp.Algo.Testing;
    using StockSharp.BusinessEntities;

    class Program
    {
        struct depthDebugView
        {
            public DateTime LastChangeTime;
            public Quote BestAsk;
            public Quote BestBid;
            public depthDebugView(MarketDepth t)
            {
                LastChangeTime = t.LastChangeTime;
                BestAsk = t.BestAsk;
                BestBid = t.BestBid;
            }
            //     Получить строковое представление.
            public override string ToString()
            {
                return LastChangeTime.ToString() + "." + LastChangeTime.Millisecond.ToString() + ","
                    + BestAsk.Price.ToString() + "(" + BestAsk.Volume.ToString() + ")"
                    + "-" + BestBid.Price.ToString() + "(" + BestBid.Volume.ToString() + ")";
            }
        };

        static void Main()
        {
            // создаем тестовый инструмент
            var security = new Security
            {
                Id = "RIZ2@RTS",
            };

            var storage = new StorageRegistry();

            string dPATH = "E:\\TradeDB\\Hydra\\Data";

            // получаем хранилище для тиковых сделок
            var tradeStorage = storage.GetTradeStorage(security, new LocalMarketDataDrive(dPATH));

            // загружаем сделки
            DateTime dtDayToLoad = new DateTime(2012, 10, 24);
            var trList = tradeStorage.Load(dtDayToLoad);


            // получаем хранилище для стаканов
            var depthStorage = storage.GetMarketDepthStorage(security, new LocalMarketDataDrive(dPATH));

            // загружаем стаканы
            var depthData = depthStorage.Load(dtDayToLoad);
            // Фикс проблемы со временем - всем элементам принудительно ставим DateTimeKind.Local
            // Иначе - где-то Local, а где-то Unspecified => проблемы с конвертацией
            foreach (MarketDepth t in depthData)
            {
                t.LastChangeTime = DateTime.SpecifyKind(t.LastChangeTime, DateTimeKind.Local);
            }
            //Берем нужный кусок данных из загруженных стаканов
            DateTime dtLoadFromTime = new DateTime(2012, 10, 24, 15, 48, 30, DateTimeKind.Local);
            DateTime dtLoadToTime = new DateTime(2012, 10, 24, 15, 50, 00, DateTimeKind.Local);
            var depthData2 = depthData.Where(d => dtLoadFromTime <= d.LastChangeTime && d.LastChangeTime <= dtLoadToTime);

            var depthDebug = depthData2.Select(d => new depthDebugView(d));
            //Здесь ставим брякпойнт и наблюдаем содержимое depthDebug
            //Сравниваем его с сохраненным ранее логом от модифицированной Гидры

            Console.WriteLine("Количество загруженных сделок: " + trList.Count());
            foreach (Trade t in trList)
            {
                Console.WriteLine(t.Time + "(" + t.Latency.TotalSeconds + "c) : " + t.Price + " : " + t.Volume + "( " + t.OrderDirection + " )");
                System.Threading.Thread.Sleep(32);

            }

            foreach (MarketDepth t in depthData2)
            {
                Console.WriteLine(t.LastChangeTime + " : " + t.BestBid.Price + " - " + t.BestAsk.Price + " : " + (t.BestBid.Volume - t.BestAsk.Volume));
                System.Threading.Thread.Sleep(32);

            }

            Console.ReadLine();
        }
    }
}


И так, результат.

Вот, что реально приходило в OnMarketDepthsChanged() и должно было быть сохранено. Данные по моим собственным логам:

Code

RIZ2@RTS,2012-10-24 15:49:01.424,2012-10-24 15:49:01.424,-04:00:00,145750(38)-145740(4)
RIZ2@RTS,2012-10-24 15:49:01.658,2012-10-24 15:49:01.658,-04:00:00,145750(35)-145740(4)
RIZ2@RTS,2012-10-24 15:49:01.736,2012-10-24 15:49:01.736,-04:00:00,145750(4)-145740(7)
RIZ2@RTS,2012-10-24 15:49:01.970,2012-10-24 15:49:01.970,-04:00:00,145760(36)-145750(4)
RIZ2@RTS,2012-10-24 15:49:02.158,2012-10-24 15:49:02.158,-04:00:00,145750(3)-145740(4)
RIZ2@RTS,2012-10-24 15:49:02.392,2012-10-24 15:49:02.392,-04:00:00,145750(3)-145740(7)
RIZ2@RTS,2012-10-24 15:49:02.516,2012-10-24 15:49:02.516,-04:00:00,145760(26)-145740(6)
RIZ2@RTS,2012-10-24 15:49:02.719,2012-10-24 15:49:02.719,-04:00:00,145750(3)-145740(14)
RIZ2@RTS,2012-10-24 15:49:02.922,2012-10-24 15:49:02.922,-04:00:00,145750(7)-145740(13)
RIZ2@RTS,2012-10-24 15:49:03.156,2012-10-24 15:49:03.156,-04:00:00,145750(7)-145740(3)
RIZ2@RTS,2012-10-24 15:49:03.296,2012-10-24 15:49:03.296,-04:00:00,145750(7)-145740(3)
RIZ2@RTS,2012-10-24 15:49:03.484,2012-10-24 15:49:03.484,-04:00:00,145750(7)-145740(22)


А вот, что было затем прочитано из хранилища:

Code

+		[32]	{2012-10-24 15:48:37.436,145750(38)-145740(4)}	SampleStorage.Program.depthDebugView
+		[33]	{2012-10-24 15:48:37.670,145750(35)-145740(4)}	SampleStorage.Program.depthDebugView
+		[34]	{2012-10-24 15:48:37.748,145750(4)-145740(7)}	SampleStorage.Program.depthDebugView
+		[35]	{2012-10-24 15:48:37.982,145760(36)-145750(4)}	SampleStorage.Program.depthDebugView
+		[36]	{2012-10-24 15:48:38.169,145750(3)-145740(4)}	SampleStorage.Program.depthDebugView
+		[37]	{2012-10-24 15:48:38.403,145750(3)-145740(7)}	SampleStorage.Program.depthDebugView
+		[38]	{2012-10-24 15:48:38.527,145760(26)-145740(6)}	SampleStorage.Program.depthDebugView
+		[39]	{2012-10-24 15:48:38.729,145750(3)-145740(14)}	SampleStorage.Program.depthDebugView
+		[40]	{2012-10-24 15:48:38.931,145750(7)-145740(13)}	SampleStorage.Program.depthDebugView
+		[41]	{2012-10-24 15:48:39.165,145750(7)-145740(3)}	SampleStorage.Program.depthDebugView
+		[42]	{2012-10-24 15:48:39.305,145750(7)-145740(3)}	SampleStorage.Program.depthDebugView
+		[43]	{2012-10-24 15:48:39.492,145750(7)-145740(22)}	SampleStorage.Program.depthDebugView

************************************


+		[154]	{2012-10-24 15:49:01.425,145800(1)-145790(16)}	SampleStorage.Program.depthDebugView
+		[155]	{2012-10-24 15:49:01.612,145810(31)-145790(16)}	SampleStorage.Program.depthDebugView
+		[156]	{2012-10-24 15:49:01.861,145810(29)-145790(16)}	SampleStorage.Program.depthDebugView
+		[157]	{2012-10-24 15:49:01.985,145810(30)-145790(19)}	SampleStorage.Program.depthDebugView
+		[158]	{2012-10-24 15:49:02.219,145810(29)-145790(18)}	SampleStorage.Program.depthDebugView
+		[159]	{2012-10-24 15:49:02.421,145810(31)-145790(18)}	SampleStorage.Program.depthDebugView
+		[160]	{2012-10-24 15:49:02.686,145880(1)-145820(14)}	SampleStorage.Program.depthDebugView
+		[161]	{2012-10-24 15:49:02.748,145830(10)-145820(14)}	SampleStorage.Program.depthDebugView
+		[162]	{2012-10-24 15:49:02.997,145880(19)-145870(2)}	SampleStorage.Program.depthDebugView
+		[163]	{2012-10-24 15:49:03.75,145990(72)-145880(2)}	SampleStorage.Program.depthDebugView
+		[164]	{2012-10-24 15:49:03.246,145950(5)-145900(5)}	SampleStorage.Program.depthDebugView
+		[165]	{2012-10-24 15:49:03.292,145950(10)-145890(4)}	SampleStorage.Program.depthDebugView


Обратите внимание на времена и значения Асков-Бидов. Все прочитанные данные получили отрицательное смещение по времени на ~14 секунд.
Thanks:

Mikhail Sukhov

Avatar
Date: 10/25/2012
Reply


Да, интересная ситуация. Думаю стоит выводить еще и DateTime.Ticks, так как Kind не понятно как работает.
Thanks:

DrChemist

Avatar
Date: 10/26/2012
Reply


Ну вот.
Проверил с тиками. Вывод – с тиками все те же проблемы.

Чтобы не смущал DateTime.SpecifyKind() – я его на этот раз убрал.

Вот результат по моим логам из гидры:
Code

RIZ2@RTS,2012-10-25 23:39:00.199(634868051401998580),2012-10-25 23:39:00.199(634868051401998580),-04:00:00,145190(47)-145170(5)
RIZ2@RTS,2012-10-25 23:39:00.409(634868051404098701),2012-10-25 23:39:00.409(634868051404098701),-04:00:00,145180(4)-145170(3)
RIZ2@RTS,2012-10-25 23:39:00.610(634868051406108816),2012-10-25 23:39:00.610(634868051406108816),-04:00:00,145180(8)-145160(6)
RIZ2@RTS,2012-10-25 23:39:00.816(634868051408168933),2012-10-25 23:39:00.812(634868051408128931),-04:00:00,145220(13)-145170(5)
RIZ2@RTS,2012-10-25 23:39:01.12(634868051410129046),2012-10-25 23:39:01.11(634868051410119045),-04:00:00,145220(12)-145180(24)
RIZ2@RTS,2012-10-25 23:39:01.149(634868051411499124),2012-10-25 23:39:01.149(634868051411499124),-04:00:00,145220(3)-145210(6)
RIZ2@RTS,2012-10-25 23:39:01.341(634868051413419234),2012-10-25 23:39:01.341(634868051413419234),-04:00:00,145220(2)-145210(29)
RIZ2@RTS,2012-10-25 23:39:01.549(634868051415499353),2012-10-25 23:39:01.549(634868051415499353),-04:00:00,145230(11)-145220(2)
RIZ2@RTS,2012-10-25 23:39:01.749(634868051417499467),2012-10-25 23:39:01.748(634868051417489466),-04:00:00,145230(10)-145220(3)
RIZ2@RTS,2012-10-25 23:39:01.952(634868051419529583),2012-10-25 23:39:01.951(634868051419519583),-04:00:00,145230(18)-145210(9)


Вот результаты считывания данных:
Code

+		 [6]	{2012-10-25 23:38:56.427(634868051364270000),145190(47)-145170(5)}	SampleStorage.Program.depthDebugView
+		 [7]	{2012-10-25 23:38:56.637(634868051366370000),145180(4)-145170(3)}	SampleStorage.Program.depthDebugView
+		 [8]	{2012-10-25 23:38:56.838(634868051368380000),145180(8)-145160(6)}	SampleStorage.Program.depthDebugView
+		 [9]	{2012-10-25 23:38:57.40(634868051370400000),145220(13)-145170(5)}	SampleStorage.Program.depthDebugView
+		[10]	{2012-10-25 23:38:57.239(634868051372390000),145220(12)-145180(24)}	SampleStorage.Program.depthDebugView
+		[11]	{2012-10-25 23:38:57.377(634868051373770000),145220(3)-145210(6)}	SampleStorage.Program.depthDebugView
+		[12]	{2012-10-25 23:38:57.569(634868051375690000),145220(2)-145210(29)}	SampleStorage.Program.depthDebugView
+		[13]	{2012-10-25 23:38:57.777(634868051377770000),145230(11)-145220(2)}	SampleStorage.Program.depthDebugView
+		[14]	{2012-10-25 23:38:57.976(634868051379760000),145230(10)-145220(3)}	SampleStorage.Program.depthDebugView
+		[15]	{2012-10-25 23:38:58.179(634868051381790000),145230(18)-145210(9)}	SampleStorage.Program.depthDebugView
****************************************************************
+		[24]	{2012-10-25 23:38:59.964(634868051399640000),145200(12)-145180(32)}	SampleStorage.Program.depthDebugView
+		[25]	{2012-10-25 23:39:00.173(634868051401730000),145200(10)-145190(19)}	SampleStorage.Program.depthDebugView
+		[26]	{2012-10-25 23:39:00.377(634868051403770000),145200(12)-145190(19)}	SampleStorage.Program.depthDebugView
+		[27]	{2012-10-25 23:39:00.578(634868051405780000),145200(12)-145190(19)}	SampleStorage.Program.depthDebugView
+		[28]	{2012-10-25 23:39:00.794(634868051407940000),145200(4)-145190(19)}	SampleStorage.Program.depthDebugView
+		[29]	{2012-10-25 23:39:00.981(634868051409810000),145200(4)-145190(19)}	SampleStorage.Program.depthDebugView
+		[30]	{2012-10-25 23:39:01.188(634868051411880000),145200(4)-145190(19)}	SampleStorage.Program.depthDebugView
+		[31]	{2012-10-25 23:39:01.394(634868051413940000),145200(1)-145190(19)}	SampleStorage.Program.depthDebugView
+		[32]	{2012-10-25 23:39:01.599(634868051415990000),145200(1)-145190(19)}	SampleStorage.Program.depthDebugView
+		[33]	{2012-10-25 23:39:01.788(634868051417880000),145210(15)-145190(23)}	SampleStorage.Program.depthDebugView
+		[34]	{2012-10-25 23:39:01.803(634868051418030000),145210(11)-145190(27)}	SampleStorage.Program.depthDebugView
+		[35]	{2012-10-25 23:39:01.995(634868051419950000),145210(16)-145190(2)}	SampleStorage.Program.depthDebugView
+		[36]	{2012-10-25 23:39:02.203(634868051422030000),145200(9)-145190(2)}	SampleStorage.Program.depthDebugView
+		[37]	{2012-10-25 23:39:02.406(634868051424060000),145200(4)-145190(21)}	SampleStorage.Program.depthDebugView



Сдвиг по времени меньше, чем в прошлые разы, но все равно значительный. У меня сложилось впечатление, что сдвиг зависит от времени работы Гидры – ошибка накапливается в процессе работы. В этот раз гидра работала всего 1.5 часа в вечернюю сессию, ранее - по 8 часов в дневную.


Несколько слов о заплатке с DateTime.SpecifyKind(), которую я использовал в прошлый раз. Это -обход другой проблемы со временем. При считывании данных стаканов значение LastChangeTime.Kind может оказаться или Local или Unspecified. От чего это зависит – проще разобраться самим разработчикам. В общем-то это – тоже бага.
Разные LastChangeTime.Kind могут по-разному обрабатываются всякими функциями преобразования времени в другие форматы. В частности, перестают нормально работать ф-ии сравнения времени больше-меньше. Без моего фикса картинки были совсем веселые.

DateTime.SpecifyKind() меняет только поле типа времени и не трогает остальные значения.

Мои коды:
Code

		private void OnMarketDepthsChanged(IEnumerable<MarketDepth> depths)
		{
            DateTime dtCur = DateTime.Now;
            depths.ForEach(d =>
			{
				//Trace.WriteLine("MDA " +dc.Security+":"+dc._DebugId+":"+ dc.LastChangeTime.ToString("HHmmss.fff"));
				if (d.Bids.Length > 0 || d.Asks.Length > 0)
				{
					_depthsBuffer.Add(d.Security, d.Clone());
				}
			});

            if (null != sm_FlogDepth)
            {
                var testDepth = depths.Where(dtst => ((dtst.Security.Id == "SBER@EQBR") || (dtst.Security.Id == "RIZ2@RTS")));
                foreach (MarketDepth myD in testDepth)
                {
                    if (myD.Bids.Length > 0 || myD.Asks.Length > 0)
                    {
                        sm_FlogDepth.Write(myD.Security.Id);
                        sm_FlogDepth.Write(",");

                        sm_FlogDepth.Write(dtCur.ToString());
                        sm_FlogDepth.Write(".");
                        sm_FlogDepth.Write(dtCur.Millisecond);
                        sm_FlogDepth.Write("(");
                        sm_FlogDepth.Write(dtCur.Ticks.ToString());
                        sm_FlogDepth.Write("),");

                        sm_FlogDepth.Write(myD.LastChangeTime.ToString());
                        sm_FlogDepth.Write(".");
                        sm_FlogDepth.Write(myD.LastChangeTime.Millisecond);
                        sm_FlogDepth.Write("(");
                        sm_FlogDepth.Write(myD.LastChangeTime.Ticks.ToString());
                        sm_FlogDepth.Write("),");

                        sm_FlogDepth.Write(myD.Latency.ToString());
                        sm_FlogDepth.Write(",");

                        sm_FlogDepth.Write(myD.Asks[0].Price);
                        sm_FlogDepth.Write("(");
                        sm_FlogDepth.Write(myD.Asks[0].Volume);
                        sm_FlogDepth.Write(")-");
                        sm_FlogDepth.Write(myD.Bids[0].Price);
                        sm_FlogDepth.Write("(");
                        sm_FlogDepth.Write(myD.Bids[0].Volume);
                        sm_FlogDepth.Write(")");
                        sm_FlogDepth.WriteLine();
                    }
                }

                if (testDepth.Any())
                    sm_FlogDepth.Flush();
            }

		}


Code

namespace SampleStorage
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using Ecng.Common;
    using StockSharp.Algo.Storages;
    using StockSharp.Algo.Testing;
    using StockSharp.BusinessEntities;

    class Program
    {
        struct depthDebugView
        {
            public DateTime LastChangeTime;
            public Quote BestAsk;
            public Quote BestBid;
            public depthDebugView(MarketDepth t)
            {
                LastChangeTime = t.LastChangeTime;
                BestAsk = t.BestAsk;
                BestBid = t.BestBid;
            }
            //     Получить строковое представление.
            public override string ToString()
            {
                return LastChangeTime.ToString() + "." + LastChangeTime.Millisecond.ToString() + "(" + LastChangeTime.Ticks.ToString() + "),"
                    + BestAsk.Price.ToString() + "(" + BestAsk.Volume.ToString() + ")"
                    + "-" + BestBid.Price.ToString() + "(" + BestBid.Volume.ToString() + ")";
            }
        };

        static void Main()
        {
            // создаем тестовый инструмент
            var security = new Security
            {
                Id = "RIZ2@RTS",
            };

            var storage = new StorageRegistry();

            string dPATH = "E:\\TradeDB\\Hydra\\Data";

            // получаем хранилище для тиковых сделок
            var tradeStorage = storage.GetTradeStorage(security, new LocalMarketDataDrive(dPATH));

            // загружаем сделки
            DateTime dtDayToLoad = new DateTime(2012, 10, 25);
            var trList = tradeStorage.Load(dtDayToLoad);


            // получаем хранилище для стаканов
            var depthStorage = storage.GetMarketDepthStorage(security, new LocalMarketDataDrive(dPATH));

            // загружаем стаканы
            var depthData = depthStorage.Load(dtDayToLoad);
#if NOTUSED
            // Фикс проблемы со временем - всем элементам принудительно ставим DateTimeKind.Local
            // Иначе - где-то Local, а где-то Unspecified => проблемы с конвертацией
            foreach (MarketDepth t in depthData)
            {
                t.LastChangeTime = DateTime.SpecifyKind(t.LastChangeTime, DateTimeKind.Local);
            }
            //Берем нужный кусок данных из загруженных стаканов
            DateTime dtLoadFromTime = new DateTime(2012, 10, 25, 23, 37, 00, DateTimeKind.Local);
            DateTime dtLoadToTime =   new DateTime(2012, 10, 25, 23, 41, 00, DateTimeKind.Local);
            var depthData2 = depthData.Where(d => dtLoadFromTime <= d.LastChangeTime && d.LastChangeTime <= dtLoadToTime);
#else
            //Берем нужный кусок данных из загруженных стаканов
            long ticStart = 634868051350000000L;
            long ticEnd =   634868051440000000L;
            var depthData2 = depthData.Where(d => (ticStart <= d.LastChangeTime.Ticks) && (d.LastChangeTime.Ticks <= ticEnd));
#endif
            var depthDebug = depthData2.Select(d => new depthDebugView(d));
            //Здесь ставим брякпойнт и наблюдаем содержимое depthDebug
            //Сравниваем его с сохраненным ранее логом от модифицированной Гидры

            Console.WriteLine("Количество загруженных сделок: " + trList.Count());
            foreach (Trade t in trList)
            {
                Console.WriteLine(t.Time + "(" + t.Latency.TotalSeconds + "c) : " + t.Price + " : " + t.Volume + "( " + t.OrderDirection + " )");
                System.Threading.Thread.Sleep(32);

            }

            foreach (MarketDepth t in depthData2)
            {
                Console.WriteLine(t.LastChangeTime + " : " + t.BestBid.Price + " - " + t.BestAsk.Price + " : " + (t.BestBid.Volume - t.BestAsk.Volume));
                System.Threading.Thread.Sleep(32);

            }

            Console.ReadLine();
        }
    }
}
Thanks:

Mikhail Sukhov

Avatar
Date: 10/29/2012
Reply


Фиксы залиты и выложены на бокс.нет

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

Стаканы из Плазы, как самые достоверные, не были подвержены этой ошибке, так как маркируются временной меткой биржи.

Приносим свои извинения за доставленные неудобства, и благодарим за своевременное оповещение о проблеме.
Thanks:

DrChemist

Avatar
Date: 10/31/2012
Reply


В течение двух дней проверял.

Проблема действительно полностью устранена. Никаких расхождений более 1 миллисекунды больше не наблюдается.

Вообще, очень приятно то, что проблему устранили достаточно оперативно. Желаю дальнейших успехов разработчикам и проекту!
Thanks: Alexander Mikhail Sukhov


Attach files by dragging & dropping, , or pasting from the clipboard.

loading
clippy