// // Draw finance chart track line with legend // private void trackFinance(MultiChart m, int mouseX) { // Clear the current dynamic layer and get the DrawArea object to draw on it. DrawArea d = m.initDynamicLayer(); // It is possible for a FinanceChart to be empty, so we need to check for it. if (m.getChartCount() == 0) { return; } // Get the data x-value that is nearest to the mouse int xValue = (int)(((XYChart)m.getChart(0)).getNearestXValue(mouseX)); // Iterate the XY charts (main price chart and indicator charts) in the FinanceChart XYChart c = null; for (int i = 0; i < m.getChartCount(); ++i) { c = (XYChart)m.getChart(i); // Variables to hold the legend entries string ohlcLegend = ""; ArrayList legendEntries = new ArrayList(); // Iterate through all layers to find the highest data point for (int j = 0; j < c.getLayerCount(); ++j) { Layer layer = c.getLayerByZ(j); int xIndex = layer.getXIndexOf(xValue); int dataSetCount = layer.getDataSetCount(); // In a FinanceChart, only layers showing OHLC data can have 4 data sets if (dataSetCount == 4) { double highValue = layer.getDataSet(0).getValue(xIndex); double lowValue = layer.getDataSet(1).getValue(xIndex); double openValue = layer.getDataSet(2).getValue(xIndex); double closeValue = layer.getDataSet(3).getValue(xIndex); if (closeValue != Chart.NoValue) { // Build the OHLC legend ohlcLegend = "Open: " + c.formatValue(openValue, "{value|P4}") + ", High: " + c.formatValue(highValue, "{value|P4}") + ", Low: " + c.formatValue(lowValue, "{value|P4}") + ", Close: " + c.formatValue(closeValue, "{value|P4}"); // We also draw an upward or downward triangle for up and down days and the % // change double lastCloseValue = layer.getDataSet(3).getValue(xIndex - 1); if (lastCloseValue != Chart.NoValue) { double change = closeValue - lastCloseValue; double percent = change * 100 / closeValue; string symbol = ((change >= 0) ? "<*font,color=008800*><*img=@triangle,width=8,color=008800*>" : "<*font,color=CC0000*><*img=@invertedtriangle,width=8,color=CC0000*>"); ohlcLegend = ohlcLegend + " " + symbol + " " + c.formatValue(change, "{value|P4}") + " (" + c.formatValue(percent, "{value|2}") + "%)<*/font*>" ; } // Use a <*block*> to make sure the line does not wrap within the legend entry ohlcLegend = "<*block*>" + ohlcLegend + " <*/*>"; } } else { // Iterate through all the data sets in the layer for (int k = 0; k < layer.getDataSetCount(); ++k) { ChartDirector.DataSet dataSet = layer.getDataSetByZ(k); string name = dataSet.getDataName(); double value = dataSet.getValue(xIndex); if ((!string.IsNullOrEmpty(name)) && (value != Chart.NoValue)) { // In a FinanceChart, the data set name consists of the indicator name and its // latest value. It is like "Vol: 123M" or "RSI (14): 55.34". As we are // generating the values dynamically, we need to extract the indictor name // out, and also the volume unit (if any). // The unit character, if any, is the last character and must not be a digit. string unitChar = name.Substring(name.Length - 1); if (unitChar.CompareTo("0") >= 0 && unitChar.CompareTo("9") <= 0) { unitChar = ""; } // The indicator name is the part of the name up to the colon character. int delimiterPosition = name.IndexOf(":"); if (delimiterPosition != -1) { name = name.Substring(0, delimiterPosition); } // In a FinanceChart, if there are two data sets, it must be representing a // range. if (dataSetCount == 2) { // We show both values in the range in a single legend entry value = layer.getDataSet(0).getValue(xIndex); double value2 = layer.getDataSet(1).getValue(xIndex); name = name + ": " + c.formatValue(Math.Min(value, value2), "{value|P3}") + " - " + c.formatValue(Math.Max(value, value2), "{value|P3}"); } else { // In a FinanceChart, only the layer for volume bars has 3 data sets for // up/down/flat days if (dataSetCount == 3) { // The actual volume is the sum of the 3 data sets. value = layer.getDataSet(0).getValue(xIndex) + layer.getDataSet(1 ).getValue(xIndex) + layer.getDataSet(2).getValue(xIndex); } // Create the legend entry name = name + ": " + c.formatValue(value, "{value|P3}") + unitChar; } // Build the legend entry, consist of a colored square box and the name (with // the data value in it). legendEntries.Add("<*block*><*img=@square,width=8,edgeColor=000000,color=" + dataSet.getDataColor().ToString("x") + "*> " + name + "<*/*>"); } } } } // Get the plot area position relative to the entire FinanceChart PlotArea plotArea = c.getPlotArea(); int plotAreaLeftX = plotArea.getLeftX() + c.getAbsOffsetX(); int plotAreaTopY = plotArea.getTopY() + c.getAbsOffsetY(); // The legend is formed by concatenating the legend entries. legendEntries.Reverse(); string legendText = String.Join(" ", (string[])legendEntries.ToArray(typeof(string))); // Add the date and the ohlcLegend (if any) at the beginning of the legend legendText = "<*block,valign=top,maxWidth=" + (plotArea.getWidth() - 5) + "*><*font=Arial Bold*>[" + c.xAxis().getFormattedLabel(xValue, "mmm dd, yyyy") + "]<*/font*> " + ohlcLegend + legendText; // Draw a vertical track line at the x-position d.vline(plotAreaTopY, plotAreaTopY + plotArea.getHeight(), c.getXCoor(xValue) + c.getAbsOffsetX(), d.dashLineColor(0x000000, 0x0101)); // Display the legend on the top of the plot area TTFText t = d.text(legendText, "Arial", 8); t.draw(plotAreaLeftX + 5, plotAreaTopY + 3, 0x000000, Chart.TopLeft); } }