private Security GetSecurity(AlorTable table, object[] values, AlorColumn codeColumn, AlorColumn boardColumn) { if (table == null) { throw new ArgumentNullException(nameof(table)); } if (values == null) { throw new ArgumentNullException(nameof(values)); } if (codeColumn == null) { throw new ArgumentNullException(nameof(codeColumn)); } if (boardColumn == null) { throw new ArgumentNullException(nameof(boardColumn)); } var secCode = table.GetValue <string>(values, codeColumn); var secBoard = ExchangeBoard.GetBoard(table.GetValue <string>(values, boardColumn)); var id = SecurityIdGenerator.GenerateId(secCode, secBoard); return(GetSecurity(id, name => { var security = EntityFactory.CreateSecurity(name); security.Board = secBoard; security.Code = secCode; return security; }, security => false)); }
public AllSecurityMarketDataStorage(Security security, object arg, Func <T, DateTimeOffset> getTime, Func <T, Security> getSecurity, Func <Security, IMarketDataDrive, IMarketDataStorage <T> > getStorage, IMarketDataStorageDrive drive) { if (security == null) { throw new ArgumentNullException("security"); } if (getTime == null) { throw new ArgumentNullException("getTime"); } if (getSecurity == null) { throw new ArgumentNullException("getSecurity"); } if (getStorage == null) { throw new ArgumentNullException("getStorage"); } if (drive == null) { throw new ArgumentNullException("drive"); } _security = security; _getTime = getTime; _arg = arg; Drive = drive; _basket = new BasketMarketDataStorage <T>(); var idGenerator = new SecurityIdGenerator(); var parts = idGenerator.Split(security.Id); var code = parts.Item1; var securities = InteropHelper .GetDirectories(Path.Combine(Drive.Drive.Path, code.Substring(0, 1)), code + "*") .Select(p => Path.GetFileName(p).FolderNameToSecurityId()) .Select(s => { var id = idGenerator.Split(s); var clone = security.Clone(); clone.Id = s; clone.Board = ExchangeBoard.GetOrCreateBoard(id.Item2); return(clone); }); foreach (var sec in securities) { _basket.InnerStorages.Add(getStorage(sec, Drive.Drive)); } }
public void Init() { if (!File.Exists(_fileName)) { return; } CultureInfo.InvariantCulture.DoInCulture(() => { using (var stream = new FileStream(_fileName, FileMode.Open, FileAccess.Read)) { var reader = new FastCsvReader(stream, Encoding.UTF8); reader.NextLine(); reader.Skip(); var fields = new string[reader.ColumnCount - 1]; for (var i = 0; i < reader.ColumnCount - 1; i++) { fields[i] = reader.ReadString(); } reader.NextLine(); reader.Skip(); var types = new Type[reader.ColumnCount - 1]; for (var i = 0; i < reader.ColumnCount - 1; i++) { types[i] = reader.ReadString().To <Type>(); _fieldTypes.Add(fields[i], types[i]); } var idGenerator = new SecurityIdGenerator(); while (reader.NextLine()) { var secId = idGenerator.Split(reader.ReadString()); var values = new Dictionary <object, object>(); for (var i = 0; i < fields.Length; i++) { values[fields[i]] = reader.ReadString().To(types[i]); } _cache.Add(secId, values); } } }); }
private void ExecutedSaveSecurityCommand(object sender, ExecutedRoutedEventArgs e) { var entityRegistry = ConfigManager.GetService <IStudioEntityRegistry>(); var security = (Security)PropertyGrid.SelectedObject; if (_isNew) { var mbBuilder = new MessageBoxBuilder() .Owner(this) .Error(); if (security.Code.IsEmpty()) { mbBuilder.Text(LocalizedStrings.Str1551).Show(); return; } if (security.Board == null) { mbBuilder.Text(LocalizedStrings.Str1547).Show(); return; } if (security.PriceStep == null) { mbBuilder.Text(LocalizedStrings.Str1546).Show(); return; } var id = new SecurityIdGenerator().GenerateId(security.Code, security.Board); if (entityRegistry.Securities.ReadById(id) != null) { mbBuilder.Text(LocalizedStrings.Str3275Params.Put(id)).Show(); return; } security.Id = id; } else { security.CopyTo(SecurityPicker.SelectedSecurity); security = SecurityPicker.SelectedSecurity; } entityRegistry.Securities.Save(security); _isNew = false; _changed = false; }
private Security GetSecurityBySecCode(string secCode) { var sfxSecurity = new SfxSecurity { SlotID = _slot.ID, SecCode = secCode }; sfxSecurity.Load().ThrowIfNeed(LocalizedStrings.Str3704); secCode = sfxSecurity.SecCode; var secBoard = ExchangeBoard.GetBoard(sfxSecurity.SecBoard); var id = SecurityIdGenerator.GenerateId(secCode, secBoard); return(GetSecurity(id, name => { var security = EntityFactory.CreateSecurity(name); security.Board = secBoard; security.Code = secCode; return security; }, security => false)); }
private void StartBtnClick(object sender, RoutedEventArgs e) { InitChart(); if (HistoryPath.Folder.IsEmpty() || !Directory.Exists(HistoryPath.Folder)) { MessageBox.Show(this, LocalizedStrings.Str3014); return; } if (_connectors.Any(t => t.State != EmulationStates.Stopped)) { MessageBox.Show(this, LocalizedStrings.Str3015); return; } var secGen = new SecurityIdGenerator(); var id = secGen.Split(SecId.Text); //if (secIdParts.Length != 2) //{ // MessageBox.Show(this, LocalizedStrings.Str3016); // return; //} var timeFrame = TimeSpan.FromMinutes(TimeFrame.SelectedIndex == 0 ? 1 : 5); var secCode = id.SecurityCode; var board = ExchangeBoard.GetOrCreateBoard(id.BoardCode); // create test security var security = new Security { Id = SecId.Text, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; if (FinamCandlesCheckBox.IsChecked == true) { _finamHistorySource.Refresh(new FinamSecurityStorage(security), security, s => {}, () => false); } // create backtesting modes var settings = new[] { Tuple.Create( TicksCheckBox, TicksProgress, TicksParameterGrid, // ticks new EmulationInfo { UseTicks = true, CurveColor = Colors.DarkGreen, StrategyName = LocalizedStrings.Ticks }), Tuple.Create( TicksAndDepthsCheckBox, TicksAndDepthsProgress, TicksAndDepthsParameterGrid, // ticks + order book new EmulationInfo { UseTicks = true, UseMarketDepth = true, CurveColor = Colors.Red, StrategyName = LocalizedStrings.XamlStr757 }), Tuple.Create( DepthsCheckBox, DepthsProgress, DepthsParameterGrid, // order book new EmulationInfo { UseMarketDepth = true, CurveColor = Colors.OrangeRed, StrategyName = LocalizedStrings.MarketDepths }), Tuple.Create( CandlesCheckBox, CandlesProgress, CandlesParameterGrid, // candles new EmulationInfo { UseCandleTimeFrame = timeFrame, CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.Candles }), Tuple.Create( CandlesAndDepthsCheckBox, CandlesAndDepthsProgress, CandlesAndDepthsParameterGrid, // candles + orderbook new EmulationInfo { UseMarketDepth = true, UseCandleTimeFrame = timeFrame, CurveColor = Colors.Cyan, StrategyName = LocalizedStrings.XamlStr635 }), Tuple.Create( OrderLogCheckBox, OrderLogProgress, OrderLogParameterGrid, // order log new EmulationInfo { UseOrderLog = true, CurveColor = Colors.CornflowerBlue, StrategyName = LocalizedStrings.OrderLog }), Tuple.Create( Level1CheckBox, Level1Progress, Level1ParameterGrid, // order log new EmulationInfo { UseLevel1 = true, CurveColor = Colors.Aquamarine, StrategyName = LocalizedStrings.Level1 }), Tuple.Create( FinamCandlesCheckBox, FinamCandlesProgress, FinamCandlesParameterGrid, // candles new EmulationInfo { UseCandleTimeFrame = timeFrame, HistorySource = d => _finamHistorySource.GetCandles(security, timeFrame, d.Date, d.Date), CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.FinamCandles }), Tuple.Create( YahooCandlesCheckBox, YahooCandlesProgress, YahooCandlesParameterGrid, // candles new EmulationInfo { UseCandleTimeFrame = timeFrame, HistorySource = d => new YahooHistorySource().GetCandles(security, timeFrame, d.Date, d.Date), CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.YahooCandles }), }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(HistoryPath.Folder) }; var startTime = ((DateTime)From.Value).ChangeKind(DateTimeKind.Utc); var stopTime = ((DateTime)To.Value).ChangeKind(DateTimeKind.Utc); // (ru only) ОЛ необходимо загружать с 18.45 пред дня, чтобы стаканы строились правильно if (OrderLogCheckBox.IsChecked == true) { startTime = startTime.Subtract(TimeSpan.FromDays(1)).AddHours(18).AddMinutes(45).AddTicks(1).ApplyTimeZone(TimeHelper.Moscow).UtcDateTime; } // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To <TimeSpan>(); // set ProgressBar bounds _progressBars.ForEach(p => { p.Value = 0; p.Maximum = 100; }); var logManager = new LogManager(); var fileLogListener = new FileLogListener("sample.log"); logManager.Listeners.Add(fileLogListener); //logManager.Listeners.Add(new DebugLogListener()); // for track logs in output window in Vusial Studio (poor performance). var generateDepths = GenDepthsCheckBox.IsChecked == true; var maxDepth = MaxDepth.Text.To <int>(); var maxVolume = MaxVolume.Text.To <int>(); var secId = security.ToSecurityId(); foreach (var set in settings) { if (set.Item1.IsChecked == false) { continue; } var progressBar = set.Item2; var statistic = set.Item3; var emulationInfo = set.Item4; var level1Info = new Level1ChangeMessage { SecurityId = secId, ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, secCode == "RIZ2" ? 10m : 1) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; // create backtesting connector var connector = new HistoryEmulationConnector( new[] { security }, new[] { portfolio }) { EmulationAdapter = { Emulator = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = false, } } }, UseExternalCandleSource = emulationInfo.UseCandleTimeFrame != null, CreateDepthFromOrdersLog = emulationInfo.UseOrderLog, CreateTradesFromOrdersLog = emulationInfo.UseOrderLog, HistoryMessageAdapter = { StorageRegistry = storageRegistry, // set history range StartDate = startTime, StopDate = stopTime, OrderLogMarketDepthBuilders = { { secId, LocalizedStrings.ActiveLanguage == Languages.Russian ? (IOrderLogMarketDepthBuilder) new PlazaOrderLogMarketDepthBuilder(secId) : new ItchOrderLogMarketDepthBuilder(secId) } } }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; ((ILogSource)connector).LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info; logManager.Sources.Add(connector); var candleManager = emulationInfo.UseCandleTimeFrame == null ? new CandleManager(new TradeCandleBuilderSourceEx(connector)) : new CandleManager(connector); var series = new CandleSeries(typeof(TimeFrameCandle), security, timeFrame); _shortMa = new SimpleMovingAverage { Length = 10 }; _shortElem = new ChartIndicatorElement { Color = Colors.Coral, ShowAxisMarker = false, FullTitle = _shortMa.ToString() }; _bufferedChart.AddElement(_area, _shortElem); _longMa = new SimpleMovingAverage { Length = 80 }; _longElem = new ChartIndicatorElement { ShowAxisMarker = false, FullTitle = _longMa.ToString() }; _bufferedChart.AddElement(_area, _longElem); // create strategy based on 80 5-min и 10 5-min var strategy = new SmaStrategy(_bufferedChart, _candlesElem, _tradesElem, _shortMa, _shortElem, _longMa, _longElem, candleManager, series) { Volume = 1, Portfolio = portfolio, Security = security, Connector = connector, LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info, // by default interval is 1 min, // it is excessively for time range with several months UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>() }; logManager.Sources.Add(strategy); connector.NewSecurities += securities => { if (securities.All(s => s != security)) { return; } // fill level1 values connector.SendInMessage(level1Info); if (emulationInfo.HistorySource != null) { if (emulationInfo.UseCandleTimeFrame != null) { connector.RegisterHistorySource(security, MarketDataTypes.CandleTimeFrame, emulationInfo.UseCandleTimeFrame.Value, emulationInfo.HistorySource); } if (emulationInfo.UseTicks) { connector.RegisterHistorySource(security, MarketDataTypes.Trades, null, emulationInfo.HistorySource); } if (emulationInfo.UseLevel1) { connector.RegisterHistorySource(security, MarketDataTypes.Level1, null, emulationInfo.HistorySource); } if (emulationInfo.UseMarketDepth) { connector.RegisterHistorySource(security, MarketDataTypes.MarketDepth, null, emulationInfo.HistorySource); } } else { if (emulationInfo.UseMarketDepth) { connector.RegisterMarketDepth(security); if ( // if order book will be generated generateDepths || // of backtesting will be on candles emulationInfo.UseCandleTimeFrame != TimeSpan.Zero ) { // if no have order book historical data, but strategy is required, // use generator based on last prices connector.RegisterMarketDepth(new TrendMarketDepthGenerator(connector.GetSecurityId(security)) { Interval = TimeSpan.FromSeconds(1), // order book freq refresh is 1 sec MaxAsksDepth = maxDepth, MaxBidsDepth = maxDepth, UseTradeVolume = true, MaxVolume = maxVolume, MinSpreadStepCount = 2, // min spread generation is 2 pips MaxSpreadStepCount = 5, // max spread generation size (prevent extremely size) MaxPriceStepCount = 3 // pips size, }); } } if (emulationInfo.UseOrderLog) { connector.RegisterOrderLog(security); } if (emulationInfo.UseTicks) { connector.RegisterTrades(security); } if (emulationInfo.UseLevel1) { connector.RegisterSecurity(security); } } // start strategy before emulation started strategy.Start(); candleManager.Start(series); // start historical data loading when connection established successfully and all data subscribed connector.Start(); }; // fill parameters panel statistic.Parameters.Clear(); statistic.Parameters.AddRange(strategy.StatisticManager.Parameters); var pnlCurve = Curve.CreateCurve(LocalizedStrings.PnL + " " + emulationInfo.StrategyName, emulationInfo.CurveColor, EquityCurveChartStyles.Area); var unrealizedPnLCurve = Curve.CreateCurve(LocalizedStrings.PnLUnreal + emulationInfo.StrategyName, Colors.Black); var commissionCurve = Curve.CreateCurve(LocalizedStrings.Str159 + " " + emulationInfo.StrategyName, Colors.Red, EquityCurveChartStyles.DashedLine); var posItems = PositionCurve.CreateCurve(emulationInfo.StrategyName, emulationInfo.CurveColor); strategy.PnLChanged += () => { var pnl = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnL - strategy.Commission ?? 0 }; var unrealizedPnL = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnLManager.UnrealizedPnL }; var commission = new EquityData { Time = strategy.CurrentTime, Value = strategy.Commission ?? 0 }; pnlCurve.Add(pnl); unrealizedPnLCurve.Add(unrealizedPnL); commissionCurve.Add(commission); }; strategy.PositionChanged += () => posItems.Add(new EquityData { Time = strategy.CurrentTime, Value = strategy.Position }); var nextTime = startTime + progressStep; // handle historical time for update ProgressBar connector.MarketTimeChanged += d => { if (connector.CurrentTime < nextTime && connector.CurrentTime < stopTime) { return; } var steps = (connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To <TimeSpan>(); this.GuiAsync(() => progressBar.Value = steps); }; connector.StateChanged += () => { if (connector.State == EmulationStates.Stopped) { candleManager.Stop(series); strategy.Stop(); logManager.Dispose(); _connectors.Clear(); SetIsEnabled(false); this.GuiAsync(() => { if (connector.IsFinished) { progressBar.Value = progressBar.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - _startEmulationTime)); } else { MessageBox.Show(this, LocalizedStrings.cancelled); } }); } else if (connector.State == EmulationStates.Started) { SetIsEnabled(true); } }; if (ShowDepth.IsChecked == true) { MarketDepth.UpdateFormat(security); connector.NewMessage += message => { var quoteMsg = message as QuoteChangeMessage; if (quoteMsg != null) { MarketDepth.UpdateDepth(quoteMsg); } }; } _connectors.Add(connector); progressBar.Value = 0; } _startEmulationTime = DateTime.Now; // start emulation foreach (var connector in _connectors) { // raise NewSecurities and NewPortfolio for full fill strategy properties connector.Connect(); // 1 cent commission for trade connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); } TabControl.Items.Cast <TabItem>().First(i => i.Visibility == Visibility.Visible).IsSelected = true; }
private void LoadData() { _candle = null; _lastPrice = 0m; _allCandles.Clear(); var id = new SecurityIdGenerator().Split(SecurityId.Text); _security = new Security { Id = SecurityId.Text, PriceStep = _priceStep, Board = _exchangeInfoProvider.GetExchangeBoard(id.BoardCode) }; Chart.Reset(new IChartElement[] { _candleElement1 }); var storage = new StorageRegistry(); var maxDays = 2; BusyIndicator.IsBusy = true; var path = HistoryPath.Folder; Task.Factory.StartNew(() => { var date = DateTime.MinValue; foreach (var tick in storage.GetTickMessageStorage(_security, new LocalMarketDataDrive(path)).Load()) { AppendTick(_security, tick); _lastTime = tick.ServerTime; if (date != tick.ServerTime.Date) { date = tick.ServerTime.Date; var str = date.To <string>(); this.GuiAsync(() => BusyIndicator.BusyContent = str); maxDays--; if (maxDays == 0) { break; } } } }) .ContinueWith(t => { if (t.Exception != null) { Error(t.Exception.Message); } this.GuiAsync(() => { BusyIndicator.IsBusy = false; Chart.IsAutoRange = false; _areaComb.YAxises.First().AutoRange = false; }); }, TaskScheduler.FromCurrentSynchronizationContext()); }
/// <summary> /// Synchronize securities with storage. /// </summary> /// <param name="drives">Storage drives.</param> /// <param name="entityRegistry">The storage of trade objects.</param> /// <param name="newSecurity">The handler through which a new instrument will be passed.</param> /// <param name="updateProgress">The handler through which a progress change will be passed.</param> /// <param name="addLog">The handler through which a new log message be passed.</param> /// <param name="isCancelled">The handler which returns an attribute of search cancel.</param> public static void SynchronizeSecurities(this IEnumerable <IMarketDataDrive> drives, IEntityRegistry entityRegistry, Action <Security> newSecurity, Action <int, int> updateProgress, Action <LogMessage> addLog, Func <bool> isCancelled) { if (drives == null) { throw new ArgumentNullException(nameof(drives)); } if (entityRegistry == null) { throw new ArgumentNullException(nameof(entityRegistry)); } if (newSecurity == null) { throw new ArgumentNullException(nameof(newSecurity)); } if (updateProgress == null) { throw new ArgumentNullException(nameof(updateProgress)); } if (addLog == null) { throw new ArgumentNullException(nameof(addLog)); } if (isCancelled == null) { throw new ArgumentNullException(nameof(isCancelled)); } var securityPaths = new List <string>(); var progress = 0; foreach (var dir in drives.Select(drive => drive.Path).Distinct()) { foreach (var letterDir in InteropHelper.GetDirectories(dir)) { if (isCancelled()) { break; } var name = Path.GetFileName(letterDir); if (name == null || name.Length != 1) { continue; } securityPaths.AddRange(InteropHelper.GetDirectories(letterDir)); } if (isCancelled()) { break; } } if (isCancelled()) { return; } // кол-во проходов по директории для создания инструмента var iterCount = securityPaths.Count; updateProgress(0, iterCount); var logSource = ConfigManager.GetService <LogManager>().Application; var securityIdGenerator = new SecurityIdGenerator(); var securities = entityRegistry.Securities.ToDictionary(s => s.Id, s => s, StringComparer.InvariantCultureIgnoreCase); foreach (var securityPath in securityPaths) { if (isCancelled()) { break; } var securityId = Path.GetFileName(securityPath).FolderNameToSecurityId(); var isNew = false; var security = securities.TryGetValue(securityId); if (security == null) { var firstDataFile = Directory.EnumerateDirectories(securityPath) .SelectMany(d => Directory.EnumerateFiles(d, "*.bin") .Concat(Directory.EnumerateFiles(d, "*.csv")) .OrderBy(f => Path.GetExtension(f).CompareIgnoreCase(".bin") ? 0 : 1)) .FirstOrDefault(); if (firstDataFile != null) { var id = securityIdGenerator.Split(securityId); decimal priceStep; if (Path.GetExtension(firstDataFile).CompareIgnoreCase(".bin")) { try { priceStep = File.ReadAllBytes(firstDataFile).Range(6, 16).To <decimal>(); } catch (Exception ex) { throw new InvalidOperationException(LocalizedStrings.Str2929Params.Put(firstDataFile), ex); } } else { priceStep = 0.01m; } security = new Security { Id = securityId, PriceStep = priceStep, Name = id.SecurityCode, Code = id.SecurityCode, Board = ExchangeBoard.GetOrCreateBoard(id.BoardCode), }; securities.Add(securityId, security); entityRegistry.Securities.Save(security); newSecurity(security); isNew = true; } } updateProgress(progress++, iterCount); if (isNew) { addLog(new LogMessage(logSource, TimeHelper.NowWithOffset, LogLevels.Info, LocalizedStrings.Str2930Params.Put(security))); } } }
private void StartBtnClick(object sender, RoutedEventArgs e) { InitChart(); if (HistoryPath.Text.IsEmpty() || !Directory.Exists(HistoryPath.Text)) { MessageBox.Show(this, LocalizedStrings.Str3014); return; } var secGen = new SecurityIdGenerator(); var secIdParts = secGen.Split(SecId.Text); var storageRegistry = new StorageRegistry() { DefaultDrive = new LocalMarketDataDrive(HistoryPath.Text) }; var startTime = ((DateTime)From.Value).ChangeKind(DateTimeKind.Utc); var stopTime = ((DateTime)To.Value).ChangeKind(DateTimeKind.Utc); //var logManager = new LogManager(); //var fileLogListener = new FileLogListener("sample.log"); //logManager.Listeners.Add(fileLogListener); //logManager.Listeners.Add(new DebugLogListener()); // for track logs in output window in Vusial Studio (poor performance). var maxDepth = 5; var maxVolume = 5; var secCode = secIdParts.Item1; var board = ExchangeBoard.GetOrCreateBoard(secIdParts.Item2); var progressBar = TicksTestingProcess; var progressStep = ((stopTime - startTime).Ticks / 100).To <TimeSpan>(); progressBar.Value = 0; progressBar.Maximum = 100; var statistic = TicksParameterGrid; var security = new Security() { Id = SecId.Text, Code = secCode, Board = board, }; var portfolio = new Portfolio() { Name = "test account", BeginValue = 1000000, }; var connector = new HistoryEmulationConnector(new[] { security }, new[] { portfolio }) { EmulationAdapter = { Emulator = { Settings = { MatchOnTouch = true, PortfolioRecalcInterval = TimeSpan.FromMilliseconds(100), SpreadSize = 1, }, LogLevel = LogLevels.Debug, }, LogLevel = LogLevels.Debug, }, HistoryMessageAdapter = { StorageRegistry = storageRegistry, StartDate = startTime, StopDate = stopTime, MarketTimeChangedInterval = TimeSpan.FromMilliseconds(50), }, }; //logManager.Sources.Add(connector); var candleManager = new CandleManager(connector); var series = new CandleSeries(typeof(RangeCandle), security, new Unit(100)); shortMa = new SimpleMovingAverage { Length = 10 }; shortElem = new ChartIndicatorElement { Color = Colors.Coral, ShowAxisMarker = false, FullTitle = shortMa.ToString() }; bufferedChart.AddElement(area, shortElem); longMa = new SimpleMovingAverage { Length = 30 }; longElem = new ChartIndicatorElement { ShowAxisMarker = false, FullTitle = longMa.ToString() }; bufferedChart.AddElement(area, longElem); var strategy = new SmaStrategy(bufferedChart, candlesElem, tradesElem, shortMa, shortElem, longMa, longElem, series) { Volume = 1, Portfolio = portfolio, Security = security, Connector = connector, LogLevel = LogLevels.Debug, UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>() }; //logManager.Sources.Add(strategy); connector.NewSecurities += securities => { if (securities.All(s => s != security)) { return; } connector.RegisterMarketDepth(security); connector.RegisterMarketDepth(new TrendMarketDepthGenerator(connector.GetSecurityId(security)) { Interval = TimeSpan.FromMilliseconds(100), // order book freq refresh is 1 sec MaxAsksDepth = maxDepth, MaxBidsDepth = maxDepth, UseTradeVolume = true, MaxVolume = maxVolume, MinSpreadStepCount = 1, // min spread generation is 2 pips MaxSpreadStepCount = 1, // max spread generation size (prevent extremely size) MaxPriceStepCount = 3 // pips size, }); connector.RegisterTrades(security); connector.RegisterSecurity(security); strategy.Start(); candleManager.Start(series); connector.Start(); }; statistic.Parameters.Clear(); statistic.Parameters.AddRange(strategy.StatisticManager.Parameters); var pnlCurve = Curve.CreateCurve(LocalizedStrings.PnL + " " + StrategyName, Colors.Cyan, EquityCurveChartStyles.Area); var unrealizedPnLCurve = Curve.CreateCurve(LocalizedStrings.PnLUnreal + StrategyName, Colors.Black); var commissionCurve = Curve.CreateCurve(LocalizedStrings.Str159 + " " + StrategyName, Colors.Red, EquityCurveChartStyles.DashedLine); var posItems = PositionCurve.CreateCurve(StrategyName, Colors.Crimson); strategy.PnLChanged += () => { var pnl = new EquityData() { Time = strategy.CurrentTime, Value = strategy.PnL - strategy.Commission ?? 0 }; var unrealizedPnL = new EquityData() { Time = strategy.CurrentTime, Value = strategy.PnLManager.UnrealizedPnL }; var commission = new EquityData() { Time = strategy.CurrentTime, Value = strategy.Commission ?? 0 }; pnlCurve.Add(pnl); unrealizedPnLCurve.Add(unrealizedPnL); commissionCurve.Add(commission); }; strategy.PositionChanged += () => posItems.Add(new EquityData { Time = strategy.CurrentTime, Value = strategy.Position }); var nextTime = startTime + progressStep; connector.MarketTimeChanged += d => { if (connector.CurrentTime < nextTime && connector.CurrentTime < stopTime) { return; } var steps = (connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To <TimeSpan>(); this.GuiAsync(() => progressBar.Value = steps); }; connector.StateChanged += () => { if (connector.State == EmulationStates.Stopped) { candleManager.Stop(series); strategy.Stop(); //logManager.Dispose(); SetIsEnabled(false); this.GuiAsync(() => { if (connector.IsFinished) { progressBar.Value = progressBar.Maximum; MessageBox.Show(this, LocalizedStrings.Str3024.Put(DateTime.Now - startEmulationTime)); } else { MessageBox.Show(this, LocalizedStrings.cancelled); } }); } else if (connector.State == EmulationStates.Started) { SetIsEnabled(true); } }; progressBar.Value = 0; startEmulationTime = DateTime.Now; connector.Connect(); connector.SendInMessage(new CommissionRuleMessage() { Rule = new CommissionPerTradeRule { Value = 0.01m } }); }
//private void InitOrderLogBuilders(DateTime loadDate) //{ // if (StorageRegistry == null || !MarketEmulator.Settings.UseMarketDepth) // return; // foreach (var security in RegisteredMarketDepths) // { // var builder = _orderLogBuilders.TryGetValue(security); // if (builder == null) // continue; // // стакан из ОЛ строиться начиная с 18.45 предыдущей торговой сессии // var olDate = loadDate.Date; // do // { // olDate -= TimeSpan.FromDays(1); // } // while (!ExchangeBoard.Forts.WorkingTime.IsTradeDate(olDate)); // olDate += new TimeSpan(18, 45, 0); // foreach (var item in StorageRegistry.GetOrderLogStorage(security, Drive).Load(olDate, loadDate - TimeSpan.FromTicks(1))) // { // builder.Update(item); // } // } //} private Security FindSecurity(SecurityId secId) { return(this.LookupById(SecurityIdGenerator.GenerateId(secId.SecurityCode, secId.BoardCode))); }
private void OnSecurity(int tableId, object[] values) { var table = SecuritiesTable; var secCode = table.GetValue <string>(values, AlorSecurityColumns.Code); var secBoard = ExchangeBoard.GetBoard(table.GetValue <string>(values, AlorSecurityColumns.Board)); var id = SecurityIdGenerator.GenerateId(secCode, secBoard); GetSecurity(id, name => { var security = EntityFactory.CreateSecurity(name); security.Code = table.GetValue <string>(values, AlorSecurityColumns.Code); security.Name = table.GetValue <string>(values, AlorSecurityColumns.Name); security.ShortName = table.GetValue <string>(values, AlorSecurityColumns.ShortName); security.Board = secBoard; security.ExpiryDate = table.GetValue <DateTime>(values, AlorSecurityColumns.ExecutionDate); security.Decimals = table.GetValue <int>(values, AlorSecurityColumns.Decimals); security.Multiplier = table.GetValue <int>(values, AlorSecurityColumns.MinLotSize); security.MinStepPrice = table.GetValue <decimal>(values, AlorSecurityColumns.MinStepPrice); security.PriceStep = table.GetValue <decimal>(values, AlorSecurityColumns.MinStep); return(security); }, security => { using (security.BeginUpdate()) { security.BestBid = new Quote { OrderDirection = OrderDirections.Buy, Price = table.GetValue <decimal>(values, AlorSecurityColumns.BestBidPrice), Volume = table.GetValue <int>(values, AlorSecurityColumns.BestBidVolume), Security = security, }; security.BestAsk = new Quote { OrderDirection = OrderDirections.Sell, Price = table.GetValue <decimal>(values, AlorSecurityColumns.BestAskPrice), Volume = table.GetValue <int>(values, AlorSecurityColumns.BestBidVolume), Security = security, }; security.OpenPrice = table.GetValue <decimal>(values, AlorSecurityColumns.OpenPrice); security.HighPrice = table.GetValue <decimal>(values, AlorSecurityColumns.HighPrice); security.LowPrice = table.GetValue <decimal>(values, AlorSecurityColumns.LowPrice); // NOTE: security.SettlementDate = table.GetValue <DateTime>(values, AlorSecurityColumns.CancellationDate); if (table.GetValue <string>(values, AlorSecurityColumns.OptionType).Trim().IsEmpty() || table.GetValue <string>(values, AlorSecurityColumns.OptionType) == "F") { if (table.GetValue <string>(values, AlorSecurityColumns.FutureCode).Trim().IsEmpty()) { security.Type = SecurityTypes.Stock; } else { security.Type = SecurityTypes.Future; security.ExtensionInfo[AlorSecurityColumns.FutureCode] = table.GetValue <string>(values, AlorSecurityColumns.FutureCode); } } else { security.Type = SecurityTypes.Option; security.ExtensionInfo[AlorSecurityColumns.OptionType] = table.GetValue <string>(values, AlorSecurityColumns.OptionType); } security.LastTrade = new Trade { Security = security, Time = table.GetValue <DateTime>(values, AlorSecurityColumns.LastTradeTime), Price = table.GetValue <decimal>(values, AlorSecurityColumns.LastTradePrice), Volume = table.GetValue <int>(values, AlorSecurityColumns.LastTradeVolume), }; security.MinPrice = table.GetValue <decimal>(values, AlorSecurityColumns.LowPriceLimit); security.MaxPrice = table.GetValue <decimal>(values, AlorSecurityColumns.HighPriceLimit); security.MarginBuy = table.GetValue <decimal>(values, AlorSecurityColumns.BuyDeposit); security.MarginSell = table.GetValue <decimal>(values, AlorSecurityColumns.SellDeposit); security.State = table.GetValue <string>(values, AlorSecurityColumns.TradingStatus) == "O" ? SecurityStates.Trading : SecurityStates.Stoped; table.FillNonMandatoryInfo(security, values); } return(true); }); }
private void LoadData() { _lastPrice = 0m; _candles.Clear(); var id = new SecurityIdGenerator().Split(SecurityId); _security = new Security { Id = SecurityId, PriceStep = PriceStep, Board = ExchangeBoard.GetBoard(id.BoardCode) }; Chart.Reset(new IChartElement[] { _candleElement }); var storage = new StorageRegistry(); var maxDays = 50; BusyIndicator.IsBusy = true; var path = HistoryPath; _curCandleNum = 0; Task.Factory.StartNew(() => { var date = DateTime.MinValue; foreach (var tick in storage.GetTickMessageStorage(_security, new LocalMarketDataDrive(path)).Load()) { if (date != tick.ServerTime.Date) { date = tick.ServerTime.Date; this.GuiAsync(() => BusyIndicator.BusyContent = $"Loading ticks for {date:dd MMM yyyy}..."); if (--maxDays == 0) { break; } } AppendTick(tick); } }) .ContinueWith(t => { this.GuiAsync(() => BusyIndicator.IsBusy = false); for (var i = 0; i < _candles.Count; i += CandlesPacketSize) { var data = new ChartDrawData(); var candles = _candles.GetRange(i, Math.Min(CandlesPacketSize, _candles.Count - i)).Select(c => c.ToCandle(TFSpan)); foreach (var candle in candles) { candle.State = CandleStates.Finished; var group = data.Group(candle.OpenTime); group.Add(_candleElement, candle); if (_indicatorElement != null) { group.Add(_indicatorElement, _indicator.Process((double)candle.ClosePrice)); } } Chart.Draw(data); } }) .ContinueWith(t => { if (t.Exception != null) { Error(t.Exception.Message); } _dataIsLoaded = true; this.GuiAsync(() => BusyIndicator.IsBusy = false); Chart.IsAutoRange = false; //_area.YAxises.FirstOrDefault().Do(a => a.AutoRange = false); }, TaskScheduler.FromCurrentSynchronizationContext()); }
private void StartButtonOnClick(object sender, RoutedEventArgs e) { _logManager.Sources.Clear(); _bufferedChart.ClearAreas(); Curve.Clear(); PositionCurve.Clear(); if (HistoryPathTextBox.Text.IsEmpty() || !Directory.Exists(HistoryPathTextBox.Text)) { MessageBox.Show("Wrong path."); return; } if (_connector != null && _connector.State != EmulationStates.Stopped) { MessageBox.Show("Already launched."); return; } if (Composition == null) { MessageBox.Show("No strategy selected."); return; } var secGen = new SecurityIdGenerator(); var secIdParts = secGen.Split(SecusityTextBox.Text); var secCode = secIdParts.SecurityCode; var board = ExchangeBoard.GetOrCreateBoard(secIdParts.BoardCode); var timeFrame = (TimeSpan)TimeFrameComboBox.SelectedItem; var useCandles = (string)MarketDataTypeComboBox.SelectedItem != "Ticks"; // create test security var security = new Security { Id = SecusityTextBox.Text, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(HistoryPathTextBox.Text) }; var startTime = ((DateTime)FromDatePicker.Value).ChangeKind(DateTimeKind.Utc); var stopTime = ((DateTime)ToDatePicke.Value).ChangeKind(DateTimeKind.Utc); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To <TimeSpan>(); // set ProgressBar bounds TicksAndDepthsProgress.Value = 0; TicksAndDepthsProgress.Maximum = 100; var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, secIdParts.SecurityCode == "RIZ2" ? 10m : 1) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; // create backtesting connector _connector = new HistoryEmulationConnector( new[] { security }, new[] { portfolio }) { EmulationAdapter = { Emulator = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = false, } } }, UseExternalCandleSource = useCandles, HistoryMessageAdapter = { StorageRegistry = storageRegistry, // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; //((ILogSource)_connector).LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info; _logManager.Sources.Add(_connector); var candleManager = !useCandles ? new CandleManager(new TradeCandleBuilderSourceEx(_connector)) : new CandleManager(_connector); // create strategy based on 80 5-min и 10 5-min var strategy = new DiagramStrategy { Volume = 1, Portfolio = portfolio, Security = security, Connector = _connector, //LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info, Composition = Composition, // by default interval is 1 min, // it is excessively for time range with several months UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>() }; strategy.SetChart(_bufferedChart); strategy.SetCandleManager(candleManager); _logManager.Sources.Add(strategy); strategy.OrderRegistering += OnStrategyOrderRegistering; strategy.OrderReRegistering += OnStrategyOrderReRegistering; strategy.OrderRegisterFailed += OnStrategyOrderRegisterFailed; strategy.StopOrderRegistering += OnStrategyOrderRegistering; strategy.StopOrderReRegistering += OnStrategyOrderReRegistering; strategy.StopOrderRegisterFailed += OnStrategyOrderRegisterFailed; strategy.NewMyTrades += OnStrategyNewMyTrade; var pnlCurve = Curve.CreateCurve(LocalizedStrings.PnL + " " + strategy.Name, Colors.DarkGreen, EquityCurveChartStyles.Area); var unrealizedPnLCurve = Curve.CreateCurve(LocalizedStrings.PnLUnreal + strategy.Name, Colors.Black); var commissionCurve = Curve.CreateCurve(LocalizedStrings.Str159 + " " + strategy.Name, Colors.Red, EquityCurveChartStyles.DashedLine); strategy.PnLChanged += () => { var pnl = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnL - strategy.Commission ?? 0 }; var unrealizedPnL = new EquityData { Time = strategy.CurrentTime, Value = strategy.PnLManager.UnrealizedPnL }; var commission = new EquityData { Time = strategy.CurrentTime, Value = strategy.Commission ?? 0 }; pnlCurve.Add(pnl); unrealizedPnLCurve.Add(unrealizedPnL); commissionCurve.Add(commission); }; var posItems = PositionCurve.CreateCurve(strategy.Name, Colors.DarkGreen); strategy.PositionChanged += () => posItems.Add(new EquityData { Time = strategy.CurrentTime, Value = strategy.Position }); _connector.NewSecurities += securities => { if (securities.All(s => s != security)) { return; } // fill level1 values _connector.SendInMessage(level1Info); //_connector.RegisterMarketDepth(security); if (!useCandles) { _connector.RegisterTrades(security); } // start strategy before emulation started strategy.Start(); // start historical data loading when connection established successfully and all data subscribed _connector.Start(); }; var nextTime = startTime + progressStep; // handle historical time for update ProgressBar _connector.MarketTimeChanged += d => { if (_connector.CurrentTime < nextTime && _connector.CurrentTime < stopTime) { return; } var steps = (_connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To <TimeSpan>(); this.GuiAsync(() => TicksAndDepthsProgress.Value = steps); }; _connector.StateChanged += () => { switch (_connector.State) { case EmulationStates.Stopped: strategy.Stop(); SetIsEnabled(false); this.GuiAsync(() => { if (_connector.IsFinished) { TicksAndDepthsProgress.Value = TicksAndDepthsProgress.Maximum; MessageBox.Show("Done."); } else { MessageBox.Show("Cancelled."); } }); break; case EmulationStates.Started: SetIsEnabled(true); break; } }; TicksAndDepthsProgress.Value = 0; // raise NewSecurities and NewPortfolio for full fill strategy properties _connector.Connect(); // 1 cent commission for trade _connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); }
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e) { Year.ItemsSource = Competition.AllYears; Year.SelectedItem = Competition.AllYears.Last(); Directory.CreateDirectory(_settingsDir); var ns = typeof(IIndicator).Namespace; var rendererTypes = typeof(Chart).Assembly .GetTypes() .Where(t => !t.IsAbstract && typeof(BaseChartIndicatorPainter).IsAssignableFrom(t)) .ToDictionary(t => t.Name); var indicators = typeof(IIndicator).Assembly .GetTypes() .Where(t => t.Namespace == ns && !t.IsAbstract && typeof(IIndicator).IsAssignableFrom(t)) .Select(t => new IndicatorType(t, rendererTypes.TryGetValue(t.Name + "Painter"))); Chart.IndicatorTypes.AddRange(indicators); var finamSecurities = Path.Combine(_settingsDir, "finam2.csv"); BusyIndicator.BusyContent = "Обновление инструментов..."; BusyIndicator.IsBusy = true; Task.Factory.StartNew(() => { File.Delete("finam.csv"); if (File.Exists(finamSecurities)) { CultureInfo.InvariantCulture.DoInCulture(() => { var idGen = new SecurityIdGenerator(); var securities = File.ReadAllLines(finamSecurities).Select(line => { var cells = line.SplitByComma(); var idParts = idGen.Split(cells[0]); return(new Security { Id = cells[0], Code = idParts.Item1, Board = ExchangeBoard.GetOrCreateBoard(idParts.Item2), ExtensionInfo = new Dictionary <object, object> { { FinamHistorySource.MarketIdField, cells[1].To <long>() }, { FinamHistorySource.SecurityIdField, cells[2].To <long>() }, }, PriceStep = cells[3].To <decimal?>(), Decimals = cells[4].To <int?>(), Currency = cells[5].To <CurrencyTypes?>(), }); }); foreach (var security in securities) { _securityProvider.Securities.Add(security); _securityStorage.Save(security); } }); } else { _finamHistorySource.Refresh(_securityStorage, new Security(), s => { }, () => false); var securities = _securityStorage.LookupAll().ToArray(); foreach (var security in securities) { _securityProvider.Securities.Add(security); } File.WriteAllLines(finamSecurities, securities.Where(s => !s.Id.Contains(',')).Select(s => "{0},{1},{2},{3},{4},{5}" .Put(s.Id, s.ExtensionInfo[FinamHistorySource.MarketIdField], s.ExtensionInfo[FinamHistorySource.SecurityIdField], s.PriceStep, s.Decimals, s.Currency))); } }) .ContinueWith(res => { BusyIndicator.IsBusy = false; if (res.Exception != null) { new MessageBoxBuilder() .Error() .Owner(this) .Text(res.Exception.ToString()) .Show(); } if (File.Exists(_settingsFile)) { var settings = CultureInfo.InvariantCulture.DoInCulture(() => new XmlSerializer <SettingsStorage>().Deserialize(_settingsFile).Load <Settings>()); Year.SelectedItem = settings.Year; Trader.Text = settings.Trader; From.Value = settings.From; To.Value = settings.To; Security1.Text = settings.Security1; Security2.Text = settings.Security2; Security3.Text = settings.Security3; Security4.Text = settings.Security4; TimeFrame.SelectedItem = settings.TimeFrame; Apart.IsChecked = settings.Apart; } else { Trader.Text = "Vasya"; Security1.Text = "RIZ5@FORTS"; //Trader.Text = "iZotov"; //Security1.Text = "SPZ5@FORTS"; //Security2.Text = "SIZ5@FORTS"; //From.Value = new DateTime(2014, 09, 16); Apart.IsChecked = true; } }, TaskScheduler.FromCurrentSynchronizationContext()); }
private void StartEmulation() { if (_connector != null && _connector.State != EmulationStates.Stopped) { throw new InvalidOperationException(LocalizedStrings.Str3015); } if (Strategy == null) { throw new InvalidOperationException("Strategy not selected."); } var strategy = (EmulationDiagramStrategy)Strategy; if (strategy.MarketDataSettings == null) { throw new InvalidOperationException(LocalizedStrings.Str3014); } strategy .Composition .Parameters .ForEach(p => { if (p.Type == typeof(Security) && p.Value == null) { throw new InvalidOperationException(LocalizedStrings.Str1380); } }); strategy.Reset(); Reset(); var securityId = "empty@empty"; var secGen = new SecurityIdGenerator(); var secIdParts = secGen.Split(securityId); var secCode = secIdParts.SecurityCode; var board = ExchangeBoard.GetOrCreateBoard(secIdParts.BoardCode); var timeFrame = strategy.CandlesTimeFrame; var useCandles = strategy.MarketDataSource == MarketDataSource.Candles; // create test security var security = new Security { Id = securityId, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; // storage to historical data var storageRegistry = new StudioStorageRegistry { MarketDataSettings = strategy.MarketDataSettings }; var startTime = strategy.StartDate.ChangeKind(DateTimeKind.Utc); var stopTime = strategy.StopDate.ChangeKind(DateTimeKind.Utc); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To <TimeSpan>(); // set ProgressBar bounds TicksAndDepthsProgress.Value = 0; TicksAndDepthsProgress.Maximum = 100; // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; var securityProvider = ConfigManager.GetService <ISecurityProvider>(); // create backtesting connector _connector = new HistoryEmulationConnector(securityProvider, new[] { portfolio }, new StorageRegistry()) { EmulationAdapter = { Emulator = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = strategy.MatchOnTouch, IsSupportAtomicReRegister = strategy.IsSupportAtomicReRegister, Latency = strategy.EmulatoinLatency, } } }, UseExternalCandleSource = useCandles, HistoryMessageAdapter = { StorageRegistry = storageRegistry, StorageFormat = strategy.StorageFormat, // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; ((ILogSource)_connector).LogLevel = strategy.DebugLog ? LogLevels.Debug : LogLevels.Info; ConfigManager.GetService <LogManager>().Sources.Add(_connector); strategy.Volume = 1; strategy.Portfolio = portfolio; strategy.Security = security; strategy.Connector = _connector; strategy.LogLevel = strategy.DebugLog ? LogLevels.Debug : LogLevels.Info; // by default interval is 1 min, // it is excessively for time range with several months strategy.UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>(); strategy.SetCandleManager(new CandleManager(_connector)); _connector.NewSecurity += s => { //TODO send real level1 message var level1Info = new Level1ChangeMessage { SecurityId = s.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, secIdParts.SecurityCode == "RIZ2" ? 10m : 1) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // fill level1 values _connector.SendInMessage(level1Info); if (strategy.UseMarketDepths) { _connector.RegisterMarketDepth(security); if ( // if order book will be generated strategy.GenerateDepths || // of backtesting will be on candles useCandles ) { // if no have order book historical data, but strategy is required, // use generator based on last prices _connector.RegisterMarketDepth(new TrendMarketDepthGenerator(_connector.GetSecurityId(s)) { Interval = TimeSpan.FromSeconds(1), // order book freq refresh is 1 sec MaxAsksDepth = strategy.MaxDepths, MaxBidsDepth = strategy.MaxDepths, UseTradeVolume = true, MaxVolume = strategy.MaxVolume, MinSpreadStepCount = 2, // min spread generation is 2 pips MaxSpreadStepCount = 5, // max spread generation size (prevent extremely size) MaxPriceStepCount = 3 // pips size, }); } } }; var nextTime = startTime + progressStep; // handle historical time for update ProgressBar _connector.MarketTimeChanged += d => { if (_connector.CurrentTime < nextTime && _connector.CurrentTime < stopTime) { return; } var steps = (_connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To <TimeSpan>(); this.GuiAsync(() => TicksAndDepthsProgress.Value = steps); }; _connector.LookupSecuritiesResult += (ss) => { if (strategy.ProcessState != ProcessStates.Stopped) { return; } // start strategy before emulation started strategy.Start(); // start historical data loading when connection established successfully and all data subscribed _connector.Start(); }; _connector.StateChanged += () => { switch (_connector.State) { case EmulationStates.Stopped: strategy.Stop(); this.GuiAsync(() => { if (_connector.IsFinished) { TicksAndDepthsProgress.Value = TicksAndDepthsProgress.Maximum; } }); break; case EmulationStates.Started: break; } }; TicksAndDepthsProgress.Value = 0; DiagramDebuggerControl.Debugger.IsEnabled = true; // raise NewSecurities and NewPortfolio for full fill strategy properties _connector.Connect(); // 1 cent commission for trade _connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); }
private void LoadData() { _lastPrice = 0m; _candles.Clear(); var id = new SecurityIdGenerator().Split(SecurityId); _security = new Security { Id = SecurityId, PriceStep = PriceStep, Board = ExchangeBoard.GetBoard(id.BoardCode) }; Chart.Reset(new IChartElement[] { _candleElement }); var storage = new StorageRegistry(); var maxDays = 1; BusyIndicator.IsBusy = true; var path = HistoryPath; _curCandleNum = 0; Task.Factory.StartNew(() => { var date = DateTime.MinValue; foreach (var tick in storage.GetTickMessageStorage(_security, new LocalMarketDataDrive(path)).Load()) { if (date != tick.ServerTime.Date) { date = tick.ServerTime.Date; this.GuiAsync(() => BusyIndicator.BusyContent = $"Loading ticks for {date:dd MMM yyyy}..."); if (maxDays-- == 0) { break; } } AppendTick(tick); } }) .ContinueWith(t => { this.GuiAsync(() => BusyIndicator.IsBusy = false); for (var i = 0; i < _candles.Count; i += CandlesPacketSize) { var candles = _candles.GetRange(i, Math.Min(CandlesPacketSize, _candles.Count - i)).Select(c => c.ToCandle(TFSpan)); Chart.Draw(candles.Select(c => { c.State = CandleStates.Finished; var dict = (IDictionary <IChartElement, object>) new Dictionary <IChartElement, object> { { _candleElement, c } }; if (_indicatorElement != null) { dict.Add(_indicatorElement, _indicator.Process((double)c.ClosePrice)); } if (_tradeElement != null && _curCandleNum++ % TradeEveryNCandles == 0) { dict.Add(_tradeElement, new MyTrade { Trade = new Trade { Security = _security, Volume = 1, Price = c.LowPrice, OrderDirection = Sides.Buy, Time = c.OpenTime }, Order = new Order { Security = _security, Direction = Sides.Buy } }); } return(RefTuple.Create(c.OpenTime, dict)); }).ToArray()); } }) .ContinueWith(t => { if (t.Exception != null) { Error(t.Exception.Message); } _dataIsLoaded = true; this.GuiAsync(() => BusyIndicator.IsBusy = false); Chart.IsAutoRange = false; //_area.YAxises.FirstOrDefault().Do(a => a.AutoRange = false); }, TaskScheduler.FromCurrentSynchronizationContext()); }
private void OkClick(object sender, RoutedEventArgs e) { var volumeStep = VolumeStep.Text.To <decimal?>(); var priceStep = PriceStep.Text.To <decimal?>(); var type = TypeCtrl.SelectedType; if (Securities == null) { if (Code.Text.IsEmpty()) { ShowError(LocalizedStrings.Str2923); return; } //if (SecName.Text.IsEmpty()) //{ // ShowError("Не заполнено название инструмента."); // return; //} if (volumeStep == null) { ShowError(LocalizedStrings.Str2924); return; } if (priceStep == null) { ShowError(LocalizedStrings.Str2925); return; } if (ExchangeCtrl.SelectedBoard == null) { ShowError(LocalizedStrings.Str2926); return; } var security = Security; security.Code = Code.Text; if (!SecName.Text.IsEmpty()) { security.Name = SecName.Text; } security.VolumeStep = volumeStep.Value; security.PriceStep = priceStep.Value; security.Board = ExchangeCtrl.SelectedBoard; security.Type = TypeCtrl.SelectedType; if (security.Id.IsEmpty()) { var id = new SecurityIdGenerator().GenerateId(security.Code, security.Board); var isExist = _entityRegistry.Securities.ReadById(id) != null; if (isExist) { new MessageBoxBuilder() .Text(LocalizedStrings.Str2927Params.Put(id)) .Owner(this) .Warning() .Show(); return; } security.Id = id; security.ExtensionInfo = new Dictionary <object, object>(); } _entityRegistry.Securities.Save(security); } else { foreach (var security in Securities) { if (volumeStep != null) { security.VolumeStep = volumeStep.Value; } if (priceStep != null) { security.PriceStep = priceStep.Value; } if (type != null) { security.Type = type.Value; } _entityRegistry.Securities.Save(security); } } DialogResult = true; }
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e) { Year.ItemsSource = Competition.AllYears; Year.SelectedItem = Competition.AllYears.Last(); var ns = typeof(IIndicator).Namespace; var rendererTypes = typeof(Chart).Assembly .GetTypes() .Where(t => !t.IsAbstract && typeof(BaseChartIndicatorPainter).IsAssignableFrom(t)) .ToDictionary(t => t.Name); var indicators = typeof(IIndicator).Assembly .GetTypes() .Where(t => t.Namespace == ns && !t.IsAbstract && typeof(IIndicator).IsAssignableFrom(t)) .Select(t => new IndicatorType(t, rendererTypes.TryGetValue(t.Name + "Painter"))); Chart.IndicatorTypes.AddRange(indicators); const string finamSecurities = "finam.csv"; if (File.Exists(finamSecurities)) { var idGen = new SecurityIdGenerator(); var securities = File.ReadAllLines(finamSecurities).Select(line => { var cells = line.SplitByComma(); var idParts = idGen.Split(cells[0]); return(new Security { Id = cells[0], Code = idParts.Item1, Board = ExchangeBoard.GetOrCreateBoard(idParts.Item2), ExtensionInfo = new Dictionary <object, object> { { FinamHistorySource.MarketIdField, cells[1].To <long>() }, { FinamHistorySource.SecurityIdField, cells[2].To <long>() }, } }); }); foreach (var security in securities) { _securities.Add(security); _securityStorage.Save(security); } } else { _finamHistorySource.Refresh(_securityStorage, new Security(), s => { }, () => false); var securities = _securityStorage.LookupAll().ToArray(); foreach (var security in securities) { _securities.Add(security); } File.WriteAllLines(finamSecurities, securities.Where(s => !s.Id.Contains(',')).Select(s => "{0},{1},{2}" .Put(s.Id, s.ExtensionInfo[FinamHistorySource.MarketIdField], s.ExtensionInfo[FinamHistorySource.SecurityIdField]))); } Trader.Text = "Bull"; Security.Text = "SIZ4@FORTS"; From.Value = new DateTime(2014, 09, 16); }
private void Sync_Click(object sender, RoutedEventArgs e) { if (_token != null) { StopSync(); return; } Sync.Content = LocalizedStrings.Str2890; _token = new CancellationTokenSource(); Task.Factory.StartNew(() => { var securityPaths = new List <string>(); foreach (var dir in DriveCache.Instance.AllDrives .OfType <LocalMarketDataDrive>() .Select(drive => drive.Path) .Distinct()) { foreach (var letterDir in InteropHelper.GetDirectories(dir)) { if (_token.IsCancellationRequested) { break; } var name = Path.GetFileName(letterDir); if (name == null || name.Length != 1) { continue; } securityPaths.AddRange(InteropHelper.GetDirectories(letterDir)); } if (_token.IsCancellationRequested) { break; } } if (_token.IsCancellationRequested) { return; } var iterCount = securityPaths.Count + // кол-во проходов по директории для создания инструмента DriveCache.Instance.AllDrives.Count() * (EntityRegistry.Securities.Count + securityPaths.Count); // кол-во сбросов кэша дат this.GuiSync(() => Progress.Maximum = iterCount); var logSource = ConfigManager.GetService <LogManager>().Application; var securityIdGenerator = new SecurityIdGenerator(); var securities = EntityRegistry.Securities.ToDictionary(s => s.Id, s => new KeyValuePair <Security, bool>(s, true), StringComparer.InvariantCultureIgnoreCase); foreach (var securityPath in securityPaths) { if (_token.IsCancellationRequested) { break; } var securityId = Path.GetFileName(securityPath).FolderNameToSecurityId(); var isNew = false; var security = securities.TryGetValue(securityId).Key; if (security == null) { var firstDataFile = Directory.EnumerateDirectories(securityPath) .SelectMany(d => Directory.EnumerateFiles(d, "*.bin") .Concat(Directory.EnumerateFiles(d, "*.csv")) .OrderBy(f => Path.GetExtension(f).CompareIgnoreCase(".bin") ? 0 : 1)) .FirstOrDefault(); if (firstDataFile != null) { var info = securityIdGenerator.Split(securityId); decimal priceStep; if (Path.GetExtension(firstDataFile).CompareIgnoreCase(".bin")) { try { priceStep = File.ReadAllBytes(firstDataFile).Range(6, 16).To <decimal>(); } catch (Exception ex) { throw new InvalidOperationException(LocalizedStrings.Str2929Params.Put(firstDataFile), ex); } } else { priceStep = 0.01m; } security = new Security { Id = securityId, PriceStep = priceStep, Name = info.Item1, Code = info.Item1, Board = ExchangeBoard.GetOrCreateBoard(info.Item2), ExtensionInfo = new Dictionary <object, object>() }; securities.Add(securityId, new KeyValuePair <Security, bool>(security, false)); isNew = true; } } this.GuiSync(() => { Progress.Value++; if (isNew) { Logs.Messages.Add(new LogMessage(logSource, TimeHelper.Now, LogLevels.Info, LocalizedStrings.Str2930Params.Put(security))); } }); } EntityRegistry.Securities.AddRange(securities.Values.Where(p => !p.Value).Select(p => p.Key)); if (_token.IsCancellationRequested) { return; } var dataTypes = new Dictionary <Type, object> { { typeof(ExecutionMessage), ExecutionTypes.Tick }, { typeof(ExecutionMessage), ExecutionTypes.OrderLog }, { typeof(ExecutionMessage), ExecutionTypes.Order }, { typeof(ExecutionMessage), ExecutionTypes.Trade }, { typeof(QuoteChangeMessage), null }, { typeof(Level1ChangeMessage), null }, { typeof(NewsMessage), null } }; var formats = Enumerator.GetValues <StorageFormats>(); foreach (var drive in DriveCache.Instance.AllDrives) { foreach (var security in EntityRegistry.Securities) { foreach (var dataType in dataTypes) { foreach (var format in formats) { if (_token.IsCancellationRequested) { break; } drive.GetStorageDrive(security.ToSecurityId(), dataType.Key, dataType.Value, format).ClearDatesCache(); } } if (_token.IsCancellationRequested) { break; } this.GuiSync(() => { Progress.Value++; Logs.Messages.Add(new LogMessage(logSource, TimeHelper.Now, LogLevels.Info, LocalizedStrings.Str2931Params.Put(security, drive.Path))); }); } if (_token.IsCancellationRequested) { break; } } }, _token.Token) .ContinueWithExceptionHandling(this, res => { Sync.Content = LocalizedStrings.Str2932; Sync.IsEnabled = true; Progress.Value = 0; _token = null; }); }
private void StartEmulation() { if (_connector != null && _connector.State != EmulationStates.Stopped) { throw new InvalidOperationException(LocalizedStrings.Str3015); } if (Strategy == null) { throw new InvalidOperationException("Strategy not selected."); } var strategy = Strategy; if (strategy.DataPath.IsEmpty() || !Directory.Exists(strategy.DataPath)) { throw new InvalidOperationException(LocalizedStrings.Str3014); } strategy.Reset(); OrderGrid.Orders.Clear(); MyTradeGrid.Trades.Clear(); _pnlCurve.Clear(); _unrealizedPnLCurve.Clear(); _commissionCurve.Clear(); _posItems.Clear(); var secGen = new SecurityIdGenerator(); var secIdParts = secGen.Split(strategy.SecurityId); var secCode = secIdParts.SecurityCode; var board = ExchangeBoard.GetOrCreateBoard(secIdParts.BoardCode); var timeFrame = strategy.CandlesTimeFrame; var useCandles = strategy.MarketDataSource == MarketDataSource.Candles; // create test security var security = new Security { Id = strategy.SecurityId, // sec id has the same name as folder with historical data Code = secCode, Board = board, }; // storage to historical data var storageRegistry = new StorageRegistry { // set historical path DefaultDrive = new LocalMarketDataDrive(strategy.DataPath) }; var startTime = strategy.StartDate.ChangeKind(DateTimeKind.Utc); var stopTime = strategy.StopDate.ChangeKind(DateTimeKind.Utc); // ProgressBar refresh step var progressStep = ((stopTime - startTime).Ticks / 100).To <TimeSpan>(); // set ProgressBar bounds TicksAndDepthsProgress.Value = 0; TicksAndDepthsProgress.Maximum = 100; var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, secIdParts.SecurityCode == "RIZ2" ? 10m : 1) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // test portfolio var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; // create backtesting connector _connector = new HistoryEmulationConnector( new[] { security }, new[] { portfolio }) { EmulationAdapter = { Emulator = { Settings = { // match order if historical price touched our limit order price. // It is terned off, and price should go through limit order price level // (more "severe" test mode) MatchOnTouch = false, } } }, UseExternalCandleSource = useCandles, HistoryMessageAdapter = { StorageRegistry = storageRegistry, // set history range StartDate = startTime, StopDate = stopTime, }, // set market time freq as time frame MarketTimeChangedInterval = timeFrame, }; //((ILogSource)_connector).LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info; ConfigManager.GetService <LogManager>().Sources.Add(_connector); var candleManager = !useCandles ? new CandleManager(new TradeCandleBuilderSourceEx(_connector)) : new CandleManager(_connector); strategy.Volume = 1; strategy.Portfolio = portfolio; strategy.Security = security; strategy.Connector = _connector; //LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info, // by default interval is 1 min, // it is excessively for time range with several months strategy.UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>(); strategy.SetChart(_bufferedChart); strategy.SetCandleManager(candleManager); _connector.NewSecurities += securities => { if (securities.All(s => s != security)) { return; } // fill level1 values _connector.SendInMessage(level1Info); //_connector.RegisterMarketDepth(security); if (!useCandles) { _connector.RegisterTrades(security); } // start strategy before emulation started strategy.Start(); // start historical data loading when connection established successfully and all data subscribed _connector.Start(); }; var nextTime = startTime + progressStep; // handle historical time for update ProgressBar _connector.MarketTimeChanged += d => { if (_connector.CurrentTime < nextTime && _connector.CurrentTime < stopTime) { return; } var steps = (_connector.CurrentTime - startTime).Ticks / progressStep.Ticks + 1; nextTime = startTime + (steps * progressStep.Ticks).To <TimeSpan>(); this.GuiAsync(() => TicksAndDepthsProgress.Value = steps); }; _connector.StateChanged += () => { switch (_connector.State) { case EmulationStates.Stopped: strategy.Stop(); this.GuiAsync(() => { if (_connector.IsFinished) { TicksAndDepthsProgress.Value = TicksAndDepthsProgress.Maximum; } }); break; case EmulationStates.Started: break; } }; TicksAndDepthsProgress.Value = 0; // raise NewSecurities and NewPortfolio for full fill strategy properties _connector.Connect(); // 1 cent commission for trade _connector.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); }