search
Japanese Chinese Nederlands Espanol Italiano Deutsch Francais Twitter Rss Feeds
MicrosoftArticlesForumsFAQs
C# .NET
VB.NET
Visual Studio .NET
ADO.NET
Xml / Xslt
VB 6.0
.NET CF
GDI+
LINQ
Deployment
Security
FoxPro
Silverlight / WPF
Entity Framework
RIA Services

Web ProgrammingArticlesForumsFAQs
JavaScript
ASP
ASP.NET
Web Services

Non-MicrosoftArticlesForumsFAQs
NHibernate
Perl
PHP
Ruby
Java
Linux / Unix
Apple
Open Source

DatabasesArticlesForumsFAQs
SQL Server
Access
Oracle
MySQL
Other Databases

OfficeArticlesForumsFAQs
Excel
Word
Powerpoint
Outlook
Publisher
Money

Operating SystemsArticlesForumsFAQs
Windows 7
Windows Server
Windows Vista
Windows XP
Windows Update
MAC
Linux / UNIX

Server PlatformsArticlesForumsFAQs
BizTalk
Site Server
Exhange Server
IIS

Graphic DesignArticlesForumsFAQs
Macromedia Flash
Adobe PhotoShop
Expression Blend
Expression Design
Expression Web

OtherArticlesForumsFAQs
Subversion / CVS
Ask Dr. Dotnetsky
Active Directory
Networking
Uninstall Virus
Job Openings
Product Reviews
Search Engines
Resumes

 

Silverlight 2 Custom Stock Charts With Silverlight Toolkit


By Peter Bromberg
Printer Friendly Version
View My Articles
97 Views
    

Since the Silverlight Toolkit came out, I've been trying to find a couple of hours to start experimenting with the incredible number and quality of new controls that are now available. Well, I had some time Sunday afternoon, and I thought I'd take a crack at a custom Stock chart using the LineSeries chart from the Toolkit. This isn't feature-complete, but I think it's a pretty good start.


I started out with a Silverlight-enabled WCF Service that would allow me to use some previous code I'd written to accept a Stock (or index) Symbol, a beginning date and an ending date, and which would call out to the appropriate Yahoo Finance historical data url to get the csv- formatted list of historical quotes. This older code massaged the list into a DataTable. For Silverlight, I created a StockData class and simply iterated over the list of DataRows, turning it into a List<T> of type StockData, which is perfect for Silverlight to consume on the client side:

public class StockData
    {
        //Date    Open    High    Low    Close    Volume    Adj Close
        public DateTime Date { get; set; }
        public double Open { get; set;   }
        public double High { get; set; }
        public double Low { get; set;}
        public double Close { get; set; }
        public long Volume { get; set; }
        public double AdjClose { get; set; }
        public StockData() {}
        public StockData( DateTime date, double open, double high, double low, 
double close, long volume, double adjClose) { Date = date; Open = open; High = high; Low = low; Close = close; Volume = volume; AdjClose = adjClose; } }



When this is returned to the Silverlight calling application, it is exposed as an ObservableCollection which makes it easy to databind to the Chart control. Here is the "guts" of the Downloader class:

 
using System;
using System.Data;
using System.Net;

namespace StockCharts.Web
{
    public class Downloader
    {
        private string urlTemplate =
            @"http://ichart.finance.yahoo.com/table.csv?s=[symbol]&a=" +
            "[startMonth]&b=[startDay]&c=[startYear]&d=[endMonth]&e=" +
            "[endDay]&f=[endYear]&g=d&ignore=.csv";

        public DataTable UpdateSymbol(string symbol, DateTime? startDate, DateTime? endDate)
        {
            if (!endDate.HasValue) endDate = DateTime.Now;
            if (!startDate.HasValue) startDate = DateTime.Now.AddYears(-2);
            if (String.IsNullOrEmpty(symbol))
                throw new ArgumentException("Symbol invalid: " + symbol);
            // NOTE: Yahoo's scheme uses a month number 1 less than actual e.g. Jan. ="0"
            int strtMo = startDate.Value.Month - 1;
            string startMonth = strtMo.ToString();
            string startDay = startDate.Value.Day.ToString();
            string startYear = startDate.Value.Year.ToString();

            int endMo = endDate.Value.Month - 1;
            string endMonth = endMo.ToString();
            string endDay = endDate.Value.Day.ToString();
            string endYear = endDate.Value.Year.ToString();

            urlTemplate = urlTemplate.Replace("[symbol]", symbol);

            urlTemplate = urlTemplate.Replace("[startMonth]", startMonth);
            urlTemplate = urlTemplate.Replace("[startDay]", startDay);
            urlTemplate = urlTemplate.Replace("[startYear]", startYear);

            urlTemplate = urlTemplate.Replace("[endMonth]", endMonth);
            urlTemplate = urlTemplate.Replace("[endDay]", endDay);
            urlTemplate = urlTemplate.Replace("[endYear]", endYear);
            string history = String.Empty;
            var wc = new WebClient();
            try
            {
                history = wc.DownloadString(urlTemplate);
            }
            catch (WebException wex)
            {
                throw;
            }
            finally
            {
                wc.Dispose();
            }
            var dt = new DataTable();
            // trim off unused characters from end of line
            history = history.Replace("\r", "");
            // split to array on end of line
            string[] rows = history.Split('\n');
            // split to colums
            string[] colNames = rows[0].Split(',');
            // add the columns to the DataTable
            foreach (string colName in colNames)
                dt.Columns.Add(colName);
            DataRow row = null;
            string[] rowValues;
            object[] rowItems;
            // split the rows
            for (int i = rows.Length - 1; i > 0; i--)
            {
                rowValues = rows[i].Split(',');
                row = dt.NewRow();
                rowItems = ConvertStringArrayToObjectArray(rowValues);
                if (rowItems[0] != null && (string) rowItems[0] != "")
                {
                    row.ItemArray = rowItems;
                    dt.Rows.Add(row);
                }
            }
            return dt;
        }


        private object[] ConvertStringArrayToObjectArray(string[] input)
        {
            int elements = input.Length;
            var objArray = new object[elements];
            input.CopyTo(objArray, 0);
            return objArray;
        }
    }
}

For most developers, the above should be self-explanatory. Note the use of nullable DateTime parameters to allow for a default chart span. Next, I need an OperationContract in my service that makes a call to the above method:

        [OperationContract]
        public List<StockData> GetStockData(string symbol, DateTime startDate, DateTime endDate)
        {
            List<StockData> stockList = new List<StockData>();
            Downloader d = new Downloader();
            DataTable dt=  d.UpdateSymbol(symbol, startDate, endDate);
            // Date    Open    High    Low    Close    Volume    Adj Close
            foreach(DataRow row in dt.Rows)
            {
                DateTime date = Convert.ToDateTime(row["Date"]);
                double open = Convert.ToDouble(row["Open"]);
                double high = Convert.ToDouble(row["High"]);
                double low = Convert.ToDouble(row["Low"]);
                double close = Convert.ToDouble(row["Close"]);
                long volume = Convert.ToInt64(row["Volume"]);
                double adjClose = Convert.ToDouble(row["Adj Close"]);
                StockData sd = new StockData(date, open, high, low, close, volume, adjClose);
                stockList.Add(sd);
            }
            return stockList;
        }


Finally, here is the XAML Markup in my Page, and then, following that, the codebehind code in Page.xaml.cs:

<UserControl xmlns:charting="clr-namespace:Microsoft.Windows.Controls.DataVisualization.Charting;assembly=Microsoft.Windows.Controls.DataVisualization"  x:Class="StockCharts.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="600" Height="400">
    <Grid x:Name="LayoutRoot" Background="White">
        <charting:Chart x:Name="Chart1" Width="750" Height="350" VerticalAlignment="Top" >

            <charting:Chart.Series>

                <charting:LineSeries ItemsSource="{Binding}" 

                            DependentValueBinding="{Binding Close}" 

                            IndependentValueBinding="{Binding Date}" >

                    <charting:LineSeries.DataPointStyle>
                        <Style TargetType="Control">
                            <Setter Property="Visibility" Value="Collapsed"/>
                        </Style>
                    </charting:LineSeries.DataPointStyle>
                 </charting:LineSeries> 
 
            </charting:Chart.Series>
                
        </charting:Chart>
        <TextBox x:Name="text1" VerticalAlignment="Bottom" HorizontalAlignment="Left" Text="YHOO"></TextBox>
        <Controls:Button xmlns:Controls="clr-namespace:System.Windows.Controls;assembly=System.Windows"  Width="200"  Height="24" Content="Chart It!" VerticalAlignment="Bottom" Click="Button_Click"   ></Controls:Button>
        

    </Grid>
</UserControl>

 

And here is the codebehind that does the work:

 
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using StockCharts.StockService;

namespace StockCharts
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
        }

        void c_GetStockDataCompleted(object sender, GetStockDataCompletedEventArgs e)
        {  
            ObservableCollection stockDatas = e.Result;
            Chart1.LegendTitle = null;
            Chart1.Title = text1.Text;
            Dispatcher.BeginInvoke(() =>  this.Chart1.DataContext= stockDatas);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            string symbol = this.text1.Text;
            StockService.stocksClient c = new stocksClient();
            c.GetStockDataCompleted += new EventHandler(c_GetStockDataCompleted);
            c.GetStockDataAsync(symbol, DateTime.Now.AddYears(-2), DateTime.Now);
        }
    }
}

 

The result, when you fill in a stock or index symbol and press the button, looks like this:


Obviously this is just a start - If I had time and really wanted to make a top-rate stock charting app in Silverlight, I'd add a lower chart for the volume, and a selection of technical analysis indicators such as moving averages, stochastics, Relative Strength, and more.  I'd probably also add an autocomplete control for selecting the desired stock symbol from a typeahead dropdown list.

And of course, if I really wanted to get "flashy" (no pun intended) I'd think about how I can animate the chart as it appears.

But as a proof-of-concept, I can safely say that it doesn't take much to create really stunning chart graphics in Silverlight- no matter what the business application may be.

Microsoft's stock chart doesn't look very pretty right now, but then neither does anybody else's. It could be a while before we bottom out and have a new bull market -- a long while, unfortunately. If you really want to see a picture of a disaster, type in FNM in the symbol textbox. That's -- you guessed it -- Fannie Mae.

This stock chart Silverlight Application should run fine in Internet Explorer, Firefox, or Google Chrome (with the Dev Channel enabled).

Silverlight has matured remarkably over the last year or so, and has become a quite exciting RIA platform for developers. Stock charting and analysis applications are just the tip of the iceberg.

You can download the complete Visual Studio 2008 solution here.


Biography - Peter Bromberg
Peter Bromberg is a C# MVP, MCP, and .NET expert who has worked in banking, financial and telephony for over 20 years. Pete focuses exclusively on the .NET Platform, and currently develops SOA and other .NET applications for a Fortune 500 clientele. Peter enjoys producing digital photo collage with Maya,playing jazz flute, the beach, and fine wines. You can view Peter's UnBlog and IttyUrl sites.
Please post questions at forums, not via email!

button
Article Discussion: Silverlight 2 Custom Stock Charts With Silverlight Toolkit
Peter Bromberg posted at Sunday, November 02, 2008 6:47 PM
Original Article
 

Yahoo stock chart
Deepak Raghavan replied to Peter Bromberg at Friday, February 20, 2009 5:18 PM
I am trying to add animation to this example shell and a second chart to indicate the volume of stocks. Can you give me any pointers on how that can be accomplished.

Thanks in advance!