/*------------------------------------------------------------ Displaying live YTD stock prices using Yahoo as data source, and mimicing a graph style akin to what might be shown at http://finance.yahoo.com/echarts?s=GOOG Author: Michael Goldwasser ------------------------------------------------------------*/ String symbol = "YHOO"; // initial choice String stockName, caption; // set dynamically based on symbol int padding = 50; // margin between canvas edge and graph rectangle on each edge int X1, Y1, X2, Y2; // coordinate range for the graph Table data; float low, high, incr; // relevant range of stock prices void setup() { // drawing setup size(800, 600); X1 = padding; Y1 = padding; X2 = width - padding; Y2 = height - padding; textFont(createFont("SansSerif", padding/2)); loadData(); } void loadData() { symbol = symbol.toUpperCase(); // Load the full name of the stock for caption String url = "http://finance.yahoo.com/d/quotes.csv?f=n&s="; url += symbol; String full = loadStrings(url)[0]; if (full.equals("N/A")) { caption = "unknown (" + symbol + ")"; } else { stockName = full.substring(1, full.length()-1); // remove quotation marks caption = stockName + " (" + symbol + ")"; // Load the historical data url = "http://ichart.yahoo.com/table.csv?s="; url += symbol; url += "&a=0&b=1&c=1900"; // start date url += "&d=" + (month()-1); url += "&e=" + day(); url += "&f=" + year(); url += "&ignore=.csv"; data = loadTable(url, "header"); low = high = data.getFloat(0, "Adj Close"); for (int j=1; j < data.getRowCount(); j++) { float price = data.getFloat(j, "Adj Close"); low = min(low, price); high = max(high, price); } computeIncrements(); } symbol = ""; // reset for next time } // Determine vertical increment levels and rounded high/low values for graphing void computeIncrements() { float gap = (high - low)/8; // ideally we want 8 sections. int digits = nf(int(gap)).length(); int power = int(pow(10,digits-1)); incr = int(0.5 + gap/power)*power; low = int(low/incr)*incr; high = int(1 + high/incr)*incr; } void showGraph() { background(255); strokeWeight(1); stroke(0, 150, 233); fill(240, 250, 255); beginShape(); vertex(X1, Y2); vertex(X2, Y2); int n = data.getRowCount(); // number of days of trading for (int j=0; j < n; j++) { float price = data.getFloat(j, "Adj Close"); vertex(map(j, 0, n-1, X2, X1), map(price, low, high, Y2, Y1)); } endShape(); // draw guide lines and labels textAlign(LEFT, BOTTOM); stroke(127); fill(127); textSize(10); for (float price = low; price < high; price += incr) { float y = map(price,low,high,Y2,Y1); line(X1,y,width,y); text(nf(price,1,2), X2 + 10, y); } // draw bounding rectangle stroke(0); noFill(); rect(X1, Y1, X2-X1, Y2-Y1); } // Display interactive crosshairs for day indicated with mouse void showInteractive() { // determine y-value for relevant stock entry int j = int(map(mouseX, X1, X2, data.getRowCount()-1, 0)); float price = data.getFloat(j, "Adj Close"); float y = map(price, low, high, Y2, Y1); stroke(0); // dashed lines strokeWeight(1); for (int a=X1; a < X2; a += 5) { line(a, y, a+3, y); } for (int b=Y1; b < Y2; b += 5) { line(mouseX, b, mouseX, b+3); } // circle highlight strokeWeight(6); point(mouseX, y); showCurrentPrice(y, price); showCurrentDate(mouseX, data.getString(j, "Date")); showCurrentData(j); } void showCurrentPrice(float y, float price) { textSize(10); String disp = nf(price, 1, 2); float w = 4+textWidth(disp); strokeWeight(0); stroke(50); fill(50); triangle(X2, y, X2+7, y+7, X2+7, y-7); rect(X2+7, y-7, w, 14); fill(255); textAlign(LEFT, CENTER); text(disp, X2+9, y); } void showCurrentDate(float x, String date) { textSize(10); float w = 4+textWidth(date); strokeWeight(0); stroke(50); fill(50); rect(x-w/2, Y2, w, 14); fill(255); textAlign(CENTER, TOP); text(date, x, Y2+2); } String fields[] = {"Open", "Close", "Low", "High", "Volume"}; String fieldsDisplay[] = {"Open", "Close", "Low", "High", "Vol", "% Chg"}; // Show stock data for day with index j void showCurrentData(int j) { float minipad = 5; fill(255); stroke(0); strokeWeight(1); textSize(10); float w = textWidth("% chg XXXXX.XX%"); rect(X1+minipad, Y1+minipad, w + 2*minipad, 72 + 2*minipad); float ctrX = X1 + 2*minipad + textWidth("% chg "); fill(127); textAlign(RIGHT, TOP); for (int a=0; a < fieldsDisplay.length; a++) { text(fieldsDisplay[a]+" ", ctrX, Y1 + 2*minipad + 12*a); } textAlign(LEFT, TOP); float adj = data.getFloat(j, "Adj Close") / data.getFloat(j, "Close"); for (int a=0; a < fields.length; a++) { float val = data.getFloat(j, fields[a]); String valDisplay; if (a < fields.length-1) { valDisplay = nf(adj*val, 1, 2); } else { valDisplay = nf(val/1000000, 1, 2) + "M"; // volume in millions } text(" " + valDisplay, ctrX, Y1 + 2*minipad + 12*a); } float pctChange = 100 * data.getFloat(j, "Adj Close") / data.getFloat(data.getRowCount()-1, "Adj Close") - 100; text(" " + nf(pctChange, 1, 2) + "%", ctrX, Y1 + 2*minipad + 60); } void draw() { if (data != null) { showGraph(); if (mouseX >= X1 && mouseX <= X2 && mouseY >= Y1 && mouseY <= Y2) { showInteractive(); } } textAlign(LEFT, CENTER); textSize(padding/2); if (symbol.length() == 0) { fill(0); text(caption, X1, padding/2); } else { fill(255, 0, 0); text(symbol.toUpperCase(), X1, padding/2); } } void keyPressed() { if (key == ENTER || key == RETURN) { loadData(); // load data for current symbol } else if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z')) { symbol += key; } }