/// <summary> /// Получить хранилище маркет-данных. /// </summary> /// <param name="path">Путь к данным.</param> /// <returns>Хранилище маркет-данных.</returns> public IMarketDataDrive GetDrive(string path) { if (path.IsEmpty()) { return(DefaultDrive); } return(_drives.SafeAdd(path ?? string.Empty, key => { IMarketDataDrive drive; try { var addr = path.To <EndPoint>(); drive = new RemoteMarketDataDrive(new RemoteStorageClient(new Uri(addr.To <string>()))); } catch { drive = new LocalMarketDataDrive(path); } NewDriveCreated.SafeInvoke(drive); return drive; })); }
private void HistoryPath_OnFolderChanged(string path) { var secs = LocalMarketDataDrive.GetAvailableSecurities(path).ToArray(); Securities.ItemsSource = secs; if (secs.Length > 0) { Securities.SelectedIndex = 0; } }
static void Main() { // creating AAPL security var security = new Security { Id = "AAPL@NASDAQ", PriceStep = 0.1m, Decimals = 1, }; var trades = new List <Trade>(); // generation 1000 random ticks // for (var i = 0; i < 1000; i++) { var t = new Trade { Time = DateTime.Today + TimeSpan.FromMinutes(i), Id = i + 1, Security = security, Volume = RandomGen.GetInt(1, 10), Price = RandomGen.GetInt(1, 100) * security.PriceStep ?? 1m + 99 }; trades.Add(t); } using (var drive = new LocalMarketDataDrive()) { // get AAPL storage var aaplStorage = drive.GetSecurityDrive(security); // get tick storage var tradeStorage = (IMarketDataStorage <Trade>)aaplStorage.GetTickStorage(new CsvMarketDataSerializer <ExecutionMessage>()); // saving ticks tradeStorage.Save(trades); // loading ticks var loadedTrades = tradeStorage.Load(DateTime.Today, DateTime.Today + TimeSpan.FromMinutes(1000)); foreach (var trade in loadedTrades) { Console.WriteLine(LocalizedStrings.Str2968Params, trade.Id, trade); } Console.ReadLine(); // deleting ticks (and removing file) tradeStorage.Delete(DateTime.Today, DateTime.Today + TimeSpan.FromMinutes(1000)); } }
public void InitDl() { dlDates = new double[0]; dlInfo = new double[0, 0]; SecList = new List <string>(); SecArray = new string[0]; //SStor = new SimStorage(); str = new StorageRegistry(); lmd = new LocalMarketDataDrive(); lmd.Path = Path; str.DefaultDrive = lmd; // }
static void Main(string[] args) { var security = new Security() { Id = "RIH7@FORTS", Board = ExchangeBoard.Forts }; StorageRegistry storage = new StorageRegistry(); string path = @"../../../Data/Quik"; //Для работы будем использовать готовое локальное файловое хранилище, в которое данные были предварительно записаны при помощи Гидры. //Создаем экземпляр LocalMarketDataDrive. LocalMarketDataDrive drive = new LocalMarketDataDrive() { Path = path }; //Передаем в StorageRegistry ссылку на локальное файловое хранилище storage.DefaultDrive = drive; DateTime from = new DateTime(2017, 02, 14, 10, 0, 0); DateTime to = new DateTime(2017, 02, 15, 23, 50, 0); // Получаем хранилище сделок для RIH7 IMarketDataStorage <Trade> tradeStorage = storage.GetTradeStorage(security); // Загружаем сделки из хранилища var trades = tradeStorage.Load(from, to); // Отображаем в окне вывода информацию foreach (var trade in trades) { Debug.WriteLine("{0} {1}", trade, trade.OpenInterest); } Console.Read(); }
public StudioDrive() { _localDrive = new LocalMarketDataDrive(); _remoteDrive = new RemoteMarketDataDrive(); _cacheDrive = new LocalMarketDataDrive(Path.Combine(BaseApplication.AppDataPath, "Cache")); }
protected override TimeSpan OnProcess() { IBackupService service; switch (_settings.Service) { case BackupServices.AwsS3: service = new AmazonS3Service(AmazonExtensions.GetEndpoint(_settings.Address), _settings.ServiceRepo, _settings.Login, _settings.Password.To <string>()); break; case BackupServices.AwsGlacier: service = new AmazonGlacierService(AmazonExtensions.GetEndpoint(_settings.Address), _settings.ServiceRepo, _settings.Login, _settings.Password.To <string>()); break; default: throw new ArgumentOutOfRangeException(); } var hasSecurities = false; this.AddInfoLog(LocalizedStrings.Str2306Params.Put(_settings.StartFrom)); var startDate = _settings.StartFrom; var endDate = DateTime.Today - TimeSpan.FromDays(_settings.Offset); var allDates = startDate.Range(endDate, TimeSpan.FromDays(1)).ToArray(); var pathEntry = ToEntry(new DirectoryInfo(_settings.Drive.Path)); var workingSecurities = GetWorkingSecurities().ToArray(); foreach (var date in allDates) { foreach (var security in workingSecurities) { hasSecurities = true; if (!CanProcess()) { break; } var dateEntry = new BackupEntry { Name = date.ToString("yyyy_MM_dd"), Parent = new BackupEntry { Parent = new BackupEntry { Name = security.Security.Id.Substring(0, 1), Parent = pathEntry }, Name = security.Security.Id, } }; var dataTypes = _settings.Drive.GetAvailableDataTypes(security.Security.ToSecurityId(), _settings.StorageFormat); foreach (var dataType in dataTypes) { var storage = StorageRegistry.GetStorage(security.Security, dataType.MessageType, dataType.Arg, _settings.Drive, _settings.StorageFormat); var drive = storage.Drive; var stream = drive.LoadStream(date); if (stream == Stream.Null) { continue; } var entry = new BackupEntry { Name = LocalMarketDataDrive.GetFileName(dataType.MessageType, dataType.Arg) + LocalMarketDataDrive.GetExtension(StorageFormats.Binary), Parent = dateEntry, }; service.Upload(entry, stream, p => { }); this.AddInfoLog(LocalizedStrings.Str1580Params, GetPath(entry)); } } if (CanProcess()) { _settings.StartFrom += TimeSpan.FromDays(1); SaveSettings(); } } if (!hasSecurities) { this.AddWarningLog(LocalizedStrings.Str2292); return(TimeSpan.MaxValue); } if (CanProcess()) { this.AddInfoLog(LocalizedStrings.Str2300); } return(base.OnProcess()); }
private void Download_OnClick(object sender, RoutedEventArgs e) { var year = SelectedYear; var from = From.Value ?? year.Days.First(); var to = (To.Value ?? year.Days.Last()).EndOfDay(); var trader = SelectedTrader; var security = SelectedSecurity; var tf = SelectedTimeFrame; var series = new CandleSeries(typeof(TimeFrameCandle), security, tf); BusyIndicator.BusyContent = "Подготовка данных..."; BusyIndicator.IsBusy = true; Dictionary <DateTimeOffset, Tuple <MyTrade[], MyTrade> > trades = null; var worker = new BackgroundWorker { WorkerReportsProgress = true }; worker.DoWork += (o, ea) => { var candleStorage = _dataRegistry.GetCandleStorage(series, format: StorageFormats.Csv); _candles = candleStorage.Load(from, to); var candlesDatesCache = _candlesDates.SafeAdd(Tuple.Create(security, tf), k => new DatesCache(Path.Combine(((LocalMarketDataDrive)candleStorage.Drive.Drive).GetSecurityPath(security.ToSecurityId()), "{0}min_date.bin".Put((int)tf.TotalMinutes)))); var minCandleDate = candlesDatesCache.MinValue; var maxCandleDate = candlesDatesCache.MaxValue; if (from < minCandleDate || to > maxCandleDate) { var finamFrom = from; var finamTo = to; if (maxCandleDate != default(DateTime) && finamFrom >= minCandleDate && finamFrom <= maxCandleDate) { finamFrom = maxCandleDate + TimeSpan.FromDays(1); } if (minCandleDate != default(DateTime) && finamTo >= minCandleDate && finamTo <= maxCandleDate) { finamTo = minCandleDate - TimeSpan.FromDays(1); } if (finamTo > finamFrom) { worker.ReportProgress(1); var newCandles = (tf.Ticks == 1 ? finamFrom.Range(finamTo, TimeSpan.FromDays(1)).SelectMany(day => _finamHistorySource.GetTrades(security, day, day)).ToEx().ToCandles <TimeFrameCandle>(tf) : _finamHistorySource.GetCandles(security, tf, finamFrom, finamTo) ).ToArray(); candleStorage.Save(newCandles); foreach (var date in newCandles.Select(c => c.OpenTime.Date).Distinct()) { candlesDatesCache.Add(date); } candlesDatesCache.Save(); _candles = _candles.Concat(newCandles); } } var traderDrive = new LocalMarketDataDrive(trader); var traderStorage = _traderStorages.SafeAdd(trader, key => new StorageRegistry { DefaultDrive = traderDrive }); var olStorage = traderStorage.GetOrderLogStorage(security, format: StorageFormats.Csv); var tradeDatesCache = _tradesDates.SafeAdd(trader, k => new DatesCache(Path.Combine(traderDrive.Path, "dates.bin"))); trades = from .Range(to, TimeSpan.FromDays(1)) .Intersect(year.Days) .SelectMany(date => { if (olStorage.Dates.Contains(date)) { return(olStorage.Load(date)); } if (tradeDatesCache.Contains(date)) { return(Enumerable.Empty <OrderLogItem>()); } worker.ReportProgress(2, date); var loadedTrades = year.GetTrades(_securityStorage, trader, date); var secTrades = Enumerable.Empty <OrderLogItem>(); foreach (var group in loadedTrades.GroupBy(t => t.Order.Security)) { var sec = group.Key; traderStorage .GetOrderLogStorage(sec, format: StorageFormats.Csv) .Save(group.OrderBy(i => i.Order.Time)); if (group.Key == security) { secTrades = group; } } tradeDatesCache.Add(date); tradeDatesCache.Save(); return(secTrades); }) .GroupBy(ol => { var time = ol.Order.Time; var period = security.Board.WorkingTime.GetPeriod(time.DateTime); if (period != null && period.Times.Length > 0) { var last = period.Times.Last().Max; if (time.TimeOfDay >= last) { time = time.AddTicks(-1); } } return(time.Truncate(tf)); }) .ToDictionary(g => g.Key, g => { var candleTrades = g .Select(order => new MyTrade { Order = order.Order, Trade = order.Trade }) .ToArray(); if (candleTrades.Length > 0) { var order = candleTrades[0].Order; var volume = candleTrades.Sum(t1 => t1.Trade.Volume * (t1.Order.Direction == Sides.Buy ? 1 : -1)); if (volume == 0) { return(Tuple.Create(candleTrades, (MyTrade)null)); } var side = volume > 0 ? Sides.Buy : Sides.Sell; volume = volume.Abs(); var availableVolume = volume; var avgPrice = 0m; foreach (var trade in candleTrades.Where(t1 => t1.Order.Direction == side)) { var tradeVol = trade.Trade.Volume.Min(availableVolume); avgPrice += trade.Trade.Price * tradeVol; availableVolume -= tradeVol; if (availableVolume <= 0) { break; } } avgPrice = avgPrice / volume; return(Tuple.Create(candleTrades, new MyTrade { Order = new Order { Security = order.Security, Direction = side, Time = g.Key, Portfolio = order.Portfolio, Price = avgPrice, Volume = volume, }, Trade = new Trade { Security = order.Security, Time = g.Key, Volume = volume, Price = avgPrice } })); } return(null); }); }; worker.ProgressChanged += (o, ea) => { switch (ea.ProgressPercentage) { case 1: BusyIndicator.BusyContent = "Скачивание свечей..."; break; default: BusyIndicator.BusyContent = "Скачивание сделок за {0:yyyy-MM-dd}...".Put(ea.UserState); break; } }; worker.RunWorkerCompleted += (o, ea) => { BusyIndicator.IsBusy = false; if (ea.Error == null) { Chart.ClearAreas(); _statisticManager.Reset(); var area = new ChartArea(); area.YAxises.Add(new ChartAxis { Id = "equity", AutoRange = true, AxisType = ChartAxisType.Numeric, AxisAlignment = ChartAxisAlignment.Left, }); Chart.AddArea(area); var candlesElem = new ChartCandleElement { ShowAxisMarker = false }; Chart.AddElement(area, candlesElem, series); var tradesElem = new ChartTradeElement { BuyStrokeColor = Colors.Black, SellStrokeColor = Colors.Black, FullTitle = "trades", }; Chart.AddElement(area, tradesElem); var equityElem = new ChartIndicatorElement { YAxisId = "equity", FullTitle = "equity", IndicatorPainter = new PnlPainter() }; var equityInd = new SimpleMovingAverage { Length = 1 }; Chart.AddElement(area, equityElem); var positionArea = new ChartArea { Height = 200 }; Chart.AddArea(positionArea); var positionElem = new ChartIndicatorElement { FullTitle = "position" }; var positionInd = new SimpleMovingAverage { Length = 1 }; Chart.AddElement(positionArea, positionElem); Chart.IsAutoRange = true; var pnlQueue = new PnLQueue(security.ToSecurityId()); //var level1Info = new Level1ChangeMessage //{ // SecurityId = pnlQueue.SecurityId, //} //.TryAdd(Level1Fields.PriceStep, security.PriceStep) //.TryAdd(Level1Fields.StepPrice, security.StepPrice); //pnlQueue.ProcessLevel1(level1Info); var pos = 0m; var chartValues = _candles .Select(c => { c.State = CandleStates.Finished; pnlQueue.ProcessLevel1(new Level1ChangeMessage { SecurityId = security.ToSecurityId(), }.TryAdd(Level1Fields.LastTradePrice, c.ClosePrice)); var values = new Dictionary <IChartElement, object> { { candlesElem, c }, }; var candleTrade = trades.TryGetValue(c.OpenTime); if (candleTrade != null) { if (candleTrade.Item2 != null) { values.Add(tradesElem, candleTrade.Item2); } foreach (var myTrade in candleTrade.Item1) { pos += myTrade.Order.Direction == Sides.Buy ? myTrade.Trade.Volume : -myTrade.Trade.Volume; var pnl = pnlQueue.Process(myTrade.ToMessage()); _statisticManager.AddMyTrade(pnl); } _statisticManager.AddPosition(c.OpenTime, pos); _statisticManager.AddPnL(c.OpenTime, pnlQueue.RealizedPnL + pnlQueue.UnrealizedPnL); } values.Add(equityElem, equityInd.Process(pnlQueue.RealizedPnL + pnlQueue.UnrealizedPnL)); values.Add(positionElem, positionInd.Process(pos)); return(new RefPair <DateTimeOffset, IDictionary <IChartElement, object> > { First = c.OpenTime, Second = values }); }) .ToArray(); Chart.Draw(chartValues); Chart.IsAutoRange = false; } else { new MessageBoxBuilder() .Error() .Owner(this) .Text(ea.Error.ToString()) .Show(); } }; worker.RunWorkerAsync(); }
protected override TimeSpan OnProcess() { IBackupService service; switch (_settings.Service) { case BackupServices.AwsS3: service = new AmazonS3Service(AmazonExtensions.GetEndpoint(_settings.Address), _settings.ServiceRepo, _settings.Login, _settings.Password.To <string>()); break; case BackupServices.AwsGlacier: service = new AmazonGlacierService(AmazonExtensions.GetEndpoint(_settings.Address), _settings.ServiceRepo, _settings.Login, _settings.Password.To <string>()); break; default: throw new ArgumentOutOfRangeException(); } var hasSecurities = false; this.AddInfoLog(LocalizedStrings.Str2306Params.Put(_settings.StartFrom)); var startDate = _settings.StartFrom; var endDate = DateTime.Today - TimeSpan.FromDays(_settings.Offset); var allDates = startDate.Range(endDate, TimeSpan.FromDays(1)).ToArray(); var pathEntry = ToEntry(new DirectoryInfo(_settings.Drive.Path)); IEnumerable <Tuple <Type, object> > dataTypes = new[] { Tuple.Create(typeof(ExecutionMessage), (object)ExecutionTypes.Tick), Tuple.Create(typeof(ExecutionMessage), (object)ExecutionTypes.OrderLog), Tuple.Create(typeof(ExecutionMessage), (object)ExecutionTypes.Order), Tuple.Create(typeof(ExecutionMessage), (object)ExecutionTypes.Trade), Tuple.Create(typeof(QuoteChangeMessage), (object)null), Tuple.Create(typeof(Level1ChangeMessage), (object)null), Tuple.Create(typeof(NewsMessage), (object)null) }; var workingSecurities = GetWorkingSecurities().ToArray(); foreach (var date in allDates) { foreach (var security in workingSecurities) { hasSecurities = true; if (!CanProcess()) { break; } var dateEntry = new BackupEntry { Name = date.ToString("yyyy_MM_dd"), Parent = new BackupEntry { Parent = new BackupEntry { Name = security.Security.Id.Substring(0, 1), Parent = pathEntry }, Name = security.Security.Id, } }; var candleTypes = _settings.Drive.GetCandleTypes(security.Security.ToSecurityId(), _settings.StorageFormat); var secDataTypes = dataTypes.Concat(candleTypes.SelectMany(t => t.Item2.Select(a => Tuple.Create(t.Item1, a)))); foreach (var tuple in secDataTypes) { var storage = StorageRegistry.GetStorage(security.Security, tuple.Item1, tuple.Item2, _settings.Drive, _settings.StorageFormat); var drive = storage.Drive; var stream = drive.LoadStream(date); if (stream == Stream.Null) { continue; } var entry = new BackupEntry { Name = LocalMarketDataDrive.CreateFileName(tuple.Item1, tuple.Item2) + LocalMarketDataDrive.GetExtension(StorageFormats.Binary), Parent = dateEntry, }; service.Upload(entry, stream, p => { }); this.AddInfoLog(LocalizedStrings.Str1580Params, GetPath(entry)); } } if (CanProcess()) { _settings.StartFrom += TimeSpan.FromDays(1); SaveSettings(); } } if (!hasSecurities) { this.AddWarningLog(LocalizedStrings.Str2292); return(TimeSpan.MaxValue); } if (CanProcess()) { this.AddInfoLog(LocalizedStrings.Str2300); } return(base.OnProcess()); }
public void Start(IMarketDataDrive destDrive, DateTime?startDate, DateTime?endDate, Security security, IMarketDataDrive sourceDrive, StorageFormats format, Type dataType, object arg) { CreateWorker(0, null); _worker.DoWork += (s, e) => { var storageRegistry = ConfigManager.GetService <IStorageRegistry>(); var storage = storageRegistry.GetStorage(security, dataType, arg, sourceDrive, format); try { var dates = storage.Dates.ToArray(); if (dates.IsEmpty()) { return; } var allDates = (startDate ?? dates.First()).Range((endDate ?? dates.Last()), TimeSpan.FromDays(1)); var datesToExport = storage.Dates .Intersect(allDates) .Select(d => { int count; if (dataType == typeof(ExecutionMessage)) { count = ((IMarketDataStorage <ExecutionMessage>)storage).Load(d).Count; } else if (dataType == typeof(QuoteChangeMessage)) { count = ((IMarketDataStorage <QuoteChangeMessage>)storage).Load(d).Count; } else if (dataType == typeof(Level1ChangeMessage)) { count = ((IMarketDataStorage <Level1ChangeMessage>)storage).Load(d).Count; } else if (dataType.IsSubclassOf(typeof(CandleMessage))) { count = ((IMarketDataStorage <CandleMessage>)storage).Load(d).Count; } else { throw new NotSupportedException(LocalizedStrings.Str2872Params.Put(dataType.Name)); } return(Tuple.Create(d, count)); }) .ToArray(); _worker.ReportProgress(0); var currentValuesCount = 0; var totalValuesCount = datesToExport.Select(d => d.Item2).Sum(); if (!Directory.Exists(destDrive.Path)) { Directory.CreateDirectory(destDrive.Path); } var dataPath = ((LocalMarketDataDrive)sourceDrive).GetSecurityPath(security.ToSecurityId()); var fileName = LocalMarketDataDrive.CreateFileName(dataType, arg) + LocalMarketDataDrive.GetExtension(StorageFormats.Binary); foreach (var date in datesToExport) { var d = date.Item1.ToString("yyyy_MM_dd"); var file = Path.Combine(dataPath, d, fileName); if (File.Exists(file)) { if (!Directory.Exists(Path.Combine(destDrive.Path, d))) { Directory.CreateDirectory(Path.Combine(destDrive.Path, d)); } File.Copy(file, Path.Combine(destDrive.Path, d, Path.GetFileName(file)), true); } if (date.Item2 == 0) { continue; } currentValuesCount += date.Item2; _worker.ReportProgress((int)Math.Round(currentValuesCount * 100m / totalValuesCount)); this.GuiAsync(() => UpdateCount(currentValuesCount)); } } finally { _worker.ReportProgress(100); Thread.Sleep(500); _worker.ReportProgress(0); } }; _worker.RunWorkerAsync(); }
static void Main(string[] args) { var security = new Security() { Id = "RIH7@FORTS", Board = ExchangeBoard.Forts }; StorageRegistry storage = new StorageRegistry(); string path = @"../../../Data/Quik"; //Для работы будем использовать готовое локальное файловое хранилище, в которое данные были предварительно записаны при помощи Гидры. //Создаем экземпляр LocalMarketDataDrive. LocalMarketDataDrive drive = new LocalMarketDataDrive() { Path = path }; //Передаем в StorageRegistry ссылку на локальное файловое хранилище storage.DefaultDrive = drive; DateTime from = new DateTime(2017, 02, 14, 10, 0, 0); DateTime to = new DateTime(2017, 02, 15, 23, 50, 0); // Создаем простой индикатор - простая скользяшая средняя. Простой индикатор - индикатор, которые состоит из одного индикатора. var ma = new SimpleMovingAverage() { Length = 11 }; // Событие генерируется при изменении текущего значения индикатора.. Событие имеет два параметра: входное и выходное значение. ma.Changed += (input, output) => { //TODO }; // Создаем комплексный индикатор - полосы Боллинджера. Комплексный индикатор - индикатор, которые состоит из нескольких простых или комплексных // индикаторов. В состав полос Боллинджера входят три простых индикатора: верхняя и нижняя полоса BollingerBand, а также простая скользящая средняя. var bb = new BollingerBands() { Length = 11, Width = 1.5m }; // Событие генерируется при изменении текущего значения индикатора.. Событие имеет два параметра: входное и выходное значение. bb.Changed += (input, output) => { //TODO }; // Создаем кандлеменеджер, который в качестве источника свечек использует сделки из хранилища. CandleManager candleManager = new CandleManager(new TradeStorageCandleBuilderSource() { StorageRegistry = storage }); candleManager.Processing += (series, candle) => { if (candle.State != CandleStates.Finished) { return; } // передаем в метод Process значение для обработки... // Передаваемый параметр должен реализовывать интерфейс IIndicatorValue. // В нашем случае используется метод расширения, который преобразует // свечу в класс CandleIndicatorValue, который реализует требуемый интерфейс. // Функция возвращается тип, который также реализует интерфейс IIndicatorValue // Такой подход обладает следующими преимуществами: // 1. IIndicatorValue используется при построении графиков // 2. Значение одного индикторы, можно сразу передавать на вход другого индикатра. var maValue = ma.Process(candle); // Свойство IsFormed становится true, когда текущее значение индикатора становится валидным // Наример, в скользящих средних, если число значений поступивших на вход больше или равно // периоду индикатора if (ma.IsFormed) { // Так можно вернуть "нормальное" значение из IIndicatorValue var maCur = ma.GetCurrentValue(); // Это другой способ получения "нормальное" текущего значения индикатора maCur = maValue.GetValue <decimal>(); } // Здесь мы используем комплексный индикатор. Также передаем в метод Process значение для обработки... // На выходе мы получаем тип ComplexIndicatorValue (тоже реализует IIndicatorValue). // Главная особенность типа ComplexIndicatorValue в наличии свойства InnerValues, где // хранятся текущие значения всех простых индикаторов, входящих в состав комплексного индикатора. var bbValue = (ComplexIndicatorValue)bb.Process(candle); if (bb.IsFormed) { // Так можно получить значения из InnerValues var upBandValue = bbValue.InnerValues[bb.UpBand]; var lowBandValue = bbValue.InnerValues[bb.LowBand]; var upBand = bb.UpBand.GetCurrentValue(); var lowBand = bb.LowBand.GetCurrentValue(); upBand = upBandValue.GetValue <decimal>(); lowBand = lowBandValue.GetValue <decimal>(); } }; var srs = new CandleSeries(typeof(TimeFrameCandle), security, TimeSpan.FromMinutes(1)); candleManager.Start(srs, from, to); Console.Read(); candleManager.Stop(srs); }
private void Download_OnClick(object sender, RoutedEventArgs e) { var settings = new Settings { Year = SelectedYear.Year, Trader = Trader.Text, From = From.Value, To = To.Value, Security1 = Security1.Text, Security2 = Security2.Text, Security3 = Security3.Text, Security4 = Security4.Text, TimeFrame = SelectedTimeFrame, Apart = Apart.IsChecked == true, }; CultureInfo.InvariantCulture.DoInCulture(() => new XmlSerializer <SettingsStorage>().Serialize(settings.Save(), _settingsFile)); var year = SelectedYear; var from = From.Value ?? year.Days.First(); var to = (To.Value ?? year.Days.Last()).EndOfDay(); var trader = SelectedTrader; var tf = SelectedTimeFrame; var apart = Apart.IsChecked == true; var seriesSet = _securityCtrls .Where(pair => pair.Key.SelectedSecurity != null) .Select(pair => Tuple.Create(new CandleSeries(typeof(TimeFrameCandle), pair.Key.SelectedSecurity, tf), pair.Value)) .ToArray(); BusyIndicator.BusyContent = "Подготовка данных..."; BusyIndicator.IsBusy = true; _candles.Clear(); var trades = new Dictionary <Security, Dictionary <DateTimeOffset, Tuple <MyTrade[], MyTrade> > >(); var worker = new BackgroundWorker { WorkerReportsProgress = true }; worker.DoWork += (o, ea) => { foreach (var series in seriesSet) { var security = series.Item1.Security; var candleStorage = _dataRegistry.GetCandleStorage(series.Item1, format: StorageFormats.Csv); var secCandles = _candles.SafeAdd(security); secCandles.Clear(); secCandles.AddRange(candleStorage.Load(from, to)); var candlesDatesCache = _candlesDates.SafeAdd(Tuple.Create(security, tf), k => new DatesCache(Path.Combine(((LocalMarketDataDrive)candleStorage.Drive.Drive).GetSecurityPath(security.ToSecurityId()), "{0}min_date.bin".Put((int)tf.TotalMinutes)))); var minCandleDate = candlesDatesCache.MinValue; var maxCandleDate = candlesDatesCache.MaxValue; if (from >= minCandleDate && to <= maxCandleDate) { continue; } var finamFrom = from; var finamTo = to; if (maxCandleDate != null && finamFrom >= minCandleDate && finamFrom <= maxCandleDate) { finamFrom = maxCandleDate.Value + TimeSpan.FromDays(1); } if (minCandleDate != null && finamTo >= minCandleDate && finamTo <= maxCandleDate) { finamTo = minCandleDate.Value - TimeSpan.FromDays(1); } if (finamTo <= finamFrom) { continue; } TimeFrameCandle[] newCandles; if (tf.Ticks == 1) { newCandles = finamFrom.Range(finamTo, TimeSpan.FromDays(1)).SelectMany(day => { worker.ReportProgress(1, Tuple.Create(security, day)); var candles = _finamHistorySource.GetTrades(security, day, day).ToEx().ToCandles <TimeFrameCandle>(tf).ToArray(); candleStorage.Save(candles); candlesDatesCache.Add(day); return(candles); }).ToArray(); } else { worker.ReportProgress(1, Tuple.Create(security, finamFrom, finamTo)); newCandles = _finamHistorySource.GetCandles(security, tf, finamFrom, finamTo).ToArray(); candleStorage.Save(newCandles); candlesDatesCache.Add(newCandles.Select(c => c.OpenTime.Date).Distinct().ToArray()); } // TODO secCandles.AddRange(newCandles); } var traderDrive = new LocalMarketDataDrive(Path.Combine(_settingsDir, trader)); var traderStorage = _traderStorages.SafeAdd(trader, key => new StorageRegistry { DefaultDrive = traderDrive }); foreach (var series in seriesSet) { var security = series.Item1.Security; var olStorage = traderStorage.GetOrderLogStorage(security, format: StorageFormats.Csv); var tradeDatesCache = _tradesDates.SafeAdd(trader, k => new DatesCache(Path.Combine(traderDrive.Path, "dates.xml"))); var secTrades = from .Range(to, TimeSpan.FromDays(1)) .Intersect(year.Days) .SelectMany(date => { if (olStorage.Dates.Contains(date)) { return(olStorage.Load(date)); } if (tradeDatesCache.Contains(date)) { return(Enumerable.Empty <OrderLogItem>()); } worker.ReportProgress(2, date); var loadedTrades = year.GetTrades(_securityStorage, trader, date); var dateTrades = Enumerable.Empty <OrderLogItem>(); foreach (var group in loadedTrades.GroupBy(t => t.Order.Security)) { var sec = group.Key; traderStorage .GetOrderLogStorage(sec, format: StorageFormats.Csv) .Save(group.OrderBy(i => i.Order.Time)); if (group.Key == security) { dateTrades = group; } } tradeDatesCache.Add(date); return(dateTrades); }) .GroupBy(ol => { var time = ol.Order.Time; var period = security.Board.WorkingTime.GetPeriod(time.ToLocalTime(security.Board.Exchange.TimeZoneInfo)); if (period != null && period.Times.Length > 0) { var last = period.Times.Last().Max; if (time.TimeOfDay >= last) { time = time.AddTicks(-1); } } if (tf == TimeSpan.FromDays(1) && period != null && period.Times.Length > 0) { return(new DateTimeOffset(time.Date + period.Times[0].Min, time.Offset)); } return(time.Truncate(tf)); }) .ToDictionary(g => g.Key, g => { var candleTrades = g.Select(ol => new MyTrade { Order = ol.Order, Trade = ol.Trade }) .ToArray(); if (candleTrades.Length == 0) { return(null); } var order = candleTrades[0].Order; var volume = candleTrades.Sum(t1 => t1.Trade.Volume * (t1.Order.Direction == Sides.Buy ? 1 : -1)); if (volume == 0) { return(Tuple.Create(candleTrades, (MyTrade)null)); } var side = volume > 0 ? Sides.Buy : Sides.Sell; volume = volume.Abs(); var availableVolume = volume; var avgPrice = 0m; foreach (var trade in candleTrades.Where(t1 => t1.Order.Direction == side)) { var tradeVol = trade.Trade.Volume.Min(availableVolume); avgPrice += trade.Trade.Price * tradeVol; availableVolume -= tradeVol; if (availableVolume <= 0) { break; } } avgPrice = avgPrice / volume; return(Tuple.Create(candleTrades, new MyTrade { Order = new Order { Security = order.Security, Direction = side, Time = g.Key, Portfolio = order.Portfolio, Price = avgPrice, Volume = volume, }, Trade = new Trade { Security = order.Security, Time = g.Key, Volume = volume, Price = avgPrice } })); }); trades.Add(security, secTrades); } }; worker.ProgressChanged += (o, ea) => { switch (ea.ProgressPercentage) { case 1: { if (ea.UserState is Tuple <Security, DateTime> ) { BusyIndicator.BusyContent = "Скачивание {Item1.Id} тиков за {Item2:yyyy-MM-dd}...".PutEx(ea.UserState); } else { BusyIndicator.BusyContent = "Скачивание {Item1.Id} свечей с {Item2:yyyy-MM-dd} по {Item3:yyyy-MM-dd}...".PutEx(ea.UserState); } break; } default: BusyIndicator.BusyContent = "Скачивание сделок за {0:yyyy-MM-dd}...".Put(ea.UserState); break; } }; worker.RunWorkerCompleted += (o, ea) => { BusyIndicator.IsBusy = false; if (ea.Error == null) { Chart.ClearAreas(); _statisticManager.Reset(); var equityInd = new SimpleMovingAverage { Length = 1 }; ChartIndicatorElement equityElem; var candlesAreas = new Dictionary <CandleSeries, ChartArea>(); if (apart) { foreach (var series in seriesSet) { var area = new ChartArea { Title = series.Item1.Security.Id }; Chart.AddArea(area); area.YAxises.Clear(); candlesAreas.Add(series.Item1, area); } var equityArea = new ChartArea { Title = LocalizedStrings.PnL }; Chart.AddArea(equityArea); equityElem = new ChartIndicatorElement { FullTitle = LocalizedStrings.PnL, IndicatorPainter = new PnlPainter() }; Chart.AddElement(equityArea, equityElem); } else { var candlesArea = new ChartArea(); Chart.AddArea(candlesArea); foreach (var tuple in seriesSet) { candlesAreas.Add(tuple.Item1, candlesArea); } const string equityYAxis = "Equity"; candlesArea.YAxises.Clear(); candlesArea.YAxises.Add(new ChartAxis { Id = equityYAxis, AutoRange = true, AxisType = ChartAxisType.Numeric, AxisAlignment = ChartAxisAlignment.Left, }); equityElem = new ChartIndicatorElement { YAxisId = equityYAxis, FullTitle = LocalizedStrings.PnL, IndicatorPainter = new PnlPainter() }; Chart.AddElement(candlesArea, equityElem); } var positionArea = new ChartArea { Height = 100 }; Chart.AddArea(positionArea); positionArea.YAxises.Clear(); var chartValues = new SortedDictionary <DateTimeOffset, IDictionary <IChartElement, object> >(); var pnlValues = new Dictionary <DateTimeOffset, decimal>(); foreach (var series in seriesSet) { var security = series.Item1.Security; var candleYAxis = "Candles_Y_" + security.Id; var candlesArea = candlesAreas[series.Item1]; candlesArea.YAxises.Add(new ChartAxis { Id = candleYAxis, AutoRange = true, AxisType = ChartAxisType.Numeric, AxisAlignment = ChartAxisAlignment.Right, }); var candlesElem = new ChartCandleElement { ShowAxisMarker = false, YAxisId = candleYAxis, }; Chart.AddElement(candlesArea, candlesElem, series.Item1); var tradesElem = new ChartTradeElement { BuyStrokeColor = Colors.Black, SellStrokeColor = Colors.Black, BuyColor = series.Item2.Buy, SellColor = series.Item2.Sell, FullTitle = LocalizedStrings.Str985 + " " + security.Id, YAxisId = candleYAxis, }; Chart.AddElement(candlesArea, tradesElem); var posYAxis = "Pos_Y_" + security.Id; positionArea.YAxises.Add(new ChartAxis { Id = posYAxis, AutoRange = true, AxisType = ChartAxisType.Numeric, AxisAlignment = ChartAxisAlignment.Right, }); var positionElem = new ChartIndicatorElement { FullTitle = LocalizedStrings.Str862 + " " + security.Id, YAxisId = posYAxis, Color = series.Item2.Position }; var positionInd = new SimpleMovingAverage { Length = 1 }; Chart.AddElement(positionArea, positionElem); var pnlQueue = new PnLQueue(security.ToSecurityId()); //var level1Info = new Level1ChangeMessage //{ // SecurityId = pnlQueue.SecurityId, //} //.TryAdd(Level1Fields.PriceStep, security.PriceStep) //.TryAdd(Level1Fields.StepPrice, security.StepPrice); //pnlQueue.ProcessLevel1(level1Info); var pos = 0m; var secTrades = trades[security]; var secValues = _candles[security] .Select(c => { if (c.State != CandleStates.Finished) { c.State = CandleStates.Finished; } pnlQueue.ProcessLevel1(new Level1ChangeMessage { SecurityId = security.ToSecurityId(), }.TryAdd(Level1Fields.LastTradePrice, c.ClosePrice)); var values = new Dictionary <IChartElement, object> { { candlesElem, c }, }; var candleTrade = secTrades.TryGetValue(c.OpenTime); if (candleTrade != null) { if (candleTrade.Item2 != null) { values.Add(tradesElem, candleTrade.Item2); } foreach (var myTrade in candleTrade.Item1) { pos += myTrade.Order.Direction == Sides.Buy ? myTrade.Trade.Volume : -myTrade.Trade.Volume; var pnl = pnlQueue.Process(myTrade.ToMessage()); _statisticManager.AddMyTrade(pnl); } _statisticManager.AddPosition(c.OpenTime, pos); _statisticManager.AddPnL(c.OpenTime, pnlQueue.RealizedPnL + pnlQueue.UnrealizedPnL); } pnlValues[c.OpenTime] = pnlValues.TryGetValue(c.OpenTime) + (pnlQueue.RealizedPnL + pnlQueue.UnrealizedPnL); values.Add(positionElem, positionInd.Process(pos)); return(new RefPair <DateTimeOffset, IDictionary <IChartElement, object> > { First = c.OpenTime, Second = values }); }) .ToArray(); foreach (var pair in secValues) { var dict = chartValues.SafeAdd(pair.First, key => new Dictionary <IChartElement, object>()); foreach (var pair2 in pair.Second) { dict[pair2.Key] = pair2.Value; } } } foreach (var pair in pnlValues) { chartValues[pair.Key].Add(equityElem, equityInd.Process(pair.Value)); } Chart.IsAutoRange = true; try { Chart.Draw(chartValues.Select(p => RefTuple.Create(p.Key, p.Value))); } finally { Chart.IsAutoRange = false; } } else { new MessageBoxBuilder() .Error() .Owner(this) .Text(ea.Error.ToString()) .Show(); } }; worker.RunWorkerAsync(); }