/// <summary> /// To get the storage for <paramref name="path"/>. /// </summary> /// <param name="path">Path.</param> /// <returns>Market data storage.</returns> public IMarketDataDrive GetDrive(string path) { if (path.IsEmpty() || Guid.TryParse(path, out _) /* TODO remove few versions later 2019-08-06 */) { return(DefaultDrive); } var pair = CreatePair(path); return(_drives.SafeAdd(pair, key => { IMarketDataDrive drive; if (pair.Item2 == null) { drive = new LocalMarketDataDrive(path); } else { drive = new RemoteMarketDataDrive(new RemoteStorageClient(new Uri(pair.Item2.To <string>()))); } NewDriveCreated?.Invoke(drive); return drive; })); }
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)); } }
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 tf = SelectedTimeFrame; 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; worker.ReportProgress(1, Tuple.Create(security, finamFrom, finamTo)); 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(); // TODO secCandles.AddRange(newCandles); } var traderDrive = new LocalMarketDataDrive(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.bin"))); 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); tradeDatesCache.Save(); 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); } 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: 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 candlesArea = new ChartArea(); Chart.AddArea(candlesArea); var positionArea = new ChartArea { Height = 200 }; Chart.AddArea(positionArea); positionArea.YAxises.Clear(); const string equityYAxis = "Equity"; candlesArea.YAxises.Clear(); candlesArea.YAxises.Add(new ChartAxis { Id = equityYAxis, AutoRange = true, AxisType = ChartAxisType.Numeric, AxisAlignment = ChartAxisAlignment.Left, }); var equityElem = new ChartIndicatorElement { YAxisId = equityYAxis, FullTitle = LocalizedStrings.PnL, IndicatorPainter = new PnlPainter() }; var equityInd = new SimpleMovingAverage { Length = 1 }; Chart.AddElement(candlesArea, equityElem); 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; 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(); }
//private static readonly Version _dateVersion = new Version(1, 0); public LocalMarketDataStorageDrive(Type dataType, object arg, string path, StorageFormats format, LocalMarketDataDrive drive) { var fileName = GetFileName(dataType, arg); if (path.IsEmpty()) { throw new ArgumentNullException(nameof(path)); } _path = path; _drive = drive ?? throw new ArgumentNullException(nameof(drive)); _fileNameWithExtension = fileName + GetExtension(format); _datesPath = IOPath.Combine(_path, fileName + format + "Dates.txt"); _dataType = DataType.Create(dataType, arg); _datesDict = new Lazy <CachedSynchronizedOrderedDictionary <DateTime, DateTime> >(() => { var retVal = new CachedSynchronizedOrderedDictionary <DateTime, DateTime>(); if (File.Exists(_datesPath)) { foreach (var date in LoadDates()) { retVal.Add(date, date); } } else { var dates = InteropHelper .GetDirectories(_path) .Where(dir => File.Exists(IOPath.Combine(dir, _fileNameWithExtension))) .Select(dir => GetDate(IOPath.GetFileName(dir))); foreach (var date in dates) { retVal.Add(date, date); } SaveDates(retVal.CachedValues); } return(retVal); }).Track(); }
public LocalMarketDataStorageDrive(LocalMarketDataDrive parent, SecurityId securityId, string fileName, StorageFormats format, IMarketDataDrive drive) { if (parent == null) { throw new ArgumentNullException("parent"); } if (securityId.IsDefault()) { throw new ArgumentNullException("securityId"); } if (drive == null) { throw new ArgumentNullException("drive"); } if (fileName.IsEmpty()) { throw new ArgumentNullException("fileName"); } _parent = parent; _securityId = securityId; _fileName = fileName; _format = format; _drive = drive; _fileNameWithExtension = _fileName + GetExtension(_format); _datesDict = new Lazy <CachedSynchronizedOrderedDictionary <DateTime, DateTime> >(() => { var retVal = new CachedSynchronizedOrderedDictionary <DateTime, DateTime>(); var datesPath = GetDatesCachePath(); if (File.Exists(datesPath)) { foreach (var date in LoadDates()) { retVal.Add(date, date); } } else { var rootDir = Path; var dates = InteropHelper .GetDirectories(rootDir) .Where(dir => File.Exists(IOPath.Combine(dir, _fileNameWithExtension))) .Select(dir => IOPath.GetFileName(dir).ToDateTime(_dateFormat)); foreach (var date in dates) { retVal.Add(date, date); } SaveDates(retVal.CachedValues); } return(retVal); }).Track(); }
public StudioDrive() { _localDrive = new LocalMarketDataDrive(); _remoteDrive = new RemoteMarketDataDrive(); _cacheDrive = new LocalMarketDataDrive(Path.Combine(BaseApplication.AppDataPath, "Cache")); }