Mikhail Sukhov
|
Date: 10/22/2012
DrChemist Я обнаружил, что поток сделок (по таблице всех сделок) очень сильно запаздывает по отношению к потоку стаканов. Получилось, что для сбербанка запаздывание потока сделок составляет 15-20 секунд, для индекса RTS –примерно 35 секунд!
Думаю тут причины в: 1. Неправильном расчете задержки. 2. Сильно прореженных стаканах от Квика.
|
|
Thanks:
|
|
|
|
|
DrChemist
|
Date: 10/23/2012
|
|
|
|
Попробую по пунктам: 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
|
Date: 10/24/2012
|
|
|
|
DrChemist 1.6.1. Неправильно записывается или читается время MarketDepth .LastChangeTime. Т.е. в результате цикла сохранение – чтение LastChangeTime получает отрицательное смещение секунд в 10. Вопрос к разработчикам – может ли такое быть. Ведь LastChangeTime это – свойство, с некоторой реализацией “get”. Не может ли оказаться, что именно эта реализация дает такое смещение? Проверить это предположение можно, но очень муторно – фактически, нужно организовывать альтернативное хранилище по отношению к гидре. Поэтому хотелось бы сначала получить комментарии от разработчика.
http://stocksharp.com/do...0-a7c7-0979dba1ad0a.htm
DrChemist 2. Честно говоря, не понял, как разреженность стакана может быть связана с эффектом отставания тиковых данных от стакана. Не разреженность, а прореженность... В секунду происходит около сотни регистраций и снятий. Естественно, квик, да еще и в агрегированном состоянии, такое количество изменений просто не будет транслировать. Может быть такая ситуация, что матчинговый снимок стакана просто не был передан в программу, и последующий рематчинг произошел со сдвигом только за счет принципа mean reversion
|
|
Thanks:
|
|
|
|
|
DrChemist
|
Date: 10/25/2012
|
|
|
|
В общем , повозился и выяснил. Это – БАГ. То ли в гидре то ли еще где в недрах 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
|
Date: 10/25/2012
Да, интересная ситуация. Думаю стоит выводить еще и DateTime.Ticks, так как Kind не понятно как работает.
|
|
Thanks:
|
|
|
|
|
DrChemist
|
Date: 10/26/2012
|
|
|
|
Ну вот. Проверил с тиками. Вывод – с тиками все те же проблемы. Чтобы не смущал 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
|
Date: 10/29/2012
Фиксы залиты и выложены на бокс.нет
Ситуация была такая. Стаканы в Квике не имеют временной отметки, и поэтому берется локальное время компьютера. При накоплении этой информации отбрасывались значения, точнее миллисекунд (смысла в них нет особо, так как они все равно берутся с локального компьютера). В итоге происходила аккумуляционная ошибка погрешности, что со временем приводило к расхождению во временных рядах. Сейчас алгоритм переделан на другой способ (формат Гидры при этом не изменялся), и эта ошибка предотвращена.
Стаканы из Плазы, как самые достоверные, не были подвержены этой ошибке, так как маркируются временной меткой биржи.
Приносим свои извинения за доставленные неудобства, и благодарим за своевременное оповещение о проблеме.
|
|
Thanks:
|
|
|
|
|
DrChemist
|
Date: 10/31/2012
В течение двух дней проверял.
Проблема действительно полностью устранена. Никаких расхождений более 1 миллисекунды больше не наблюдается.
Вообще, очень приятно то, что проблему устранили достаточно оперативно. Желаю дальнейших успехов разработчикам и проекту!
|
|
|
|