protected override void OnMarketData(MarketDataEventArgs e) { if (e.MarketDataType == MarketDataType.Ask) { askPrice = e.Price; return; } if (e.MarketDataType == MarketDataType.Bid) { bidPrice = e.Price; return; } if (e.MarketDataType != MarketDataType.Last || ChartControl == null || askPrice == 0 || bidPrice == 0) { return; } if (Bars != null && !Bars.Session.InSession(DateTime.Now, Bars.Period, true, Bars.BarsType)) { return; } double price = e.Price; long volume = e.Volume; if (!volumeInfo.ContainsKey(price)) { volumeInfo.Add(price, new VolumeInfoItem()); } VolumeInfoItem volumeInfoItem = volumeInfo[price]; if (price >= askPrice) { volumeInfoItem.up += volume; } else if (price <= bidPrice) { volumeInfoItem.down += volume; } else { volumeInfoItem.neutral += volume; } }
protected override void OnRender(ChartControl chartControl, ChartScale chartScale) { if (Bars == null || Bars.Instrument == null || IsInHitTest) { return; } int firstBarIdxToPaint = -1; double tickSize = Bars.Instrument.MasterInstrument.TickSize; double volumeMax = 0; SharpDX.Direct2D1.Brush upBrush = VolumeUpBrush.ToDxBrush(RenderTarget); SharpDX.Direct2D1.Brush downBrush = VolumeDownBrush.ToDxBrush(RenderTarget); SharpDX.Direct2D1.Brush neutralBrush = VolumeNeutralBrush.ToDxBrush(RenderTarget); SharpDX.Direct2D1.Brush lineBrushDx = LineBrush.ToDxBrush(RenderTarget); upBrush.Opacity = (float)(alpha / 100.0); downBrush.Opacity = (float)(alpha / 100.0); neutralBrush.Opacity = (float)(alpha / 100.0); for (int i = newSessionBarIdx.Count - 1; i >= 0; i--) { int prevSessionBreakIdx = newSessionBarIdx[i]; if (prevSessionBreakIdx <= ChartBars.ToIndex) { startIndexOf = newSessionBarIdx.IndexOf(prevSessionBreakIdx); firstBarIdxToPaint = prevSessionBreakIdx; break; } } if (sortedDicList.Count < 1 && cacheDictionary.Keys.Count > 0) { sortedDicList.Add(cacheDictionary); } foreach (Dictionary <double, VolumeInfoItem> tmpDict in sortedDicList) { foreach (KeyValuePair <double, VolumeInfoItem> keyValue in tmpDict) { double price = keyValue.Key; if (Bars.BarsType.IsIntraday && (price > chartScale.MaxValue || price < chartScale.MinValue)) { continue; } VolumeInfoItem vii = keyValue.Value; double total = vii.up + vii.down + vii.neutral; volumeMax = Math.Max(volumeMax, total); } } if (volumeMax.ApproxCompare(0) == 0) { return; } int viiPositions = 0; foreach (KeyValuePair <double, VolumeInfoItem> keyValue in sortedDicList[startIndexOf]) { viiPositions++; VolumeInfoItem vii = keyValue.Value; double priceLower = keyValue.Key - tickSize / 2; float yLower = chartScale.GetYByValue(priceLower); float yUpper = chartScale.GetYByValue(priceLower + tickSize); float height = Math.Max(1, Math.Abs(yUpper - yLower) - barSpacing); int barWidthUp = (int)((ChartPanel.W / 2) * (vii.up / volumeMax)); int barWidthNeutral = (int)((ChartPanel.W / 2) * (vii.neutral / volumeMax)); int barWidthDown = (int)((ChartPanel.W / 2) * (vii.down / volumeMax)); float stationaryXpos = chartControl.GetXByBarIndex(ChartBars, !Bars.IsTickReplay ? ChartBars.FromIndex : Math.Max(ChartBars.FromIndex, firstBarIdxToPaint)); float xpos = chartControl.GetXByBarIndex(ChartBars, !Bars.IsTickReplay ? ChartBars.FromIndex : Math.Max(1, Math.Max(ChartBars.FromIndex, firstBarIdxToPaint)) - 1); RenderTarget.FillRectangle(new SharpDX.RectangleF(xpos, yUpper, barWidthUp, height), upBrush); xpos += barWidthUp; RenderTarget.FillRectangle(new SharpDX.RectangleF(xpos, yUpper, barWidthNeutral, height), neutralBrush); xpos += barWidthNeutral; RenderTarget.FillRectangle(new SharpDX.RectangleF(xpos, yUpper, barWidthDown, height), downBrush); if (!drawLines) { continue; } // Lower line RenderTarget.DrawLine(new SharpDX.Vector2(stationaryXpos, yLower), new SharpDX.Vector2((ChartPanel.X + ChartPanel.W), yLower), lineBrushDx); // Upper line (only at very top) if (viiPositions == sortedDicList[startIndexOf].Count) { RenderTarget.DrawLine(new SharpDX.Vector2(stationaryXpos, yUpper), new SharpDX.Vector2((ChartPanel.X + ChartPanel.W), yUpper), lineBrushDx); } } lineBrushDx.Dispose(); upBrush.Dispose(); downBrush.Dispose(); neutralBrush.Dispose(); }
/// <summary> /// Called when the indicator is plotted. /// </summary> public override void Plot(Graphics graphics, Rectangle bounds, double min, double max) { // Paranoia if (Bars == null || Bars.Instrument == null) { return; } double tickSize = Bars.Instrument.MasterInstrument.TickSize; // Check if we should 'gray out' the bars bool isInactive = (ChartControl.EquidistantBars && ChartControl.LastBarPainted < BarsArray[0].Count - 1 || !ChartControl.EquidistantBars && ChartControl.LastBarTimePainted < Time[0]); double volumeMax = 0; // Figure out the max volume foreach (KeyValuePair <double, VolumeInfoItem> keyValue in volumeInfo) { double price = keyValue.Key; // Don't watch volume for prices outside the visible chart if (price > max || price < min) { continue; } VolumeInfoItem vii = keyValue.Value; double total = vii.up + vii.down + vii.neutral; volumeMax = Math.Max(volumeMax, total); } if (volumeMax == 0) { return; } SolidBrush upBrush = barBrushUp; SolidBrush downBrush = barBrushDown; SolidBrush neutralBrush = barBrushNeutral; if (isInactive) { if (barBrushInactive == null) { barBrushInactive = new SolidBrush(Color.FromArgb(Math.Min(255, alpha + 50), ChartControl.PriceMarkerInactive.R, ChartControl.PriceMarkerInactive.G, ChartControl.PriceMarkerInactive.B)); } upBrush = downBrush = neutralBrush = barBrushInactive; } int viiPosition = 0; // Plot 'em foreach (KeyValuePair <double, VolumeInfoItem> keyValue in volumeInfo) { viiPosition++; VolumeInfoItem vii = keyValue.Value; double priceLower = keyValue.Key - tickSize / 2; int yLower = ChartControl.GetYByValue(this, priceLower); int yUpper = ChartControl.GetYByValue(this, priceLower + tickSize); int height = Math.Max(1, Math.Abs(yUpper - yLower) - barSpacing); int barWidthUp = (int)((bounds.Width / 2) * (vii.up / volumeMax)); int barWidthNeutral = (int)((bounds.Width / 2) * (vii.neutral / volumeMax)); int barWidthDown = (int)((bounds.Width / 2) * (vii.down / volumeMax)); int xpos = bounds.X; graphics.FillRectangle(upBrush, new Rectangle(xpos, yUpper, barWidthUp, height)); xpos += barWidthUp; graphics.FillRectangle(neutralBrush, new Rectangle(xpos, yUpper, barWidthNeutral, height)); xpos += barWidthNeutral; graphics.FillRectangle(downBrush, new Rectangle(xpos, yUpper, barWidthDown, height)); if (!drawLines) { continue; } // Lower line graphics.DrawLine(linePen, bounds.X, yLower - 1, bounds.X + bounds.Width, yLower - 1); // Upper line (only at the very top) if (viiPosition == volumeInfo.Count) { graphics.DrawLine(linePen, bounds.X, yUpper - 1, bounds.X + bounds.Width, yUpper - 1); } } }