public override bool IsVisibleOnChart(ChartControl chartControl, ChartScale chartScale, DateTime firstTimeOnChart, DateTime lastTimeOnChart) { // First check any of the actual anchors are visible, we are immediately done foreach (ChartAnchor anchor in Anchors) { if (anchor.IsEditing || anchor.Time >= firstTimeOnChart && anchor.Time <= lastTimeOnChart) { return(true); } } // Calculate extensions and see if they extend into our visible times ChartPanel chartPanel = chartControl.ChartPanels[PanelIndex]; Point anchorStartPoint = StartAnchor.GetPoint(chartControl, chartPanel, chartScale); Point anchorEndPoint = EndAnchor.GetPoint(chartControl, chartPanel, chartScale); Point anchorExtensionPoint = ExtensionAnchor.GetPoint(chartControl, chartPanel, chartScale); Point midPointExtension = new Point((anchorExtensionPoint.X + anchorEndPoint.X) / 2, (anchorExtensionPoint.Y + anchorEndPoint.Y) / 2); if (CalculationMethod == AndrewsPitchforkCalculationMethod.Schiff) { anchorStartPoint = new Point(anchorStartPoint.X, (anchorStartPoint.Y + anchorEndPoint.Y) / 2); } else if (CalculationMethod == AndrewsPitchforkCalculationMethod.ModifiedSchiff) { anchorStartPoint = new Point((anchorEndPoint.X + anchorStartPoint.X) / 2, (anchorEndPoint.Y + anchorStartPoint.Y) / 2); } double totalPriceRange = EndAnchor.Price - ExtensionAnchor.Price; double startPrice = ExtensionAnchor.Price; foreach (PriceLevel priceLevel in PriceLevels.Where(pl => pl.IsVisible && pl.Stroke != null)) { double levelPrice = (startPrice + ((priceLevel.Value / 100) * totalPriceRange)); float pixelY = chartScale.GetYByValue(levelPrice); float pixelX = anchorExtensionPoint.X > anchorEndPoint.X ? (float)(anchorExtensionPoint.X - (Math.Abs((anchorEndPoint.X - anchorExtensionPoint.X) * (priceLevel.Value / 100)))) : (float)(anchorExtensionPoint.X + ((anchorEndPoint.X - anchorExtensionPoint.X) * (priceLevel.Value / 100))); Point startPoint = new Point(pixelX, pixelY); Point endPoint = new Point(startPoint.X + (midPointExtension.X - anchorStartPoint.X), startPoint.Y + (midPointExtension.Y - anchorStartPoint.Y)); Point maxLevelPoint = GetExtendedPoint(startPoint, endPoint); double padding = 5d; foreach (Point chkPoint in new[] { startPoint, maxLevelPoint, endPoint }) { if (chkPoint.X >= chartPanel.X - padding && chkPoint.X <= chartPanel.W + chartPanel.X + padding && chkPoint.Y >= chartPanel.Y - padding && chkPoint.Y <= chartPanel.Y + chartPanel.H + padding) { return(true); } } } return(false); }
private IEnumerable <Tuple <Point, Point> > GetAndrewsEndPoints(ChartControl chartControl, ChartScale chartScale) { ChartPanel panel = chartControl.ChartPanels[PanelIndex]; double totalPriceRange = EndAnchor.Price - ExtensionAnchor.Price; double startPrice = ExtensionAnchor.Price; Point anchorExtensionPoint = ExtensionAnchor.GetPoint(chartControl, panel, chartScale); Point anchorStartPoint = StartAnchor.GetPoint(chartControl, panel, chartScale); Point anchorEndPoint = EndAnchor.GetPoint(chartControl, panel, chartScale); Point midPointExtension = new Point((anchorExtensionPoint.X + anchorEndPoint.X) / 2, (anchorExtensionPoint.Y + anchorEndPoint.Y) / 2); foreach (PriceLevel pl in PriceLevels.Where(pl => pl.IsVisible)) { double levelPrice = (startPrice + ((pl.Value / 100) * totalPriceRange)); float pixelY = chartScale.GetYByValue(levelPrice); float pixelX = anchorExtensionPoint.X > anchorEndPoint.X ? (float)(anchorExtensionPoint.X - (Math.Abs((anchorEndPoint.X - anchorExtensionPoint.X) * (pl.Value / 100)))) : (float)(anchorExtensionPoint.X + ((anchorEndPoint.X - anchorExtensionPoint.X) * (pl.Value / 100))); Point startPoint = new Point(pixelX, pixelY); Point endPoint = new Point(startPoint.X + (midPointExtension.X - anchorStartPoint.X), startPoint.Y + (midPointExtension.Y - anchorStartPoint.Y)); Point maxLevelPoint = GetExtendedPoint(startPoint, endPoint); yield return(new Tuple <Point, Point>(new Point(Math.Max(maxLevelPoint.X, 1), Math.Max(maxLevelPoint.Y, 1)), startPoint)); } }
/// <summary> /// To calculate the value area. /// </summary> public void Calculate() { // Основная суть: // Есть POC Vol от него выше и ниже берется по два значения(объемы) // Суммируются и сравниваются, те что в сумме больше, складываются в общий объем, в котором изначально лежит POC Vol. // На следующей итерации берутся следующие два объема суммируются и сравниваются, и опять большая сумма ложится в общий объем // И так до тех пор пока общий объем не превысит порог, который устанавливается в процентном отношении к всему объему. // После превышения порога, самый верхний и самый нижний объем, из которых складывался общий объем будут VAH и VAL. // Возможные траблы: // Если POC Vol находится на границе ценового диапазона, то сверху/снизу брать нечего, то "набор" объемов только в одну сторону. // Если POC Vol находится на один шаг ниже/выше ценового диапазона, то сверху/снизу можно взять только одно значение для сравнения с двумя другими значениями. // Теоретически в ценовом диапазоне может быть несколько POC Vol, если будет несколько ценовых уровней с одинаковыми объемом, // в таком случае должен браться POC Vol который ближе к центру. Теоретически они могут быть равно удалены от центра.))) // Если сумма сравниваемых объемов равна, х.з. какие брать. var maxVolume = Math.Round(PriceLevels.Sum(p => p.BuyVolume + p.SellVolume) * VolumePercent / 100, 0); var currVolume = PriceLevels.Select(p => (p.BuyVolume + p.SellVolume)).Max(); POC = PriceLevels.FirstOrDefault(p => p.BuyVolume + p.SellVolume == currVolume); var abovePoc = Combine(PriceLevels.Where(p => p.Price > POC.Price).OrderBy(p => p.Price)); var belowePoc = Combine(PriceLevels.Where(p => p.Price < POC.Price).OrderByDescending(p => p.Price)); if (abovePoc.Count == 0) { LinkedListNode <PriceLevel> node; for (node = belowePoc.First; node != null; node = node.Next) { var vol = node.Value.BuyVolume + node.Value.SellVolume; if (currVolume + vol > maxVolume) { VAH = POC; VAL = node.Value; } else { currVolume += vol; } } } else if (belowePoc.Count == 0) { LinkedListNode <PriceLevel> node; for (node = abovePoc.First; node != null; node = node.Next) { var vol = node.Value.BuyVolume + node.Value.SellVolume; if (currVolume + vol > maxVolume) { VAH = node.Value; VAL = POC; } else { currVolume += vol; } } } else { var abovePocNode = abovePoc.First; var belowPocNode = belowePoc.First; while (true) { var aboveVol = abovePocNode.Value.BuyVolume + abovePocNode.Value.SellVolume; var belowVol = belowPocNode.Value.BuyVolume + belowPocNode.Value.SellVolume; if (aboveVol > belowVol) { if (currVolume + aboveVol > maxVolume) { VAH = abovePocNode.Value; VAL = belowPocNode.Value; break; } currVolume += aboveVol; abovePocNode = abovePocNode.Next; } else { if (currVolume + belowVol > maxVolume) { VAH = abovePocNode.Value; VAL = belowPocNode.Value; break; } currVolume += belowVol; belowPocNode = belowPocNode.Next; } } } }
public override void OnRender(ChartControl chartControl, ChartScale chartScale) { if (Anchors.All(a => a.IsEditing)) { return; } RenderTarget.AntialiasMode = SharpDX.Direct2D1.AntialiasMode.PerPrimitive; ChartPanel chartPanel = chartControl.ChartPanels[PanelIndex]; Point anchorStartPoint = StartAnchor.GetPoint(chartControl, chartPanel, chartScale); Point anchorEndPoint = EndAnchor.GetPoint(chartControl, chartPanel, chartScale); Point anchorExtensionPoint = ExtensionAnchor.GetPoint(chartControl, chartPanel, chartScale); Point midPointExtension = new Point((anchorExtensionPoint.X + anchorEndPoint.X) / 2, (anchorExtensionPoint.Y + anchorEndPoint.Y) / 2); if (CalculationMethod == AndrewsPitchforkCalculationMethod.Schiff) { anchorStartPoint = new Point(anchorStartPoint.X, (anchorStartPoint.Y + anchorEndPoint.Y) / 2); } else if (CalculationMethod == AndrewsPitchforkCalculationMethod.ModifiedSchiff) { anchorStartPoint = new Point((anchorEndPoint.X + anchorStartPoint.X) / 2, (anchorEndPoint.Y + anchorStartPoint.Y) / 2); } AnchorLineStroke.RenderTarget = RenderTarget; RetracementLineStroke.RenderTarget = RenderTarget; // Align to full pixel to avoid unneeded aliasing double strokePixAdj = AnchorLineStroke.Width % 2 == 0 ? 0.5d : 0d; Vector pixelAdjustVec = new Vector(strokePixAdj, strokePixAdj); SharpDX.Vector2 startVec = (anchorStartPoint + pixelAdjustVec).ToVector2(); SharpDX.Vector2 endVec = (anchorEndPoint + pixelAdjustVec).ToVector2(); SharpDX.Direct2D1.Brush tmpBrush = IsInHitTest ? chartControl.SelectionBrush : AnchorLineStroke.BrushDX; SharpDX.Vector2 startOriginVec = (StartAnchor.GetPoint(chartControl, chartPanel, chartScale) + pixelAdjustVec).ToVector2(); RenderTarget.DrawLine(startOriginVec, endVec, tmpBrush, AnchorLineStroke.Width, AnchorLineStroke.StrokeStyle); // Is second anchor set yet? Check both so we correctly re-draw during extension anchor editing if (ExtensionAnchor.IsEditing && EndAnchor.IsEditing) { return; } SharpDX.Vector2 extVector = anchorExtensionPoint.ToVector2(); tmpBrush = IsInHitTest ? chartControl.SelectionBrush : RetracementLineStroke.BrushDX; RenderTarget.DrawLine(endVec, extVector, tmpBrush, RetracementLineStroke.Width, RetracementLineStroke.StrokeStyle); // If we're doing a hit test pass, don't draw price levels at all, we dont want those to count for // hit testing if (IsInHitTest || PriceLevels == null || !PriceLevels.Any()) { return; } SetAllPriceLevelsRenderTarget(); // Calculate total y range for % calculation on each level double totalPriceRange = EndAnchor.Price - ExtensionAnchor.Price; double startPrice = ExtensionAnchor.Price; float minLevelY = float.MaxValue; float maxLevelY = float.MinValue; // Store values to use in correct render order Point lastEndPoint = new Point(0, 0); Point lastStartPoint = new Point(0, 0); Stroke lastStroke = null; List <Tuple <PriceLevel, Point> > textPoints = new List <Tuple <PriceLevel, Point> >(); foreach (PriceLevel priceLevel in PriceLevels.Where(pl => pl.IsVisible && pl.Stroke != null).OrderBy(pl => pl.Value)) { double levelPrice = (startPrice + ((priceLevel.Value / 100) * totalPriceRange)); float pixelY = chartScale.GetYByValue(levelPrice); float pixelX = anchorExtensionPoint.X > anchorEndPoint.X ? priceLevel.Value >= 0 ? (float)(anchorExtensionPoint.X - (Math.Abs((anchorEndPoint.X - anchorExtensionPoint.X) * (priceLevel.Value / 100)))) : (float)(anchorExtensionPoint.X + ((anchorEndPoint.X - anchorExtensionPoint.X) * (priceLevel.Value / 100))): priceLevel.Value >= 0 ? (float)(anchorExtensionPoint.X + ((anchorEndPoint.X - anchorExtensionPoint.X) * (priceLevel.Value / 100))) : (float)(anchorExtensionPoint.X - (Math.Abs((anchorEndPoint.X - anchorExtensionPoint.X) * (priceLevel.Value / 100)))); Point startPoint = new Point(pixelX, pixelY); Point endPoint = new Point(startPoint.X + (midPointExtension.X - anchorStartPoint.X), startPoint.Y + (midPointExtension.Y - anchorStartPoint.Y)); Point maxLevelPoint = GetExtendedPoint(startPoint, endPoint); if (priceLevel.Value == 50) { RenderTarget.DrawLine(startVec, maxLevelPoint.ToVector2(), priceLevel.Stroke.BrushDX, priceLevel.Stroke.Width, priceLevel.Stroke.StrokeStyle); } else { RenderTarget.DrawLine(startPoint.ToVector2(), maxLevelPoint.ToVector2(), priceLevel.Stroke.BrushDX, priceLevel.Stroke.Width, priceLevel.Stroke.StrokeStyle); } if (lastStroke == null) { lastStroke = new Stroke(); } else { SharpDX.Direct2D1.PathGeometry lineGeometry = new SharpDX.Direct2D1.PathGeometry(Core.Globals.D2DFactory); SharpDX.Direct2D1.GeometrySink sink = lineGeometry.Open(); sink.BeginFigure(lastEndPoint.ToVector2(), SharpDX.Direct2D1.FigureBegin.Filled); // Does the fill color need to fill a corner? Check and add a point if (lastEndPoint.Y != maxLevelPoint.Y && lastEndPoint.X != maxLevelPoint.X) { double boundaryX; double boundaryY; if (lastEndPoint.Y <= ChartPanel.Y || lastEndPoint.Y >= ChartPanel.Y + ChartPanel.H) { boundaryY = lastEndPoint.Y; boundaryX = maxLevelPoint.X; } else { boundaryY = maxLevelPoint.Y; boundaryX = lastEndPoint.X; } sink.AddLine(new SharpDX.Vector2((float)boundaryX, (float)boundaryY)); } sink.AddLine(maxLevelPoint.ToVector2()); sink.AddLine(startPoint.ToVector2()); sink.AddLine(lastStartPoint.ToVector2()); sink.EndFigure(SharpDX.Direct2D1.FigureEnd.Closed); sink.Close(); RenderTarget.FillGeometry(lineGeometry, lastStroke.BrushDX); lineGeometry.Dispose(); } if (IsTextDisplayed) { textPoints.Add(new Tuple <PriceLevel, Point>(priceLevel, maxLevelPoint)); } priceLevel.Stroke.CopyTo(lastStroke); lastStroke.Opacity = PriceLevelOpacity; lastStartPoint = startPoint; lastEndPoint = maxLevelPoint; minLevelY = Math.Min(pixelY, minLevelY); maxLevelY = Math.Max(pixelY, maxLevelY); } // Render text last so it's on top of the price level colors if (IsTextDisplayed) { foreach (Tuple <PriceLevel, Point> textPoint in textPoints) { DrawPriceLevelText(0, 0, textPoint.Item2, textPoint.Item1, chartPanel); } } }