Flash Historical Stock Chart Tutorial (Yahoo Data)


 

I like investing in and following the stock market. I also like developing in Adobe Flash. What a great flash tutorial idea to develop a stock application! What I have got for everyone here today is an interactive chart in Flash that dynamically grabs stock prices and other data from the Yahoo finance feed , parses, and then displays historical prices for any stock from it’s IPO (initial public offering) to present day. Additionally, because of sandbox security restrictions, your flash player cannot access external sites such as yahoo.com, only items on the same hosting server. To bypass this security restriction, a couple of php proxys were also written and stored on my server, which act as middlemen for the flash application to grab ticker information from Yahoo icharts.


The entire application is a couple hundred lines of code, but on the positive side, this is a pure actionscript 3 application, so no actual Flash IDE functionality,  external movieclips, etc.. was needed! Directly below is the source code for the tutorial in addition to a LIVE, WORKING demo of the stock chart! Enjoy this great Flash and Actionscript 3 tutorial, from the one and only, flash cove tutorial site!

Place both the flash file and two php scripts (BE SURE TO CHANGE FORMATTING FROM .TXT TO .PHP!) in the same directory/folder, and then run the “stock_chart.fla”



Here’s the final yahoo finance based stock graph, in a compiled flash program, for your enjoyment:

You can trace the historical stock prices anywhere on the flash chart by tracing along with your mouse. I defaulted with the big blue chip Microsoft (MSFT), but you can search any stock listed on the market by typing it up in the top right corner, such as AAPL for Apple Inc, CSCO for Cisco, CMG for Chipotle, F for FORD, YHOO for Yahoo, ONTY for Oncothyreon (which, with Stimuvax, is a great stock right now), etc..., and then pressing enter. Enjoy!

With that, lets get to work on the actual flash tutorial for this sweet stock application.

 

Since there is a lot of code for this flash stock chart tutorial, I am only going to go over the actionscript source code in high detail. It is up to you to decipher the smaller facets within. I’ve lined the code with a few comments to help you identify where certain objects are laid out and created, but feel free to leave a comment below if you need any help or want to post some suggestions to me or others on the flash cove.

With that, let’s get to the actual development for this flash tutorial:

INITIALIZATION

var arr:Array;
var max:Number;
var max_volume:Number;
var increment_level:Number;
var titleLoader:URLLoader;
var input_field:TextField = new TextField();
var stock_line:Sprite;
var volume_array:Array;
var year_array:Array;
var top_left:Point = new Point(15,40);
var dimensions:Point = new Point(800,300);

var displayed_data:Array = new Array();

This first part is pretty easy, we are just initializing some global variables in flash for our stock chart to use.

  • An array to stock price numbers and data
  • variables for max stock price and maximum volume
  • for every pixel on the chart, how many days we advance in the price history (that’s our increment_level)
  • titleLoader is our download wrapper for obtaining the tickers company name
  • input_field is where we can type in new stock tickers to search
  • stock_line is the actual stock price line on the screen, a flash sprite
  • volume_array is the series of lines at the bottom of the stock chart indicating volume; all sprite lines.
  • year_array are all the little text values indicating years on the bottom of the stock price chart (from IPO to current)
  • top_left is a point marking where our chart begins on the screen
  • dimensions obviously mark the width and height we want for the chart. This is scalable to whatever you like
  • displayed_data is storage for our formatting setups

And with that, let’s get to a little bit more initialization. Some actual sprites and graphics on the flash screen.

//initialization of all the little labels and formatting for the stock chart

var current_stock:String = "MSFT";

var square:Sprite = new Sprite();
addChild(square);
square.graphics.lineStyle(3,0x333333);
square.graphics.beginFill(0x111111);
square.graphics.drawRect(top_left.x,top_left.y,dimensions.x,dimensions.y);
square.graphics.endFill();

square.addEventListener(MouseEvent.MOUSE_MOVE, show_popup);

var volume_square:Sprite = new Sprite();
addChild(volume_square);
volume_square.graphics.lineStyle(3,0x333333);
volume_square.graphics.beginFill(0x111111);
volume_square.graphics.drawRect(top_left.x,top_left.y + dimensions.y + 22,dimensions.x,dimensions.y/6);
volume_square.graphics.endFill();

square.addEventListener(MouseEvent.MOUSE_MOVE, show_popup);


var tracer:MovieClip = new MovieClip();
tracer.graphics.lineStyle(1,0x222222);
tracer.graphics.beginFill(0xffcc00);
tracer.graphics.drawCircle(0,0,3);
square.addChild(tracer);
tracer.graphics.endFill();
tracer.visible = false;

var tracer_line:MovieClip = new MovieClip();
tracer_line.graphics.lineStyle(1,0xFFE87C);
tracer_line.graphics.moveTo(0,top_left.y);
tracer_line.graphics.lineTo(0,top_left.y+dimensions.y);
square.addChild(tracer_line);
tracer_line.visible = false;

var tracer_price:MovieClip = new MovieClip();
tracer_price.graphics.lineStyle(1,0x817339);
tracer_price.graphics.beginFill(0x111111);
tracer_price.graphics.drawRoundRect(-40,top_left.y - 5,80,12,10,10);
tracer_price.graphics.endFill();

var price_field:TextField = new TextField();
price_field.type = TextFieldType.DYNAMIC;
price_field.border = false;
price_field.x = -40;
price_field.y = top_left.y - 8;
price_field.selectable = false;
price_field.height = 16;
price_field.width = 80;
price_field.multiline = false;
price_field.wordWrap = false;
price_field.background = false;
price_field.text = "MSFT";

var price_format:TextFormat = new TextFormat();
price_format.color = 0xFDD017;
price_format.size = 8;
price_format.font = "Verdana";
price_format.align = TextFormatAlign.CENTER;
tracer_price.addChild(price_field);
price_field.setTextFormat(price_format);

stage.addChild(tracer_price);
tracer_price.visible = false;

var tracer_date:MovieClip = new MovieClip();
tracer_date.graphics.lineStyle(1,0x817339);
tracer_date.graphics.beginFill(0x111111);
tracer_date.graphics.drawRoundRect(-50,top_left.y + dimensions.y - 5,100,12,10,10);
tracer_date.graphics.endFill();

var date_field:TextField = new TextField();
date_field.type = TextFieldType.DYNAMIC;
date_field.border = false;
date_field.x = -40;
date_field.y = top_left.y - 8 + dimensions.y;
date_field.selectable = false;
date_field.height = 16;
date_field.width = 80;
date_field.multiline = false;
date_field.wordWrap = false;
date_field.background = false;
date_field.text = "MSFT";

var date_format:TextFormat = new TextFormat();
date_format.color = 0xFDD017;
date_format.size = 12;
date_format.font = "Verdana";
date_format.align = TextFormatAlign.CENTER;
tracer_date.addChild(date_field);
date_field.setTextFormat(date_format);

stage.addChild(tracer_date);
tracer_date.visible = false;

All this scary looking stuff is just the box where our stock line goes, the box where the volume lines go down below, and then the vertical tracer line that shows a specific stock price and the date it is recorded for. We initialize colors, sizes, and positions, and then add the necessary event listeners that go along with each element in the flash tutorial.

The next part of learning for this flash stock tutorial, is the event listener for ourpopup / tracer linethat we initialized in the previous chunk of actionscript code above.

POP-UP/TRACER EVENT LISTENER:

function show_popup(e:MouseEvent):void{ //these are the two little ovals that follow our vertical tracer line
    if(mouseX < top_left.x + dimensions.x && mouseX > top_left.x){
        tracer.visible = true;
        tracer.x = mouseX;
        tracer_line.visible = true;
        tracer_line.x = mouseX;
        tracer_price.visible = true;
        tracer_price.x = mouseX;
        tracer_date.visible = true;
        tracer_date.x = mouseX;
   
        var possib_y:Number = (top_left.y + dimensions.y) - ((arr[Math.floor((dimensions.x - (mouseX - top_left.x))*increment_level)][4] / max)*dimensions.y);
        price_field.text = "$"+arr[Math.floor((dimensions.x - (mouseX - top_left.x))*increment_level)][4].toString();
        date_field.text = arr[Math.floor((dimensions.x - (mouseX - top_left.x))*increment_level)][0].toString();
       
        var price_format:TextFormat = new TextFormat();
        price_format.color = 0xFDD017;
        price_format.size = 12;
        price_format.font = "Verdana";
        price_format.align = TextFormatAlign.CENTER;
        tracer_price.addChild(price_field);
        price_field.setTextFormat(price_format);
        date_field.setTextFormat(price_format);
       
        tracer.y = possib_y;
    }
}

If the mouse position is within our specified stock chart range, update the positioning for our tracer popup and all related data. Every time the tracer is updated, we also need to reflect that in the text formatting for the stock price. That’s the code you see at the bottom of this function.

The next chunk of actionscript code, which is our largest and most complex, is the initial construction of the stock chart and graphs. We make a request to my php script on the flashcove.net server, it in turn makes a request to the yahoo finance servers, gets a file back called “table.csv“, and then that is fed back in to flash via a return variable to the URLLoader function. Once that data is safe in our hands, we parse through, find price and volume maximums, break the dates in to meaningful chunks, and display on-screen.

CHART CONSTRUCTION:

var csvLoader:URLLoader = new URLLoader();
csvLoader.dataFormat = URLLoaderDataFormat.TEXT;
csvLoader.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent):void{ trace(e); }); //error listening, just in case
csvLoader.load(new URLRequest("http://flashcove.net/wp-content/get_stock.php?url="+current_stock));
csvLoader.addEventListener(Event.COMPLETE,CSVLoaded);

function CSVLoaded(e:Event):void {
    var str:String = csvLoader.data as String;
    arr = str.split("\n"); //break it up
    arr[1] = arr[1].split(',');
    max = Number(arr[1][4]);
    max_volume = Number(arr[1][5]); //find our max volume and price to set a perspective scale
    for (var i:int=2; i<arr.length; i++) {
        arr[i] = arr[i].split(',');
        if(Number(arr[i][4]) > max){ max = Number(arr[i][4]); }
        if(Number(arr[i][5]) > max_volume){ max_volume = Number(arr[i][5]); }
    }

    //get the line ready!
    stock_line = new Sprite();
    stock_line.mouseEnabled = false;
    square.addChild(stock_line);
    stock_line.graphics.lineStyle(3,0x817339);
   
    //secondary lining for the stock price, makes it look cool
    var dropShadow:DropShadowFilter = new DropShadowFilter();
    dropShadow.distance = 2;
    dropShadow.angle = 45;
    dropShadow.color = 0xFFE87C;
    dropShadow.alpha = 1.0;
    dropShadow.blurX = 1;
    dropShadow.blurY = 1;
    dropShadow.strength = 1;
    dropShadow.quality = 15;
    dropShadow.inner = false;
    dropShadow.knockout = false;
    dropShadow.hideObject = false;  
    stock_line.filters = new Array(dropShadow);
   

   
    stock_line.graphics.moveTo((top_left.x+dimensions.x), (top_left.y + dimensions.y) - ((arr[1][4] / max)*dimensions.y));
   
   
    volume_array = new Array();
    year_array = new Array();
    increment_level = ((arr.length-1)/dimensions.x);
    var prev_item:int = int(arr[1][0].substring(0,4));
    var first_year:int = int(arr[arr.length-2][0].substring(0,4));
    for (var k:int=2; k < dimensions.x; k++) {
        if(int(arr[Math.floor(k*increment_level)][0].substring(0,4)) < prev_item){// && int(arr[Math.floor(k*increment_level)][0].substring(0,4)) > first_year){
            add_text("\'"+prev_item.toString().substring(2,4),50,(top_left.x+dimensions.x) - k - 8, top_left.y + dimensions.y + 4,8);
            //year line
            var year_line:Sprite = new Sprite();
            year_line.mouseEnabled = false;
            square.addChild(year_line);
            year_line.graphics.lineStyle(1,0xECD872,0.1);
            year_line.graphics.moveTo((top_left.x+dimensions.x) - k, (top_left.y + dimensions.y));
            year_line.graphics.lineTo((top_left.x+dimensions.x) - k, top_left.y);
            year_array.push(year_line);
        }
        prev_item = int(arr[Math.floor(k*increment_level)][0].substring(0,4));
       
        stock_line.graphics.lineTo((top_left.x+dimensions.x) - k, (top_left.y + dimensions.y) - ((arr[Math.floor(k*increment_level)][4] / max)*dimensions.y));
       
        //volume
        var volume_line:Sprite = new Sprite();
        volume_line.mouseEnabled = false;
        volume_square.addChild(volume_line);
        volume_line.graphics.lineStyle(1,0x817339);
        volume_line.graphics.moveTo((top_left.x+k), (top_left.y + dimensions.y) + 22 + (dimensions.y/6));
        volume_line.graphics.lineTo((top_left.x+k), ((top_left.y + dimensions.y) + 22 + (dimensions.y/6)) - ((arr[Math.floor(k*increment_level)][5] / max_volume)*(dimensions.y/6)));
        volume_array.push(volume_line);
    }
   
    //High-Low-Current prices
    add_text(max.toString(),50,top_left.x + dimensions.x + 4, top_left.y - 2,12);
    add_text("0",50,top_left.x + dimensions.x + 4, top_left.y + dimensions.y-2,12);
    add_text(arr[1][4].toString(),50,top_left.x + dimensions.x + 4, (top_left.y + dimensions.y) - ((arr[1][4] / max)*dimensions.y),12);
   
    //Input Box!
    input_field.type = TextFieldType.INPUT;
    input_field.border = true;
    input_field.x = top_left.x + 228 + dimensions.x - 350;
    input_field.y = top_left.y - 26;
    input_field.height = 16;
    input_field.width = 88;
    input_field.maxChars = 6;
    input_field.scrollH = 0;
    input_field.multiline = false;
    input_field.wordWrap = false;
    input_field.background = true;
    input_field.backgroundColor = 0x111111;
    input_field.borderColor = 0x333333;
    input_field.text = current_stock;
   
   
    input_field.addEventListener(KeyboardEvent.KEY_DOWN,new_stock_handler);
   
    var input_format:TextFormat = new TextFormat();
    input_format.color = 0xFDD017;
    input_format.size = 16;

    input_format.font = "Silkscreen";
    addChild(input_field);
    input_field.setTextFormat(input_format);
    input_field.embedFonts = true;
    input_field.antiAliasType = "advanced";
   
   
    add_text("Search New Ticker Symbol: ",200,top_left.x + dimensions.x - 350, top_left.y - 24,16);
   
    square.swapChildren(tracer,stock_line);
   
   
    //Plop on the company name / title
    titleLoader = new URLLoader();
    titleLoader.dataFormat = URLLoaderDataFormat.TEXT;
    titleLoader.load(new URLRequest("http://flashcove.net/wp-content/get_stock_name.php?url="+current_stock));
    titleLoader.addEventListener(Event.COMPLETE,grab_company_name);
   
}

The first for loop iterates over the received data, and stores it in to an array. We then setup our line display values (such as the drop shadow and thickness), and move the line sprite into starting position. Inside the second for loop, we actually draw a line segment between the current stock price and the next, creating the graph as we go along. If we see the beginning of a new year in the data, write down the date at the bottom of the graph and draw a thin gray line to represent it. Additionally, each time we add a little chunk to the stock history graph, we also draw a volume line down in the tiny box on the bottom to show volume history.

Just a few more bits to go, first we display the high, low, and current stock prices on the right-hand side of the stock chart, which you see changes every time I search a new stock on the flash demo. We also create the input box so that our users can search for more stock symbols. And finally, make a request to the yahoo finance servers again to grab the company’s name given that we already know the stock ticker (Either the initial “MSFT” or whatever a user types in the search bar)!

Three more functions to go!

COMPANY NAME FUNCTION:

function grab_company_name(e:Event):void{
    var str:String = titleLoader.data as String;
    var company:Array = str.split("\n");
    company[0] = company[0].split(',');
    company[0][0] = company[0][0].substring(1, company[0][0].length-2);
    add_text(company[0][0].toString(),200,top_left.x, top_left.y - 24,16);
}

Very similar to the function we used to grab stock price history before. Split the input file on newlines, and then grab the company name off the first data entry in the 2D array. Call “add_text” to create the actual instance on-screen in flash.

Next function, ADD TEXT HELPER:

function add_text(mess:String, wid:Number, xpos:Number, ypos:Number, font_size:Number):void{
//Creating the textfield object and naming it "myTextField"
var myTextField:TextField = new TextField();

//Here we add the new textfield instance to the stage with addchild()
addChild(myTextField);

//content, width, x, and y coordinates.
myTextField.text = mess;
myTextField.width = wid;
myTextField.x = xpos;
myTextField.y = ypos - 5;

//Here are some great properties to define, first one is to make sure the text is not selectable, and also disabling the border.
myTextField.selectable = false;
myTextField.border = false;

//textfield autosizes with the text, aligning to the left.
myTextField.autoSize = TextFieldAutoSize.LEFT;

// create a TextFormat instance
var myFormat:TextFormat = new TextFormat();

//format a hex decimal color code
myFormat.color = 0xECD872;

//set the text size
myFormat.size = font_size;

//set font.
myFormat.font = "Verdana";

//the MOST important thing, actually apply the formatting
myTextField.setTextFormat(myFormat);
displayed_data.push(myTextField);
}

This is a helper function we’ve used several times throughout the program. Rather than creating a full instance and construction for each and every text entity on screen, we can just make this one helper function and call IT repeatedly instead! It takes, as input, the text, width, x and y positioning, and font size. We could have made it a little more dynamic with font colors and such, but that wasn’t really necessary for this simple flash tutorial.

The only real peculiar thing here is the displayed_data.push(myTextField);. That line exists, because, when we switch to a new stock chart, all the old information needs to be removed from the previous stock. Rather than blindly search for the instances on the screen, it is super easy to just iterate through the displayed_data array, remove from screen, and then delete the data!

Which is exactly what’s shown in the last and final actionscript function.

NEW STOCK HANDLER:

// get key press only when the stock textfield is being edited
function new_stock_handler(event:KeyboardEvent){
   // if the key is ENTER
   if(event.charCode == 13){
       //clear out the stock price line
       stock_line.graphics.clear();
       for(var ff:int = 0; ff < displayed_data.length; ff++){
           removeChild(displayed_data[ff]);
       }
       displayed_data.splice(0,displayed_data.length);
       
       //clear out all the volume bars
       for(var gg:int = 0; gg < volume_array.length; gg++){
           volume_square.removeChild(volume_array[gg]);
           volume_array[gg] = null;
       }
       volume_array.splice(0,volume_array.length);
       
       //clear out all the year bars
       for(var hh:int = 0; hh < year_array.length; hh++){
           square.removeChild(year_array[hh]);
           year_array[hh] = null;
       }
       year_array.splice(0,year_array.length);
       
       current_stock = input_field.text;
        csvLoader = new URLLoader();
        csvLoader.dataFormat = URLLoaderDataFormat.TEXT;
        csvLoader.load(new URLRequest("http://flashcove.net/wp-content/get_stock.php?url="+input_field.text));
        csvLoader.addEventListener(Event.COMPLETE,CSVLoaded);
   }
}

This function checks for the “ENTER” strike form the input text box, and if it’s there, deletes everything currently onscreen, and restarts the stock chart initialization function with a new request for data from the Yahoo finance servers!



And that’s it for the actionscript code! Of course it’s a couple hundred lines long, and in some cases confusing and unclear, but if you need any help, first look at the actual compiled code in your own flash IDE, and then follow up with any questions in the comments section if you need clarification.

All that’s left now is the two PHP files we accessed in the actionscript code above, at the web addresses:

  • “http://flashcove.net/wp-content/get_stock.php?url=”+input_field.text
  • “http://flashcove.net/wp-content/get_stock_name.php?url=”+current_stock
  • “http://flashcove.net/wp-content/get_stock.php?url=”+current_stock

What exactly are these PHP files and what are they doing? It’s not voodoo magic, but at first it may be hard to understand, especially if you don’t have a lot of web experience.

There’s certain security restrictions within flash, and to prevent harmful things from happening to your computer, eternal data access can only happen on the same local PC as where the flash file resides. Since the stock information is hosted on yahoo.com servers, and not on my flashcove.net server, security restrictions prevent me from accessing stock information online.

HOWEVER, if I instead didn’t use flash to request yahoo’s finance data, and rather a PHP file on my own server, there would be no such restriction. This is exactly what I did. The get_stock.php file will grab stock information, and temporarily cache it on my flashcove.net server. Flash can then access that cached info locally, and use it to display some charts.

When we call the PHP file from inside flash, we can dynamically pass parameters via appending ?variable1=variabledata1&variable2=variabledata2&variable3=variabledata3&… to the PHP file call.
All we append though in this flash tutorial is the ticker title for the specific stock.

With that, let’s look at the get_stock.php contents:

<?php  $filename = $_GET["url"];
 $filename = "http://ichart.finance.yahoo.com/table.csv?s=$filename";
 header("Content-Type: text/csv; Content-Disposition:attachment; filename=\"$filename\"");
 readfile($filename);
?>

We grab the passed in ticker symbol from the “url” parameter, append it to the yahoo finance webserver call through the $filename variable, and then actually call readfile to execute the file transfer. This will automatically feed the response back in to the URLLoader event listener in flash, which can then be parsed for the graph.

The other PHP file we use, get_stock_name.php, is just a call to acquire the company name for display above the graph. It’s file structure is very similar to the get_stock.php setup.

<?php  $filename = $_GET["url"];
 $filename = "http://finance.yahoo.com/d/quotes.csv?s=$filename&f=n";
 header("Content-Type: text/csv; Content-Disposition:attachment; filename=\"$filename\"");
  readfile($filename);
?>

The only thing that changed was the yahoo request URL, and we also added “&f=n” to specify we want a COMPANY NAME returned (Hence the “n“).

You now have everything you need to turn this stock flash tutorial into your very own dream stock application. My specific flash demo is riddled with bugs, doesn’t handle a ticker that doesn’t exist, and also has many other small, annoying, issues. You could really make this a professional application just like the one Google or Yahoo Finance uses for their charts. With selectable date ranges, variation on sizes, zoom, intraday stock prices, EVERYTHING. I’ll probably come around again sometime and make a more polished version of this tutorial, but for now this is plenty of ammunition for your own projects.

Just remember that, if you’re going to create your own online or commercial version of this flash stock chart application, to update the URLs of the PHP files! You don’t want to be pinging mine, but rather your own. It’ll be faster and you won’t have a dependency on someone elses servers, only your own.

Thanks everyone for tuning in, and I wish you the best of luck on your own projects and endeavors in flash and the stock and finance world!

- Dave at the Flash Cove (http://flashcove.net)

About Dave

Hey Guys! I'm Dave, an Ohio State CSE alumni who just recently started work in the industry. I'm a flash hobbyist at heart, and love making flash games and tutorials when the time permits. Check out what flash tutorials are available on the site, and don't forget to "Like Us!" on Facebook!