//Main code for creating chart. //Note: the argument chartIndex is unused because this demo only has 1 chart. public void createChart(WinChartViewer viewer, int chartIndex) { // Create a finance chart demo containing 100 days of data int noOfDays = 100; // To compute moving averages starting from the first day, we need to get extra data // points before the first day int extraDays = 30; // In this exammple, we use a random number generator utility to simulate the data. We // set up the random table to create 6 cols x (noOfDays + extraDays) rows, using 9 as // the seed. RanTable rantable = new RanTable(9, 6, noOfDays + extraDays); // Set the 1st col to be the timeStamp, starting from Sep 4, 2002, with each row // representing one day, and counting week days only (jump over Sat and Sun) rantable.setDateCol(0, new DateTime(2002, 9, 4), 86400, true); // Set the 2nd, 3rd, 4th and 5th columns to be high, low, open and close data. The open // value starts from 100, and the daily change is random from -5 to 5. rantable.setHLOCCols(1, 100, -5, 5); // Set the 6th column as the vol data from 5 to 25 million rantable.setCol(5, 50000000, 250000000); // Now we read the data from the table into arrays double[] timeStamps = rantable.getCol(0); double[] highData = rantable.getCol(1); double[] lowData = rantable.getCol(2); double[] openData = rantable.getCol(3); double[] closeData = rantable.getCol(4); double[] volData = rantable.getCol(5); // Create a FinanceChart object of width 640 pixels FinanceChart c = new FinanceChart(640); // Add a title to the chart c.addTitle("Finance Chart Demonstration"); // Set the data into the finance chart object c.setData(timeStamps, highData, lowData, openData, closeData, volData, extraDays); // Add the main chart with 240 pixels in height c.addMainChart(240); // Add a 5 period simple moving average to the main chart, using brown color c.addSimpleMovingAvg(5, 0x663300); // Add a 20 period simple moving average to the main chart, using purple color c.addSimpleMovingAvg(20, 0x9900ff); // Add HLOC symbols to the main chart, using green/red for up/down days c.addHLOC(0x008000, 0xcc0000); // Add 20 days bollinger band to the main chart, using light blue (9999ff) as the border // and semi-transparent blue (c06666ff) as the fill color c.addBollingerBand(20, 2, 0x9999ff, unchecked ((int)0xc06666ff)); // Add a 75 pixels volume bars sub-chart to the bottom of the main chart, using // green/red/grey for up/down/flat days c.addVolBars(75, 0x99ff99, 0xff9999, 0x808080); // Append a 14-days RSI indicator chart (75 pixels high) after the main chart. The main // RSI line is purple (800080). Set threshold region to +/- 20 (that is, RSI = 50 +/- // 25). The upper/lower threshold regions will be filled with red (ff0000)/blue // (0000ff). c.addRSI(75, 14, 0x800080, 20, 0xff0000, 0x0000ff); // Append a 12-days momentum indicator chart (75 pixels high) using blue (0000ff) color. c.addMomentum(75, 12, 0x0000ff); // Output the chart viewer.Chart = c; }
private void FrmTrackFinance_Load(object sender, EventArgs e) { // Create a finance chart demo containing 100 days of data int noOfDays = 100; // To compute moving averages starting from the first day, we need to get extra data points before // the first day int extraDays = 30; // In this exammple, we use a random number generator utility to simulate the data. We set up the // random table to create 6 cols x (noOfDays + extraDays) rows, using 9 as the seed. RanTable rantable = new RanTable(9, 6, noOfDays + extraDays); // Set the 1st col to be the timeStamp, starting from Sep 4, 2011, with each row representing one // day, and counting week days only (jump over Sat and Sun) rantable.setDateCol(0, new DateTime(2011, 9, 4), 86400, true); // Set the 2nd, 3rd, 4th and 5th columns to be high, low, open and close data. The open value // starts from 100, and the daily change is random from -5 to 5. rantable.setHLOCCols(1, 100, -5, 5); // Set the 6th column as the vol data from 5 to 25 million rantable.setCol(5, 50000000, 250000000); // Now we read the data from the table into arrays double[] timeStamps = rantable.getCol(0); double[] highData = rantable.getCol(1); double[] lowData = rantable.getCol(2); double[] openData = rantable.getCol(3); double[] closeData = rantable.getCol(4); double[] volData = rantable.getCol(5); // Create a FinanceChart object of width 720 pixels FinanceChart c = new FinanceChart(720); // Add a title to the chart c.addTitle("Finance Chart Demonstration"); // Disable default legend box, as we are using dynamic legend c.setLegendStyle("normal", 8, Chart.Transparent, Chart.Transparent); // Set the data into the finance chart object c.setData(timeStamps, highData, lowData, openData, closeData, volData, extraDays); // Add the main chart with 240 pixels in height c.addMainChart(240); // Add a 10 period simple moving average to the main chart, using brown color c.addSimpleMovingAvg(10, 0x663300); // Add a 20 period simple moving average to the main chart, using purple color c.addSimpleMovingAvg(20, 0x9900ff); // Add candlestick symbols to the main chart, using green/red for up/down days c.addCandleStick(0x00ff00, 0xff0000); // Add 20 days bollinger band to the main chart, using light blue (9999ff) as the border and // semi-transparent blue (c06666ff) as the fill color c.addBollingerBand(20, 2, 0x9999ff, unchecked ((int)0xc06666ff)); // Add a 75 pixels volume bars sub-chart to the bottom of the main chart, using green/red/grey for // up/down/flat days c.addVolBars(75, 0x99ff99, 0xff9999, 0x808080); // Append a 14-days RSI indicator chart (75 pixels high) after the main chart. The main RSI line // is purple (800080). Set threshold region to +/- 20 (that is, RSI = 50 +/- 25). The upper/lower // threshold regions will be filled with red (ff0000)/blue (0000ff). c.addRSI(75, 14, 0x800080, 20, 0xff0000, 0x0000ff); // Append a MACD(26, 12) indicator chart (75 pixels high) after the main chart, using 9 days for // computing divergence. c.addMACD(75, 26, 12, 9, 0x0000ff, 0xff00ff, 0x008000); // Include track line with legend for the latest data values trackFinance(c, ((XYChart)c.getChart(0)).getPlotArea().getRightX()); // Assign the chart to the WinChartViewer winChartViewer1.Chart = c; }
/// <summary> /// Draw the chart according to user selection and display it in the WPFChartViewer. /// </summary> /// <param name="viewer">The WPFChartViewer object to display the chart.</param> private void drawChart(WPFChartViewer viewer) { // Use InvariantCulture to draw the chart. This ensures the chart will look the // same on any computer. System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture; // In this demo, we just assume we plot up to the latest time. So endDate is now. DateTime endDate = DateTime.Now; // If the trading day has not yet started (before 9:30am), or if the end date is // on on Sat or Sun, we set the end date to 4:00pm of the last trading day while ((endDate.TimeOfDay.CompareTo(new TimeSpan(9, 30, 0)) < 0) || ( endDate.DayOfWeek == DayOfWeek.Sunday) || (endDate.DayOfWeek == DayOfWeek.Saturday)) { endDate = endDate.Date.AddDays(-1).Add(new TimeSpan(16, 0, 0)); } // The duration selected by the user int durationInDays = int.Parse(((ListItem)timeRange.SelectedItem).Key); // Compute the start date by subtracting the duration from the end date. DateTime startDate; if (durationInDays >= 30) { // More or equal to 30 days - so we use months as the unit startDate = new DateTime(endDate.Year, endDate.Month, 1).AddMonths( -durationInDays / 30); } else { // Less than 30 days - use day as the unit. Note that we use trading days // below. For less than 30 days, the starting point of the axis is always at // the start of the day. startDate = endDate.Date; for (int i = 1; i < durationInDays; ++i) { startDate = startDate.AddDays( (startDate.DayOfWeek == DayOfWeek.Monday) ? -3 : -1); } } // The first moving average period selected by the user. int avgPeriod1; try { avgPeriod1 = int.Parse(movAvg1.Text); } catch { avgPeriod1 = 0; } avgPeriod1 = Math.Max(0, Math.Min(300, avgPeriod1)); // The second moving average period selected by the user. int avgPeriod2; try { avgPeriod2 = int.Parse(movAvg2.Text); } catch { avgPeriod2 = 0; } avgPeriod2 = Math.Max(0, Math.Min(300, avgPeriod2)); // We need extra leading data points in order to compute moving averages. int extraPoints = Math.Max(20, Math.Max(avgPeriod1, avgPeriod2)); // Get the data series to compare with, if any. compareKey = compareWith.Text.Trim(); compareData = null; if (getData(compareKey, startDate, endDate, durationInDays, extraPoints)) { compareData = closeData; } // The data series we want to get. tickerKey = tickerSymbol.Text.Trim(); if (!getData(tickerKey, startDate, endDate, durationInDays, extraPoints)) { errMsg(viewer, "Please enter a valid ticker symbol"); return; } // We now confirm the actual number of extra points (data points that are before // the start date) as inferred using actual data from the database. extraPoints = timeStamps.Length; for (int i = 0; i < timeStamps.Length; ++i) { if (timeStamps[i] >= startDate) { extraPoints = i; break; } } // Check if there is any valid data if (extraPoints >= timeStamps.Length) { // No data - just display the no data message. errMsg(viewer, "No data available for the specified time period"); return; } // In some finance chart presentation style, even if the data for the latest day // is not fully available, the axis for the entire day will still be drawn, where // no data will appear near the end of the axis. if (resolution < 86400) { // Add extra points to the axis until it reaches the end of the day. The end // of day is assumed to be 4:00pm (it depends on the stock exchange). DateTime lastTime = timeStamps[timeStamps.Length - 1]; int extraTrailingPoints = (int)(new TimeSpan(16, 0, 0).Subtract( lastTime.TimeOfDay).TotalSeconds / resolution); if (extraTrailingPoints > 0) { DateTime[] extendedTimeStamps = new DateTime[timeStamps.Length + extraTrailingPoints]; Array.Copy(timeStamps, 0, extendedTimeStamps, 0, timeStamps.Length); for (int i = 0; i < extraTrailingPoints; ++i) { extendedTimeStamps[i + timeStamps.Length] = lastTime.AddSeconds( resolution * i); } timeStamps = extendedTimeStamps; } } // // At this stage, all data is available. We can draw the chart as according to // user input. // // // Determine the chart size. In this demo, user can select 4 different chart // sizes. Default is the large chart size. // int width = 780; int mainHeight = 255; int indicatorHeight = 80; string selectedSize = ((ListItem)chartSize.SelectedItem).Key; if (selectedSize == "S") { // Small chart size width = 450; mainHeight = 160; indicatorHeight = 60; } else if (selectedSize == "M") { // Medium chart size width = 620; mainHeight = 215; indicatorHeight = 70; } else if (selectedSize == "H") { // Huge chart size width = 1000; mainHeight = 320; indicatorHeight = 90; } // Create the chart object using the selected size FinanceChart m = new FinanceChart(width); // Set the data into the chart object m.setData(timeStamps, highData, lowData, openData, closeData, volData, extraPoints); // // We configure the title of the chart. In this demo chart design, we put the // company name as the top line of the title with left alignment. // m.addPlotAreaTitle(Chart.TopLeft, tickerKey); // We displays the current date as well as the data resolution on the next line. string resolutionText = ""; if (resolution == 30 * 86400) { resolutionText = "Monthly"; } else if (resolution == 7 * 86400) { resolutionText = "Weekly"; } else if (resolution == 86400) { resolutionText = "Daily"; } else if (resolution == 900) { resolutionText = "15-min"; } m.addPlotAreaTitle(Chart.BottomLeft, "<*font=Arial,size=8*>" + m.formatValue( DateTime.Now, "mmm dd, yyyy") + " - " + resolutionText + " chart"); // A copyright message at the bottom left corner the title area m.addPlotAreaTitle(Chart.BottomRight, "<*font=arial.ttf,size=8*>(c) Advanced Software Engineering"); // // Add the first techical indicator according. In this demo, we draw the first // indicator on top of the main chart. // addIndicator(m, ((ListItem)indicator1.SelectedItem).Key, indicatorHeight); // // Add the main chart // m.addMainChart(mainHeight); // // Set log or linear scale according to user preference // m.setLogScale(logScale.IsChecked.GetValueOrDefault()); // // Set axis labels to show data values or percentage change to user preference // if (percentageScale.IsChecked.GetValueOrDefault()) { m.setPercentageAxis(); } // // Draw any price line the user has selected // string mainType = ((ListItem)chartType.SelectedItem).Key; if (mainType == "Close") { m.addCloseLine(0x000040); } else if (mainType == "TP") { m.addTypicalPrice(0x000040); } else if (mainType == "WC") { m.addWeightedClose(0x000040); } else if (mainType == "Median") { m.addMedianPrice(0x000040); } // // Add comparison line if there is data for comparison // if ((compareData != null) && (compareData.Length > extraPoints)) { m.addComparison(compareData, 0x0000ff, compareKey); } // // Add moving average lines. // addMovingAvg(m, ((ListItem)avgType1.SelectedItem).Key, avgPeriod1, 0x663300); addMovingAvg(m, ((ListItem)avgType2.SelectedItem).Key, avgPeriod2, 0x9900ff); // // Draw candlesticks or OHLC symbols if the user has selected them. // if (mainType == "CandleStick") { m.addCandleStick(0x33ff33, 0xff3333); } else if (mainType == "OHLC") { m.addHLOC(0x008800, 0xcc0000); } // // Add parabolic SAR if necessary // if (parabolicSAR.IsChecked.GetValueOrDefault()) { m.addParabolicSAR(0.02, 0.02, 0.2, Chart.DiamondShape, 5, 0x008800, 0x000000); } // // Add price band/channel/envelop to the chart according to user selection // string selectedBand = ((ListItem)priceBand.SelectedItem).Key; if (selectedBand == "BB") { m.addBollingerBand(20, 2, 0x9999ff, unchecked ((int)(0xc06666ff))); } else if (selectedBand == "DC") { m.addDonchianChannel(20, 0x9999ff, unchecked ((int)(0xc06666ff))); } else if (selectedBand == "Envelop") { m.addEnvelop(20, 0.1, 0x9999ff, unchecked ((int)(0xc06666ff))); } // // Add volume bars to the main chart if necessary // if (volumeBars.IsChecked.GetValueOrDefault()) { m.addVolBars(indicatorHeight, 0x99ff99, 0xff9999, 0xc0c0c0); } // // Add additional indicators as according to user selection. // addIndicator(m, ((ListItem)indicator2.SelectedItem).Key, indicatorHeight); addIndicator(m, ((ListItem)indicator3.SelectedItem).Key, indicatorHeight); addIndicator(m, ((ListItem)indicator4.SelectedItem).Key, indicatorHeight); // // output the chart // viewer.Chart = m; // // tooltips for the chart // viewer.ImageMap = m.getHTMLImageMap("", "", "title='" + m.getToolTipDateFormat() + " {value|P}'"); }
/// <summary> /// Create a financial chart according to user selections. The user selections are /// encoded in the query parameters. /// </summary> public BaseChart drawChart() { // In this demo, we just assume we plot up to the latest time. So end date is now. DateTime endDate = DateTime.Now; // If the trading day has not yet started (before 9:30am), or if the end date is on on Sat or // Sun, we set the end date to 4:00pm of the last trading day while ((endDate.TimeOfDay.CompareTo(new TimeSpan(9, 30, 0)) < 0) || (endDate.DayOfWeek == DayOfWeek.Sunday) || (endDate.DayOfWeek == DayOfWeek.Saturday)) { endDate = endDate.Date.AddDays(-1).Add(new TimeSpan(16, 0, 0)); } // The duration selected by the user int durationInDays = int.Parse(Request["TimeRange"]); // Compute the start date by subtracting the duration from the end date. DateTime startDate = endDate; if (durationInDays >= 30) { // More or equal to 30 days - so we use months as the unit startDate = new DateTime(endDate.Year, endDate.Month, 1).AddMonths(-durationInDays / 30); } else { // Less than 30 days - use day as the unit. The starting point of the axis is always at // the start of the day (9:30am). Note that we use trading days, so we skip Sat and Sun // in counting the days. startDate = endDate.Date; for (int i = 1; i < durationInDays; ++i) { if (startDate.DayOfWeek == DayOfWeek.Monday) { startDate = startDate.AddDays(-3); } else { startDate = startDate.AddDays(-1); } } } // The moving average periods selected by the user. int avgPeriod1 = 0; try { avgPeriod1 = int.Parse(Request["movAvg1"]);; } catch { avgPeriod1 = 0;; } int avgPeriod2 = 0; try { avgPeriod2 = int.Parse(Request["movAvg2"]);; } catch { avgPeriod2 = 0;; } if (avgPeriod1 < 0) { avgPeriod1 = 0; } else if (avgPeriod1 > 300) { avgPeriod1 = 300; } if (avgPeriod2 < 0) { avgPeriod2 = 0; } else if (avgPeriod2 > 300) { avgPeriod2 = 300; } // We need extra leading data points in order to compute moving averages. int extraPoints = 20; if (avgPeriod1 > extraPoints) { extraPoints = avgPeriod1; } if (avgPeriod2 > extraPoints) { extraPoints = avgPeriod2; } // Get the data series to compare with, if any. string compareKey = Request["CompareWith"].Trim(); compareData = null; if (getData(compareKey, startDate, endDate, durationInDays, extraPoints)) { compareData = closeData; } // The data series we want to get. string tickerKey = Request["TickerSymbol"].Trim(); if (!getData(tickerKey, startDate, endDate, durationInDays, extraPoints)) { return(errMsg("Please enter a valid ticker symbol")); } // We now confirm the actual number of extra points (data points that are before the start // date) as inferred using actual data from the database. extraPoints = timeStamps.Length; for (int i = 0; i < timeStamps.Length; ++i) { if (timeStamps[i] >= startDate) { extraPoints = i; break; } } // Check if there is any valid data if (extraPoints >= timeStamps.Length) { // No data - just display the no data message. return(errMsg("No data available for the specified time period")); } // In some finance chart presentation style, even if the data for the latest day is not fully // available, the axis for the entire day will still be drawn, where no data will appear near // the end of the axis. if (resolution < 86400) { // Add extra points to the axis until it reaches the end of the day. The end of day is // assumed to be 16:00 (it depends on the stock exchange). DateTime lastTime = timeStamps[timeStamps.Length - 1]; int extraTrailingPoints = (int)(new TimeSpan(16, 0, 0).Subtract(lastTime.TimeOfDay ).TotalSeconds / resolution); if (extraTrailingPoints > 0) { DateTime[] extendedTimeStamps = new DateTime[timeStamps.Length + extraTrailingPoints ]; Array.Copy(timeStamps, 0, extendedTimeStamps, 0, timeStamps.Length); for (int i = 0; i < extraTrailingPoints; ++i) { extendedTimeStamps[i + timeStamps.Length] = lastTime.AddSeconds(resolution * (i + 1)); } timeStamps = extendedTimeStamps; } } // // At this stage, all data are available. We can draw the chart as according to user input. // // // Determine the chart size. In this demo, user can select 4 different chart sizes. Default // is the large chart size. // int width = 780; int mainHeight = 255; int indicatorHeight = 80; string size = Request["ChartSize"]; if (size == "S") { // Small chart size width = 450; mainHeight = 160; indicatorHeight = 60; } else if (size == "M") { // Medium chart size width = 620; mainHeight = 215; indicatorHeight = 70; } else if (size == "H") { // Huge chart size width = 1000; mainHeight = 320; indicatorHeight = 90; } // Create the chart object using the selected size FinanceChart m = new FinanceChart(width); // Set the data into the chart object m.setData(timeStamps, highData, lowData, openData, closeData, volData, extraPoints); // // We configure the title of the chart. In this demo chart design, we put the company name as // the top line of the title with left alignment. // m.addPlotAreaTitle(Chart.TopLeft, tickerKey); // We displays the current date as well as the data resolution on the next line. string resolutionText = ""; if (resolution == 30 * 86400) { resolutionText = "Monthly"; } else if (resolution == 7 * 86400) { resolutionText = "Weekly"; } else if (resolution == 86400) { resolutionText = "Daily"; } else if (resolution == 900) { resolutionText = "15-min"; } m.addPlotAreaTitle(Chart.BottomLeft, "<*font=Arial,size=8*>" + m.formatValue(DateTime.Now, "mmm dd, yyyy") + " - " + resolutionText + " chart"); // A copyright message at the bottom left corner the title area m.addPlotAreaTitle(Chart.BottomRight, "<*font=Arial,size=8*>(c) Advanced Software Engineering"); // // Add the first techical indicator according. In this demo, we draw the first indicator on // top of the main chart. // addIndicator(m, Request["Indicator1"], indicatorHeight); // // Add the main chart // m.addMainChart(mainHeight); // // Set log or linear scale according to user preference // if (Request["LogScale"] == "1") { m.setLogScale(true); } // // Set axis labels to show data values or percentage change to user preference // if (Request["PercentageScale"] == "1") { m.setPercentageAxis(); } // // Draw any price line the user has selected // string mainType = Request["ChartType"]; if (mainType == "Close") { m.addCloseLine(0x000040); } else if (mainType == "TP") { m.addTypicalPrice(0x000040); } else if (mainType == "WC") { m.addWeightedClose(0x000040); } else if (mainType == "Median") { m.addMedianPrice(0x000040); } // // Add comparison line if there is data for comparison // if (compareData != null) { if (compareData.Length > extraPoints) { m.addComparison(compareData, 0x0000ff, compareKey); } } // // Add moving average lines. // addMovingAvg(m, Request["avgType1"], avgPeriod1, 0x663300); addMovingAvg(m, Request["avgType2"], avgPeriod2, 0x9900ff); // // Draw candlesticks or OHLC symbols if the user has selected them. // if (mainType == "CandleStick") { m.addCandleStick(0x33ff33, 0xff3333); } else if (mainType == "OHLC") { m.addHLOC(0x008800, 0xcc0000); } // // Add parabolic SAR if necessary // if (Request["ParabolicSAR"] == "1") { m.addParabolicSAR(0.02, 0.02, 0.2, Chart.DiamondShape, 5, 0x008800, 0x000000); } // // Add price band/channel/envelop to the chart according to user selection // string bandType = Request["Band"]; if (bandType == "BB") { m.addBollingerBand(20, 2, 0x9999ff, unchecked ((int)0xc06666ff)); } else if (bandType == "DC") { m.addDonchianChannel(20, 0x9999ff, unchecked ((int)0xc06666ff)); } else if (bandType == "Envelop") { m.addEnvelop(20, 0.1, 0x9999ff, unchecked ((int)0xc06666ff)); } // // Add volume bars to the main chart if necessary // if (Request["Volume"] == "1") { m.addVolBars(indicatorHeight, 0x99ff99, 0xff9999, 0xc0c0c0); } // // Add additional indicators as according to user selection. // addIndicator(m, Request["Indicator2"], indicatorHeight); addIndicator(m, Request["Indicator3"], indicatorHeight); addIndicator(m, Request["Indicator4"], indicatorHeight); return(m); }