private void StartBtnClick(object sender, RoutedEventArgs e) { InitChart(); if (HistoryPath.Text.IsEmpty() || !Directory.Exists(HistoryPath.Text)) { 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.Text) }; 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); } // 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>(); 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 = security.ToSecurityId(), 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, }, // 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, 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 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 bool joinedEdge = false;//test public void CalcPipePosition(Transaction tr, ObjectId tinSurfId, double defaultDepth, bool sameDepthEveryPt, BlockTableRecord ms) { //Нужно учитывать размеры колодцев при расчете положения ребра! //Колодцы бывают просто цилиндрические и просто коробчатые //Учитывать Rotation, BoundingShape, InnerDiameterOrWidth, InnerLength //Уточнить кривую в плане по которой идет труба с учетом размеров колодца //TODO: Как учесть, что блок прямоугольного колодца может не соответствовать колодцу по направлениям длины и ширины???? PointOnCurve2d startSplitPt = null; PointOnCurve2d endSplitPt = null; CivilDB.Structure strStart = null; CivilDB.Structure strEnd = null; if (!StartNode.StructId.IsNull) { strStart = (CivilDB.Structure)tr.GetObject(StartNode.StructId, OpenMode.ForRead); //уточнить положение начала кривой в начале с учетом размеров колодца! startSplitPt = GetSplitPt(strStart, true); } if (!EndNode.StructId.IsNull) { strEnd = (CivilDB.Structure)tr.GetObject(EndNode.StructId, OpenMode.ForRead); //уточнить положение конца кривой в начале с учетом размеров колодца! endSplitPt = GetSplitPt(strEnd, false); } if (startSplitPt != null && endSplitPt != null && startSplitPt.Parameter >= endSplitPt.Parameter) { //колодцы стоят вплотную друг к другу или залезают друг на друга. Места для трубы на остается return; } //проход по составляющим кривой с отбрасыванием частей, отсекаемых колодцами //Curve2d.GetSplitCurves работает неправильно //Curve2d.Explode тоже не помогает if (startSplitPt == null) { AddPtToPipePositionList(new XYZ(PositionCurve.StartPoint)); } Curve2d[] segments = PositionCurve.GetCurves(); double currParam = 0; foreach (Curve2d seg in segments) { Interval interval = seg.GetInterval(); double len = seg.GetLength(interval.LowerBound, interval.UpperBound); currParam += len; //Если есть точка разбиения в начале и она еще не достигнута if (startSplitPt != null) { if (startSplitPt.Parameter < currParam) { //точка разбиения находится на этой кривой. Ее нужно добавить в список AddPtToPipePositionList(new XYZ(startSplitPt.Point)); startSplitPt = null; } else { //точка отсечения начала еще не достигнута, переход к следующей кривой continue; } } if (endSplitPt != null && endSplitPt.Parameter < currParam) { //точка разбиения находится на этой кривой. Ее нужно добавить в список AddPtToPipePositionList(new XYZ(endSplitPt.Point)); endSplitPt = null; break;//обход точек заканчивается } AddPtToPipePositionList(new XYZ(seg.EndPoint)); } //Задание глубин заложения ребер по концам //- если не задана глубина заложения на одном из концов, сделать их равными //- если не задана глубина на обоих концах задать обоим концам глубину по умолчанию согласно вводу в окне CivilDB.TinSurface tinSurf = (CivilDB.TinSurface)tr.GetObject(tinSurfId, OpenMode.ForRead); double startElevByData = double.NegativeInfinity; double endElevByData = double.NegativeInfinity; if (StartPipeJunctionData != null && StartPipeJunctionData.JunctionLevel != double.NegativeInfinity) { startElevByData = StartPipeJunctionData.JunctionLevel; XYZ xyz = PipePositionList.First(); xyz.Z = startElevByData; } if (EndPipeJunctionData != null && EndPipeJunctionData.JunctionLevel != double.NegativeInfinity) { endElevByData = EndPipeJunctionData.JunctionLevel; XYZ xyz = PipePositionList.Last(); xyz.Z = endElevByData; } if (startElevByData != double.NegativeInfinity && endElevByData == double.NegativeInfinity) { XYZ xyz = PipePositionList.Last(); xyz.Z = startElevByData; } else if (startElevByData == double.NegativeInfinity && endElevByData != double.NegativeInfinity) { XYZ xyz = PipePositionList.First(); xyz.Z = endElevByData; } else if (startElevByData == double.NegativeInfinity && endElevByData == double.NegativeInfinity) { XYZ xyz1 = PipePositionList.First(); SetElevBySurf(defaultDepth, tinSurf, xyz1); XYZ xyz2 = PipePositionList.Last(); SetElevBySurf(defaultDepth, tinSurf, xyz2); } //- но не допускать, чтобы труба опускалась ниже дна колодца double sartElevByStr = double.NegativeInfinity; double endElevByStr = double.NegativeInfinity; if (strStart != null && strStart.SumpElevation > PipePositionList.First().Z) { sartElevByStr = strStart.SumpElevation; PipePositionList.First().Z = sartElevByStr; } if (strEnd != null && strEnd.SumpElevation > PipePositionList.Last().Z) { endElevByStr = strEnd.SumpElevation; PipePositionList.Last().Z = endElevByStr; } //после корректировки уточнить отметку соседней точки если по ней нет данных if (sartElevByStr != double.NegativeInfinity && endElevByData == double.NegativeInfinity) { XYZ xyz = PipePositionList.Last(); xyz.Z = sartElevByStr; } else if (startElevByData == double.NegativeInfinity && endElevByStr != double.NegativeInfinity) { XYZ xyz = PipePositionList.First(); xyz.Z = endElevByStr; } //Убедиться, что если в одном узле без колодца стыкуются несколько ребер, //то в месте стыковки обязательно у всех ребер должна быть одинаковая отметка double neighborJunctElev = GetNeigborJuncElev(StartNode); XYZ startPos = PipePositionList.First(); if (neighborJunctElev != double.NegativeInfinity && startPos.Z != neighborJunctElev) { startPos.Z = neighborJunctElev; } neighborJunctElev = GetNeigborJuncElev(EndNode); XYZ endPos = PipePositionList.Last(); if (neighborJunctElev != double.NegativeInfinity && endPos.Z != neighborJunctElev) { endPos.Z = neighborJunctElev; } //Задание отметок промежуточных точек на ребрах сети (интерполяция либо относительно поверхности) if (PipePositionList.Count > 2) { if (sameDepthEveryPt) { //одинаковая глубина относительно поверхности земли //метод TinSurface.SampleElevations работает не так как надо! Он не дает подробного учета рельефа! //точки полилинии for (int i = 1; i < PipePositionList.Count - 1; i++) { XYZ xyz = PipePositionList[i]; SetElevBySurf(defaultDepth, tinSurf, xyz); } //Помимо углов поворотов нужно добавить промежуточные точки через 1 м для учета рельефа! List <XYZ> positionListExtended = new List <XYZ>(); positionListExtended.Add(PipePositionList.First()); for (int i = 1; i < PipePositionList.Count; i++) { XYZ xyz0 = PipePositionList[i - 1]; XYZ xyz1 = PipePositionList[i]; double len = xyz1.Position2d.GetDistanceTo(xyz0.Position2d); Vector2d vector = (xyz1.Position2d - xyz0.Position2d).GetNormal(); double currLen = 1; while (currLen < len) { //добавление промежуточных точек Point2d pt = xyz0.Position2d + vector * currLen; XYZ intermediateXYZ = new XYZ(pt); SetElevBySurf(defaultDepth, tinSurf, intermediateXYZ); positionListExtended.Add(intermediateXYZ); currLen += 1; } positionListExtended.Add(xyz1); } PipePositionList = positionListExtended; } else { //интерполяция между началом и концом double startElev = startPos.Z; double endElev = endPos.Z; double elevDiff = endElev - startElev; double currLength = 0; for (int i = 1; i < PipePositionList.Count - 1; i++) { XYZ xyz = PipePositionList[i]; Point2d prevPt = PipePositionList[i - 1].Position2d; currLength += prevPt.GetDistanceTo(xyz.Position2d); xyz.Z = startElev + (elevDiff * currLength / pipeHorizontalLength); } } } }
private double CalculatePosition(double intervalPos) { return(ScaleCurveToValue(PositionCurve.GetValue(intervalPos), 0, 100)); }
private void StartBtnClick(object sender, RoutedEventArgs e) { InitChart(); if (HistoryPath.Text.IsEmpty() || !Directory.Exists(HistoryPath.Text)) { MessageBox.Show(this, LocalizedStrings.Str3014); return; } if (_connectors.Any(t => t.State != EmulationStates.Stopped)) { MessageBox.Show(this, LocalizedStrings.Str3015); return; } var secIdParts = SecId.Text.Split('@'); if (secIdParts.Length != 2) { MessageBox.Show(this, LocalizedStrings.Str3016); return; } var timeFrame = TimeSpan.FromMinutes(5); // создаем настройки для тестирования var settings = new[] { Tuple.Create( TicksCheckBox, TicksTestingProcess, TicksParameterGrid, // тест только на тиках new EmulationInfo { CurveColor = Colors.DarkGreen, StrategyName = LocalizedStrings.Str3017 }), Tuple.Create( TicksAndDepthsCheckBox, TicksAndDepthsTestingProcess, TicksAndDepthsParameterGrid, // тест на тиках + стаканы new EmulationInfo { UseMarketDepth = true, CurveColor = Colors.Red, StrategyName = LocalizedStrings.Str3018 }), Tuple.Create( CandlesCheckBox, CandlesTestingProcess, CandlesParameterGrid, // тест на свечах new EmulationInfo { UseCandleTimeFrame = timeFrame, CurveColor = Colors.DarkBlue, StrategyName = LocalizedStrings.Str3019 }), Tuple.Create( CandlesAndDepthsCheckBox, CandlesAndDepthsTestingProcess, CandlesAndDepthsParameterGrid, // тест на свечах + стаканы new EmulationInfo { UseMarketDepth = true, UseCandleTimeFrame = timeFrame, CurveColor = Colors.Cyan, StrategyName = LocalizedStrings.Str3020 }), Tuple.Create( OrderLogCheckBox, OrderLogTestingProcess, OrderLogParameterGrid, // тест на логе заявок new EmulationInfo { UseOrderLog = true, CurveColor = Colors.CornflowerBlue, StrategyName = LocalizedStrings.Str3021 }) }; // хранилище, через которое будет производиться доступ к тиковой и котировочной базе var storageRegistry = new StorageRegistry { // изменяем путь, используемый по умолчанию DefaultDrive = new LocalMarketDataDrive(HistoryPath.Text) }; var startTime = (DateTime)From.Value; var stopTime = (DateTime)To.Value; // ОЛ необходимо загружать с 18.45 пред дня, чтобы стаканы строились правильно if (OrderLogCheckBox.IsChecked == true) { startTime = startTime.Subtract(TimeSpan.FromDays(1)).AddHours(18).AddMinutes(45).AddTicks(1); } // задаем шаг ProgressBar var progressStep = ((stopTime - startTime).Ticks / 100).To <TimeSpan>(); // в реальности период может быть другим, и это зависит от объема данных, // хранящихся по пути HistoryPath, TicksTestingProcess.Maximum = TicksAndDepthsTestingProcess.Maximum = CandlesTestingProcess.Maximum = 100; TicksTestingProcess.Value = TicksAndDepthsTestingProcess.Value = CandlesTestingProcess.Value = 0; var logManager = new LogManager(); var fileLogListener = new FileLogListener("sample.log"); logManager.Listeners.Add(fileLogListener); //logManager.Listeners.Add(new DebugLogListener()); // чтобы смотреть логи в отладчике - работает медленно. var generateDepths = GenDepthsCheckBox.IsChecked == true; var maxDepth = MaxDepth.Text.To <int>(); var maxVolume = MaxVolume.Text.To <int>(); var secCode = secIdParts[0]; var board = ExchangeBoard.GetOrCreateBoard(secIdParts[1]); foreach (var set in settings) { if (set.Item1.IsChecked == false) { continue; } var progressBar = set.Item2; var statistic = set.Item3; var emulationInfo = set.Item4; // создаем тестовый инструмент, на котором будет производится тестирование var security = new Security { Id = SecId.Text, // по идентификатору инструмента будет искаться папка с историческими маркет данными Code = secCode, Board = board, }; var level1Info = new Level1ChangeMessage { SecurityId = security.ToSecurityId(), ServerTime = startTime, } .TryAdd(Level1Fields.PriceStep, 10m) .TryAdd(Level1Fields.StepPrice, 6m) .TryAdd(Level1Fields.MinPrice, 10m) .TryAdd(Level1Fields.MaxPrice, 1000000m) .TryAdd(Level1Fields.MarginBuy, 10000m) .TryAdd(Level1Fields.MarginSell, 10000m); // тестовый портфель var portfolio = new Portfolio { Name = "test account", BeginValue = 1000000, }; // создаем подключение для эмуляции // инициализируем настройки (инструмент в истории обновляется раз в секунду) var connector = new HistoryEmulationConnector( new[] { security }, new[] { portfolio }) { StorageRegistry = storageRegistry, MarketEmulator = { Settings = { // использовать свечи UseCandlesTimeFrame = emulationInfo.UseCandleTimeFrame, // сведение сделки в эмуляторе если цена коснулась нашей лимитной заявки. // Если выключено - требуется "прохождение цены сквозь уровень" // (более "суровый" режим тестирования.) MatchOnTouch = false, } }, //UseExternalCandleSource = true, CreateDepthFromOrdersLog = emulationInfo.UseOrderLog, CreateTradesFromOrdersLog = emulationInfo.UseOrderLog, }; connector.MarketDataAdapter.SessionHolder.MarketTimeChangedInterval = timeFrame; ((ILogSource)connector).LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info; logManager.Sources.Add(connector); connector.NewSecurities += securities => { //подписываемся на получение данных после получения инструмента if (securities.All(s => s != security)) { return; } // отправляем данные Level1 для инструмента connector.MarketDataAdapter.SendOutMessage(level1Info); // тест подразумевает наличие стаканов if (emulationInfo.UseMarketDepth) { connector.RegisterMarketDepth(security); if ( // если выбрана генерация стаканов вместо реальных стаканов generateDepths || // для свечей генерируем стаканы всегда emulationInfo.UseCandleTimeFrame != TimeSpan.Zero ) { // если история по стаканам отсутствует, но стаканы необходимы для стратегии, // то их можно сгенерировать на основании цен последних сделок или свечек. connector.RegisterMarketDepth(new TrendMarketDepthGenerator(connector.GetSecurityId(security)) { Interval = TimeSpan.FromSeconds(1), // стакан для инструмента в истории обновляется раз в секунду MaxAsksDepth = maxDepth, MaxBidsDepth = maxDepth, UseTradeVolume = true, MaxVolume = maxVolume, MinSpreadStepCount = 2, // минимальный генерируемый спред - 2 минимальных шага цены MaxSpreadStepCount = 5, // не генерировать спрэд между лучшим бид и аск больше чем 5 минимальных шагов цены - нужно чтобы при генерации из свечей не получалось слишком широкого спреда. MaxPriceStepCount = 3 // максимальное количество шагов между ценами, }); } } else if (emulationInfo.UseOrderLog) { connector.RegisterOrderLog(security); } }; // соединяемся с трейдером и запускаем экспорт, // чтобы инициализировать переданными инструментами и портфелями необходимые свойства EmulationTrader connector.Connect(); connector.StartExport(); var candleManager = 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); // создаем торговую стратегию, скользящие средние на 80 5-минуток и 10 5-минуток var strategy = new SmaStrategy(_bufferedChart, _candlesElem, _tradesElem, _shortMa, _shortElem, _longMa, _longElem, series) { Volume = 1, Portfolio = portfolio, Security = security, Connector = connector, LogLevel = DebugLogCheckBox.IsChecked == true ? LogLevels.Debug : LogLevels.Info, // по-умолчанию интервал равен 1 минут, // что для истории в диапазон от нескольких месяцев излишне UnrealizedPnLInterval = ((stopTime - startTime).Ticks / 1000).To <TimeSpan>() }; // комиссия в 1 копейку за сделку connector.MarketEmulator.SendInMessage(new CommissionRuleMessage { Rule = new CommissionPerTradeRule { Value = 0.01m } }); logManager.Sources.Add(strategy); // копируем параметры на визуальную панель statistic.Parameters.Clear(); statistic.Parameters.AddRange(strategy.StatisticManager.Parameters); var pnlCurve = Curve.CreateCurve("P&L " + 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; // и подписываемся на событие изменения времени, чтобы обновить 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(LocalizedStrings.Str3024 + (DateTime.Now - _startEmulationTime)); } else { MessageBox.Show(LocalizedStrings.cancelled); } }); } else if (connector.State == EmulationStates.Started) { SetIsEnabled(true); // запускаем стратегию когда эмулятор запустился strategy.Start(); candleManager.Start(series); } }; if (ShowDepth.IsChecked == true) { MarketDepth.UpdateFormat(security); connector.NewMessage += (message, dir) => { var quoteMsg = message as QuoteChangeMessage; if (quoteMsg != null) { MarketDepth.UpdateDepth(quoteMsg); } }; } _connectors.Add(connector); } _startEmulationTime = DateTime.Now; // запускаем эмуляцию foreach (var connector in _connectors) { // указываем даты начала и конца тестирования connector.Start(startTime, stopTime); } TabControl.Items.Cast <TabItem>().First(i => i.Visibility == Visibility.Visible).IsSelected = true; }