<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4261954431421179908</id><updated>2011-11-28T01:20:12.135Z</updated><title type='text'>Q-media</title><subtitle type='html'>Musings on music, software and Go.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.q-media.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4261954431421179908/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.q-media.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>stuffinq</name><uri>http://www.blogger.com/profile/05111167194178030567</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_aH5d0LSOPPA/TSoM3Xxa4-I/AAAAAAAAADI/rcrzFXUO3eI/S220/robocloud.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>6</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4261954431421179908.post-9171024246124561190</id><published>2011-02-06T19:26:00.005Z</published><updated>2011-02-07T16:43:01.875Z</updated><title type='text'>Fun with Financial Data (or As Much Fun As You Can Have Without Actually Making Any Money)</title><content type='html'>I've started reading the book &lt;a href="http://www.amazon.com/Data-Analysis-Open-Source-Tools/dp/0596802358"&gt;Data Analysis with Open Source Tools&lt;/a&gt; by Philipp K. Janert. It's very well written and informative, and I highly recommend it. I've only scratched the surface so far, but I decided that I wanted to try to apply some ideas from it to a problem that had been kicking around in my head for while.&lt;br /&gt;&lt;br /&gt;And then I figured: why not write a blog post about it! (After all, no one reads this thing anyway ;-)&lt;br /&gt;&lt;br /&gt;This problem occurred to me a while back while reading another O'Reilly book: &lt;a href="http://www.amazon.com/Programming-Collective-Intelligence-Building-Applications/dp/0596529325"&gt;Programming Collective Intelligence&lt;/a&gt; by Toby Segaran. The idea involves looking at a couple of pieces of information that are available on the web: Financial price data and financial news (both of which are freely available at &lt;a href="http://developer.yahoo.com/finance/"&gt;Yahoo! Finance&lt;/a&gt;). I realize that the data that Yahoo publishes is not particularly real-time and one wouldn't probably want to build an HFT system on it, but it will serve to do a little studying.&lt;br /&gt;&lt;br /&gt;I didn't really have a hypothesis in mind when I started. Taking a cue from Mr. Janert, I decided to take a look at the data with an open mind (no pre-conceived notions). At the heart of this inquiry, however, is a two-tier question:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Is there some kind of correlation between when news gets published online and when stock prices move? and&lt;/li&gt;&lt;li&gt;Is there a predictable sequential relationship that can be construed?&lt;/li&gt;&lt;/ol&gt;Why do this? Well, it's interesting. I'd been interested for a while in finding a worthwhile excuse to do something with financial data. Besides, there's a small chance that it might reveal an interesting opportunity (if news precedes predictable changes in price? Cha-ching!). More likely, however, I was expecting that I'd find that We, The Sheeple are trailing the HFT systems and insiders and there is little or no hope for us little folks to make a thin dime playing the market.&lt;br /&gt;&lt;br /&gt;Please note that this is a first pass at this. By no means am I imagining that this is a comprehensive approach. Indeed, I would be very grateful if anyone has any ideas for what to look at next (please comment!).&lt;br /&gt;&lt;br /&gt;For those who like the punchline first, here's the spoiler: the evidence is inconclusive as to whether any predictions can be made in price change based on headlines. Further study might reveal something, but at first glance, it just doesn't add up (pun intended).&lt;br /&gt;&lt;br /&gt;Conversely, and somewhat surprisingly, it is not conclusive that price changes precede news. In other words, it is not clear cut that the news follows the market.&lt;br /&gt;&lt;br /&gt;However, there is clearly a correlation in the data: big price shifts, high volume, and an increase in published news tend to go hand-in-hand. This is an unremarkable finding, but nonetheless, it was an interesting exercise.&lt;br /&gt;&lt;br /&gt;Now, despite having warned you that these findings are dull as dishwater, if you are really interested, I presume you are still with me...&lt;br /&gt;&lt;br /&gt;To analyze the problem, the first thing I did was to set up a method for capturing data. Once I had a bit of data to look at, then I could begin my inquiry.&lt;br /&gt;&lt;br /&gt;I don't really want to spend a lot of time discussing the specific technologies employed, as that's not really the point of this post, but I will just summarize this step briefly:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;A couple of shell scripts employing curl (via cron) to grab data at reasonable intervals: every minute for price data, and every hour for news data.&lt;/li&gt;&lt;li&gt;A couple of Scala scripts for parsing that data and dumping into a sqlite database.&lt;/li&gt;&lt;li&gt;SQL queries to grab the data (grouped as appropriate) in CSV format to pull into Excel (where I can create charts, where I can create charts for visualization, etc.)&lt;/li&gt;&lt;/ol&gt;As you can see, very low-tech, indeed. All in the spirit of keeping it simple. I thought about using &lt;a href="http://matplotlib.sourceforge.net/"&gt;matplotlib&lt;/a&gt;, which is a great Python-based tool for doing data analysis that I've had the pleasure of using a number of times in the past. But, as the data set I was focusing on was pretty small, Excel did the job nicely.&lt;br /&gt;&lt;br /&gt;In another iteration of this inquiry, I might be inclined to try out &lt;a href="http://www.r-project.org/"&gt;R&lt;/a&gt; or &lt;a href="http://www.gnu.org/software/octave/"&gt;Octave&lt;/a&gt;, and dig a little deeper. Perhaps I'll even take a further look at what data is available and try to expand the reach. If I'm feeling especially crazy, I might even give &lt;a href="http://hadoop.apache.org/"&gt;Hadoop&lt;/a&gt; a try.&lt;br /&gt;&lt;br /&gt;For purposes of this exercise, I will be looking at this data over the course of a single trading week: January 10-14 2011 (inclusive), and I'm just grabbing data on stocks that make up the Dow Jones Industrial Average. No particular reason other than that that was a convenient week to do it, and it seemed like a sufficient amount of time for this initial undertaking.&lt;br /&gt;&lt;br /&gt;I figured I would take a "big picture" view first. See whether just glancing at the data indicates anything interesting. If something jumps out, then drill down further and see where it takes me.&lt;br /&gt;&lt;br /&gt;To put this in context, here's a graph showing the Dow's movement around that time:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7t9ToaMMI/AAAAAAAAAEE/_pbetTkPBYU/s1600/DowJones-Jan10-14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="210" src="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7t9ToaMMI/AAAAAAAAAEE/_pbetTkPBYU/s400/DowJones-Jan10-14.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Prices had been coming down the previous week, but were poised to generally go up this week (hindsight being 20/20 and all).&lt;br /&gt;&lt;br /&gt;The first things I want to look at are:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;How many news headlines are there per stock per day? (And look for spikes in these numbers.)&lt;/li&gt;&lt;li&gt;Significant shifts in prices.&lt;/li&gt;&lt;li&gt;Significant shifts in volumes.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;That alone should establish where there's a relationship that's worth exploring. Doing this on a per stock per day over these few days should be sufficient to start. Then, if motivated, look at the intra-day prices and specific publish times of articles to see if one can determine whether it's the chicken or the egg that comes first.&lt;br /&gt;&lt;br /&gt;To start, here's some summary information about our headline data per stock per day (biggest ranges, and largest gaps between mean and max are shown highlighted):&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7vM4Xu6FI/AAAAAAAAAEQ/QdX0BOnXEIM/s1600/data-headlines1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="197" src="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7vM4Xu6FI/AAAAAAAAAEQ/QdX0BOnXEIM/s400/data-headlines1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And here it is again, measuring number of standard deviations from the mean (largest numbers highlighted):&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_aH5d0LSOPPA/TU7vR-sodZI/AAAAAAAAAEU/GsRMKOX35TM/s1600/data-headlines2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="267" src="http://2.bp.blogspot.com/_aH5d0LSOPPA/TU7vR-sodZI/AAAAAAAAAEU/GsRMKOX35TM/s400/data-headlines2.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Those spikes are a lot clearer when looking at them charted:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_aH5d0LSOPPA/TU7udJnsp0I/AAAAAAAAAEI/gNJYU6gLVVM/s1600/HeadlinesByCount.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://2.bp.blogspot.com/_aH5d0LSOPPA/TU7udJnsp0I/AAAAAAAAAEI/gNJYU6gLVVM/s400/HeadlinesByCount.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_aH5d0LSOPPA/TU7ui27OlzI/AAAAAAAAAEM/y1F56HO8akA/s1600/HeadlinesByStdDev.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://2.bp.blogspot.com/_aH5d0LSOPPA/TU7ui27OlzI/AAAAAAAAAEM/y1F56HO8akA/s400/HeadlinesByStdDev.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Looking at these, just based on spikes in number of news articles, I'd be interested in looking further at the following:&lt;br /&gt;&lt;br /&gt;(Because I know what headlines at Yahoo tend to get grouped with stocks in the same industries, I'm looking for those relationships as well.)&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;DuPont (DD), Johnson &amp;amp; Johnson (JNJ), and Procter &amp;amp; Gamble (PG) on Monday.&lt;/li&gt;&lt;li&gt;AT&amp;amp;T (T) and Verizon (VZ) on Tuesday.&lt;/li&gt;&lt;li&gt;Merck (MRK) and Pfizer (PFE) on Thursday.&lt;/li&gt;&lt;li&gt;Bank of America (BAC) and JP Morgan (JPM) on Friday.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Now, let's see if that jives with what we can see in price data (note: this is shown by price range for the day as a fluctuation (max-min) percentage from the previous day's close):&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_aH5d0LSOPPA/TU7vVI-qckI/AAAAAAAAAEY/ZH0FNt75BtY/s1600/data-prices1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="291" src="http://2.bp.blogspot.com/_aH5d0LSOPPA/TU7vVI-qckI/AAAAAAAAAEY/ZH0FNt75BtY/s400/data-prices1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7vjI2vrAI/AAAAAAAAAEk/QZ0YyiTxhr0/s1600/PriceChangePct.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7vjI2vrAI/AAAAAAAAAEk/QZ0YyiTxhr0/s400/PriceChangePct.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Looking at the price data, there is certainly enough coming through to believe that there is a correlation here. Much of the data points to the same stocks/days. I might be inclined to filter my list, however, as it least some of the headline data is likely to be associated with certain stocks only because of the industry relationship:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;DuPont (DD) on Monday.&lt;/li&gt;&lt;li&gt;AT&amp;amp;T (T) and Verizon (VZ) on Tuesday.&lt;/li&gt;&lt;li&gt;Merck (MRK) on Thursday.&lt;/li&gt;&lt;li&gt;Bank of America (BAC) and JP Morgan (JPM) on Friday.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;That reduces it to six different stocks on four different days, which seems like a reasonable number to analyze further (with two industry relationships to look at).&lt;br /&gt;&lt;br /&gt;I might also be inclined to add some based on the price data alone, like McDonald's (MCD) or American Express (AXP) on Friday, but when looking at the headline data, there's not really enough to go on. (Most of the headlines for McDonald's on the Friday were actually about Starbucks.)&lt;br /&gt;&lt;br /&gt;And, just to complete the picture, I'll have a look at volumes as well.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7vaIZ-4PI/AAAAAAAAAEc/6-6WnrrSL4s/s1600/data-volume1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="186" src="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7vaIZ-4PI/AAAAAAAAAEc/6-6WnrrSL4s/s400/data-volume1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7veKOmsbI/AAAAAAAAAEg/cRHzeKhyiKU/s1600/data-volume2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="341" src="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7veKOmsbI/AAAAAAAAAEg/cRHzeKhyiKU/s400/data-volume2.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7vnP7qE_I/AAAAAAAAAEo/GrDGJopas6M/s1600/VolumeSpikes.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7vnP7qE_I/AAAAAAAAAEo/GrDGJopas6M/s400/VolumeSpikes.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU77amXgXaI/AAAAAAAAAGA/OIL73QvnK40/s1600/VolChangeByStdDev.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU77amXgXaI/AAAAAAAAAGA/OIL73QvnK40/s400/VolChangeByStdDev.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;br /&gt;Again, a lot of the same players emerge, which leads me to think that I'm on the right track. The correlation is clear. Now, I'll have to look at the intra-day data to see if one can draw any conclusions moment-to-moment on the specific scenarios that I've identified.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;DuPont (DD): Monday January 10th, 2011&lt;/h4&gt;&lt;h4&gt;&lt;span style="font-weight: normal;"&gt;First off, the data... (Note that the price column is price range during that hour as a percentage)&lt;/span&gt;&lt;/h4&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7xcaF4ucI/AAAAAAAAAE8/AXpxRmFgbLw/s1600/dd-data.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="185" src="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7xcaF4ucI/AAAAAAAAAE8/AXpxRmFgbLw/s400/dd-data.png" width="400" /&gt;&amp;nbsp;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;The following chart shows the deltas in headline count, price, and volume as a percentage of the total change over the course of the day (this allows unlike numbers to be compared in like manner). In particular, note that I've summed the delta of pre-open headlines into the first hour's number, which might exaggerate it's effect on the chart. But looking at the data you can certainly see that there was significant activity pre-open.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7xNaoxNvI/AAAAAAAAAE0/HwFcaKdi-Ck/s1600/dd-deltas.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7xNaoxNvI/AAAAAAAAAE0/HwFcaKdi-Ck/s400/dd-deltas.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And, price for the day...&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7xmvZvlBI/AAAAAAAAAFA/ohXV-elOUJo/s1600/dd-price.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7xmvZvlBI/AAAAAAAAAFA/ohXV-elOUJo/s400/dd-price.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;One thing's for certain, there was definitely a spike in the number of headlines at 11 am which &lt;i&gt;followed&lt;/i&gt; the steep change in price. But most of the headlines from that day were before the bell, and the big dip in price happened before 10 am. By 11 am the price was fairly stable.&lt;br /&gt;&lt;br /&gt;So, what were the headlines? Well, here are all the headlines that were published before 11 am:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;07:07 "Duke, Progress Energy, Sara Lee, Strayer, WellPoint: U.S. Equity Preview"&lt;/li&gt;&lt;li&gt;07:15 "US STOCKS-Futures lower on Portugal concerns despite M&amp;amp;A"&lt;/li&gt;&lt;li&gt;07:24 "CNNMoney Pre-Market Report - Jan. 10, 2011"&lt;/li&gt;&lt;li&gt;07:25 "DuPont to Buy Danisco for $5.8 Billion"&lt;/li&gt;&lt;li&gt;07:25 "Futures Fall; DuPont, Earnings in Focus"&lt;/li&gt;&lt;li&gt;07:32 "Disappointing Jobs Data Weighs on Futures"&lt;/li&gt;&lt;li&gt;07:45 "[$$] Danisco to Recommend DuPont Bid"&lt;/li&gt;&lt;li&gt;07:56 "US STOCKS-Futures lower as Portugal concerns offset M&amp;amp;A"&lt;/li&gt;&lt;li&gt;08:08 "Let's Make a Deal"&lt;/li&gt;&lt;li&gt;08:19 "Futures follow Asia, Europe lower"&lt;/li&gt;&lt;li&gt;08:27 "UPDATE 1-Goldman Sachs raises US chemicals sector to attractive"&lt;/li&gt;&lt;li&gt;08:30 "[$$] The Day Ahead: Merger Monday"&lt;/li&gt;&lt;li&gt;08:40 "UPDATE 3-DuPont's Danisco bid sends sector shares higher"&lt;/li&gt;&lt;li&gt;08:53 "Before the Bell: Duke Energy, DuPont, Verizon in spotlight"&lt;/li&gt;&lt;li&gt;08:59 "U.S. Stocks Poised For Weak Open"&lt;/li&gt;&lt;li&gt;09:09 "[video] News Hub: Before the Opening Bell [1.4 min]"&lt;/li&gt;&lt;li&gt;09:14 "[video] Faber Report"&lt;/li&gt;&lt;li&gt;09:20 "Danish currency firms on Danisco takeover"&lt;/li&gt;&lt;li&gt;09:40 "CNNMoney Market Report - Jan. 10, 2011"&lt;/li&gt;&lt;li&gt;09:44 "Dow Falls at Open Despite Rise in M&amp;amp;A"&lt;/li&gt;&lt;li&gt;09:47 "Verizon iPhone Announcement Seen for Tuesday"&lt;/li&gt;&lt;li&gt;09:48 "Stocks Dip on Euro Debt Concerns"&lt;/li&gt;&lt;li&gt;09:50 "Portugal Bailout Push Mars Merger Monday"&lt;/li&gt;&lt;li&gt;09:59 "[audio] Opening Bell from MarketWatch Radio Network [1.0 min]"&lt;/li&gt;&lt;li&gt;10:00 "Monday Morning Quarterback: Mergers, Foreclosure and Hank Moody"&lt;/li&gt;&lt;li&gt;10:16 "Wall St down as Portugal woes offset M&amp;amp;A deals"&lt;/li&gt;&lt;li&gt;10:39 "US equities fall on eurozone fears"&lt;/li&gt;&lt;li&gt;10:57 "Stocks to Watch: Union Pacific, Exxon and More ..."&lt;/li&gt;&lt;/ul&gt;Clearly the buyout of Danisco was a big factor here, but I'm having a hard time understanding why it had the effect it did on price. It almost seems counter-intuitive that the share price should fall so dramatically in the first 20 minutes of trading, given the seemingly "good news" of this buyout. Perhaps it's something to do with exchange rates and the European problems that were weighing on the market? Was it this: &lt;a href="http://www.reuters.com/article/2011/01/10/denmark-crown-danisco-idUSDKT00521320110110?feedType=RSS&amp;amp;feedName=rbssFinancialServicesAndRealEstateNews&amp;amp;rpc=43"&gt;Danish currency firms on Danisco takeover&lt;/a&gt;? (09:20)&lt;br /&gt;&lt;br /&gt;Interesting, but by no means conclusive.&lt;br /&gt;&lt;br /&gt;One headline in there jumps out, however, and makes a nice segue:&lt;br /&gt;&lt;br /&gt;09:47 "Verizon iPhone Announcement Seen for Tuesday"&lt;br /&gt;&lt;br /&gt;That'll explain our next subject: Verizon and AT&amp;amp;T activity on Tuesday.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;&amp;nbsp;Verizon (VZ): Tuesday January 11th, 2011&lt;/h4&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7x_mpEpDI/AAAAAAAAAFE/3XNwa5dQySE/s1600/vz-data.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="258" src="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7x_mpEpDI/AAAAAAAAAFE/3XNwa5dQySE/s400/vz-data.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7yEFiCGrI/AAAAAAAAAFI/oH4_K5k6PF4/s1600/vz-deltas.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7yEFiCGrI/AAAAAAAAAFI/oH4_K5k6PF4/s400/vz-deltas.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7yGsh17LI/AAAAAAAAAFM/c5o6eqvGvks/s1600/vz-price.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7yGsh17LI/AAAAAAAAAFM/c5o6eqvGvks/s400/vz-price.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;h4 style="font-weight: normal;"&gt;&amp;nbsp;The spike in price at around 11 am when all those articles were published (which corresponded to the time of the Verizon iPhone launch) is certainly interesting. But I don't think it would take a genius to figure out that that might happen.&lt;/h4&gt;The other scenarios below also confirm what was stated previously, so I'm happy to leave the rest of this as an exercise for the reader. There is a correlation between headline count and price changes, but not a clearly sequential one. Again, I welcome feedback or ideas. I hope to come back to this at some stage and delve a little deeper. In the meantime, here's the rest of that data, starting with the Verizon headlines...&lt;br /&gt;&lt;ul&gt;&lt;li&gt;00:01 "IPhone May Cost Verizon Wireless $5 Billion in Subsidies in First Year"&lt;/li&gt;&lt;li&gt;00:15 "ANALYSIS - AT&amp;amp;T faces tough year after losing iPhone exclusive"&lt;/li&gt;&lt;li&gt;02:47 "Telecoms' Dividend Yields"&lt;/li&gt;&lt;li&gt;03:03 "With iPhone Taunts, AT&amp;amp;T-Verizon Rivalry Escalates"&lt;/li&gt;&lt;li&gt;03:51 "11 High Yield Stocks to Consider Now"&lt;/li&gt;&lt;li&gt;05:00 "[video] Verizon iPhone Data Plan"&lt;/li&gt;&lt;li&gt;06:00 "Nielsen: Young People Across The Globe Love Their Cell Phones (But Use Them Differently)"&lt;/li&gt;&lt;li&gt;06:45 "Half of Dow Companies to Post Profit Gains"&lt;/li&gt;&lt;li&gt;06:50 "Ahead of the Bell: Verizon iPhone"&lt;/li&gt;&lt;li&gt;07:27 "10 Things You Need To Know Before The Opening Bell"&lt;/li&gt;&lt;li&gt;07:27 "10 Things You Need To Know Before The Opening Bell"&lt;/li&gt;&lt;li&gt;07:30 "CNNMoney Pre-Market Report - Jan. 11, 2011"&lt;/li&gt;&lt;li&gt;07:32 "Lowenstein Says Verizon's Network Ready to Handle IPhone: Video"&lt;/li&gt;&lt;li&gt;07:39 "10 Things You Need To Know This Morning"&lt;/li&gt;&lt;li&gt;07:40 "iPhone Raises Stakes of AT&amp;amp;T-Verizon Rivalry"&lt;/li&gt;&lt;li&gt;07:45 "Verizon big winner from having iPhone? Not so fast"&lt;/li&gt;&lt;li&gt;07:58 "AT&amp;amp;T and Verizon Trade Taunts Over iPhone"&lt;/li&gt;&lt;li&gt;08:03 "[video] 3 Stocks I Saw on TV"&lt;/li&gt;&lt;li&gt;08:22 "Will Verizon Demolish AT&amp;amp;T? It Did So Long Ago"&lt;/li&gt;&lt;li&gt;08:24 "The Big Questions About Verizon's IPhone"&lt;/li&gt;&lt;li&gt;08:24 "Verizon big winner from having iPhone? Not so fast"&lt;/li&gt;&lt;li&gt;08:25 "McCourt Says Verizon's Network Can Handle Apple's IPhone: Video"&lt;/li&gt;&lt;li&gt;08:36 "How Important Is Offering the iPhone on Verizon to Apple?"&lt;/li&gt;&lt;li&gt;08:45 "[$$] ETF Play of the Day: XLK"&lt;/li&gt;&lt;li&gt;08:56 "Liveblog: Verizon set to launch the iPhone. Finally."&lt;/li&gt;&lt;li&gt;09:02 "[video] AM Report: Chinese Stealth Jet Takes Flight [8.0 min]"&lt;/li&gt;&lt;li&gt;09:17 "[video] 3 Networking Stocks Set for Cloud Boost"&lt;/li&gt;&lt;li&gt;09:29 "Verizon iPhone Faces Even Bigger Foe"&lt;/li&gt;&lt;li&gt;09:33 "Trujillo Says 'Many Questions' Remain For Verizon IPhone: Video"&lt;/li&gt;&lt;li&gt;09:45 "Verizon IPhone Event Live Blog"&lt;/li&gt;&lt;li&gt;09:45 "[video] News Hub: Verizon iPhone: How will AT&amp;amp;T Respond? [2.7 min]"&lt;/li&gt;&lt;li&gt;09:52 "Verizon's Big iPhone Unveil"&lt;/li&gt;&lt;li&gt;09:52 "[video] Verizon's iPhone Buzz"&lt;/li&gt;&lt;li&gt;09:54 "Mobile Broadband Subs Seen Hitting 1 Billion In 2011"&lt;/li&gt;&lt;li&gt;09:58 "[audio] Opening Bell from MarketWatch Radio Network [1.0 min]"&lt;/li&gt;&lt;li&gt;10:03 "Video: Is the Verizon iPhone a Threat to Android?"&lt;/li&gt;&lt;li&gt;10:05 "Verizon iPhone expected to have unlimited data plan"&lt;/li&gt;&lt;li&gt;10:14 "[video] iPhone or Not, Verizon Is A Winner"&lt;/li&gt;&lt;li&gt;10:19 "Verizon iPhone: Live Blog"&lt;/li&gt;&lt;li&gt;10:37 "Verizon: Stay Tuned"&lt;/li&gt;&lt;li&gt;10:47 "LIVE: The Verizon iPhone Event"&lt;/li&gt;&lt;li&gt;10:53 "Faber Report: What Will Drive Verizon?"&lt;/li&gt;&lt;li&gt;10:54 "LIVE FROM NEW YORK: Verizon Gets the iPhone"&lt;/li&gt;&lt;li&gt;10:54 "Verizon iPhone: Will It Fix Verizon's $100 Billion Conundrum?"&lt;/li&gt;&lt;li&gt;10:55 "Live Blog: Verizon's iPhone Announcement"&lt;/li&gt;&lt;li&gt;10:57 "Live Blogging the Verizon iPhone Announcement"&lt;/li&gt;&lt;li&gt;11:18 "Verizon to launch iPhone 4 in February"&lt;/li&gt;&lt;li&gt;11:20 "Verizon iPhone: The basics"&lt;/li&gt;&lt;li&gt;11:24 "The Biggest Surprise About the Verizon iPhone: It's a Mobile Hotspot"&lt;/li&gt;&lt;li&gt;11:24 "Verizon to Carry Apple iPhone, Ending Months of Anticipation"&lt;/li&gt;&lt;li&gt;11:30 "Verizon Will Be Selling The iPhone Starting February 10th"&lt;/li&gt;&lt;li&gt;11:36 "Live Blog: Verizon's iPhone Announcement"&lt;/li&gt;&lt;li&gt;11:38 "UPDATE 2-Verizon Wireless to sell iPhone in February"&lt;/li&gt;&lt;li&gt;11:39 "PHOTOS: Verizon iPhone Has Different (Better?) Antenna Design Than AT&amp;amp;T iPhone"&lt;/li&gt;&lt;li&gt;11:39 "Verizon, AT&amp;amp;T, And Apple: Guess Which One's Stock Isn't Tanking Today"&lt;/li&gt;&lt;li&gt;11:41 "Verizon to Sell iPhone 4 on Feb. 10"&lt;/li&gt;&lt;li&gt;11:43 "Verizon Wireless to Offer iPhone 4 in February"&lt;/li&gt;&lt;li&gt;11:45 "Verizon iPhone: 'It Begins'"&lt;/li&gt;&lt;li&gt;11:50 "Verizon to begin selling iPhone 4 in February"&lt;/li&gt;&lt;li&gt;11:51 "Bizarre Fact of the Day: Tyler Durden and Louis Winthorpe III Are Practically the Same Person"&lt;/li&gt;&lt;li&gt;11:51 "Verizon to sell Apple iPhone"&lt;/li&gt;&lt;li&gt;11:51 "Verizon: Began Discussions With Apple In 2008; Tim Cook Talks"&lt;/li&gt;&lt;li&gt;11:54 "Verizon Wireless to sell iPhone in February"&lt;/li&gt;&lt;li&gt;12:17 "AT&amp;amp;T Preps for Verizon iPhone With New Ads, Price Cuts"&lt;/li&gt;&lt;li&gt;12:18 "[audio] Enderle: Verizon and Apple no match made in heaven [5.1 min]"&lt;/li&gt;&lt;li&gt;12:19 "Apple's Phil Schiller on Building the Verizon iPhone"&lt;/li&gt;&lt;li&gt;12:20 "AT&amp;amp;T Shrugs Off Verizon iPhone"&lt;/li&gt;&lt;li&gt;12:20 "COMPARISON: Verizon Vs. AT&amp;amp;T iPhone"&lt;/li&gt;&lt;li&gt;12:26 "FACTBOX - AT&amp;amp;T and Verizon wage battle over iPhone"&lt;/li&gt;&lt;li&gt;12:28 "Verizon iPhone: Yep, The Real Target is Google."&lt;/li&gt;&lt;li&gt;12:28 "Why No 4G iPhone?"&lt;/li&gt;&lt;li&gt;12:30 "AT&amp;amp;T: Five Reasons the Sky Won't Fall When iPhone Goes to Verizon"&lt;/li&gt;&lt;li&gt;12:35 "[audio] Midday Update from MarketWatch Radio Network [1.0 min]"&lt;/li&gt;&lt;li&gt;12:37 "FACTBOX - AT&amp;amp;T and Verizon wage battle over iPhone"&lt;/li&gt;&lt;li&gt;12:37 "Verizon Wireless to sell iPhone in February"&lt;/li&gt;&lt;li&gt;12:43 "Verizon to start selling iPhone on Feb. 10"&lt;/li&gt;&lt;li&gt;12:48 "Corrected: Factbox: AT&amp;amp;T and Verizon wage battle over iPhone"&lt;/li&gt;&lt;li&gt;12:50 "Verizon Will Offer the iPhone Next Month"&lt;/li&gt;&lt;li&gt;12:57 "Corrections: Verizon to begin selling iPhone 4 in February"&lt;/li&gt;&lt;li&gt;12:59 "Verizon Wireless to sell iPhone in February"&lt;/li&gt;&lt;li&gt;13:17 "Stronger earnings reports push stocks higher"&lt;/li&gt;&lt;li&gt;13:18 "Most active New York Stock Exchange-traded stocks"&lt;/li&gt;&lt;li&gt;13:18 "[video] Apple's iPhone 4 Comes to Verizon [1.4 min]"&lt;/li&gt;&lt;li&gt;13:19 "iPhone Will Bring 'Value' to Verizon Shareholders: CEO"&lt;/li&gt;&lt;li&gt;13:22 "Apple/Verizon Pt. 2: Should I stay or should I go now?"&lt;/li&gt;&lt;li&gt;13:23 "FACTBOX: AT&amp;amp;T and Verizon wage battle over iPhone"&lt;/li&gt;&lt;li&gt;13:23 "Halftime: Verizon iPhone To Drag Down Verizon Stock 10%?"&lt;/li&gt;&lt;li&gt;13:25 "[$$] iPhone Disappears Over the Verizon"&lt;/li&gt;&lt;li&gt;13:28 "[audio] Time for a tech detox? [1.9 min]"&lt;/li&gt;&lt;li&gt;13:29 "Wireless Struggles Against FCC Over Possible ""Bill Shock"" Rules"&lt;/li&gt;&lt;li&gt;13:34 "Verizon big winner from having iPhone? Not so fast"&lt;/li&gt;&lt;li&gt;13:38 "Verizon President Talks iPhone Strategy"&lt;/li&gt;&lt;li&gt;13:43 "FACTBOX: AT&amp;amp;T and Verizon wage battle over iPhone"&lt;/li&gt;&lt;li&gt;13:43 "US stocks up on strong earnings [at Financial times]"&lt;/li&gt;&lt;li&gt;13:45 "Verizon Gets the iPhone"&lt;/li&gt;&lt;li&gt;13:45 "Verizon to start selling iPhone on Feb. 10"&lt;/li&gt;&lt;li&gt;13:49 "Apple: No LTE iPhone This Year?"&lt;/li&gt;&lt;li&gt;13:53 "AT&amp;amp;T: We're ""Evaluating"" The New iPhone Mobile Hotspot Feature"&lt;/li&gt;&lt;li&gt;13:58 "Apple: Surprise! Street Expresses Delight With VZ iPhone"&lt;/li&gt;&lt;li&gt;14:00 "[$$] Catching the Wave: Euro Rally Due"&lt;/li&gt;&lt;li&gt;14:02 "Did Apple disappoint with no 4G iPhone?"&lt;/li&gt;&lt;li&gt;14:03 "Mobile Payments: What Verizon's iPhone Means for Square"&lt;/li&gt;&lt;li&gt;14:04 "Verizon to Sell Apple IPad That Connects Directly to Its Network"&lt;/li&gt;&lt;li&gt;14:09 "Verizon Wireless ends long wait for iPhone fans"&lt;/li&gt;&lt;li&gt;14:10 "UPDATE 4-Verizon Wireless ends long wait for iPhone fans"&lt;/li&gt;&lt;li&gt;14:13 "Verizon Wireless ends long wait for iPhone fans"&lt;/li&gt;&lt;li&gt;14:25 "[audio] Before the Close from MarketWatch Radio Network [1.0 min]"&lt;/li&gt;&lt;li&gt;14:27 "Verizon Wireless ends long wait for iPhone fans"&lt;/li&gt;&lt;li&gt;14:27 "Verizon Wireless ends long wait for iPhone fans"&lt;/li&gt;&lt;li&gt;14:33 "Market Snapshot: U.S. stocks rise on earnings optimism"&lt;/li&gt;&lt;li&gt;14:33 "Verizon to start selling iPhone on Feb. 10"&lt;/li&gt;&lt;li&gt;14:41 "Analysts Go Out on Limb, Predict Verizon iPhone Will Be Big for Apple"&lt;/li&gt;&lt;li&gt;14:48 "Verizon Says You Can Switch If Santa Brought You a Droid"&lt;/li&gt;&lt;li&gt;14:49 "Verizon Wireless to Start IPhone Sales Next Month, Ending AT&amp;amp;T Exclusivity"&lt;/li&gt;&lt;li&gt;14:50 "Motorola: Opportunity In Lack Of LTE iPhone"&lt;/li&gt;&lt;li&gt;14:50 "Verizon iPhone, WoW: Hot Trends"&lt;/li&gt;&lt;li&gt;15:07 "Is Apple's iPhone a Scrooge?"&lt;/li&gt;&lt;li&gt;15:09 "How does the Verizon iPhone affect Android and AT&amp;amp;T?"&lt;/li&gt;&lt;li&gt;15:09 "More talk of smartphones diving into featurephone ground"&lt;/li&gt;&lt;li&gt;15:09 "Verizon iPhone unlimited data won't last forever"&lt;/li&gt;&lt;li&gt;15:09 "Why the Verizon 'dream phone' matters"&lt;/li&gt;&lt;li&gt;15:22 "[video] digits: Verizon Unveils Its iPhone [8.7 min]"&lt;/li&gt;&lt;li&gt;15:35 "With Verizon, Will Apple Take a Bite of More Enterprise?"&lt;/li&gt;&lt;li&gt;15:38 "5 'Dogs of the Dow' Worth Betting On"&lt;/li&gt;&lt;li&gt;15:38 "[video] iPhone: No wonders for Verizon stock"&lt;/li&gt;&lt;li&gt;15:44 "Why RadioShack Is Still Relevant"&lt;/li&gt;&lt;li&gt;15:50 "Apple: Jefferies Ups Tgt To $450; LTE iPhone In October?"&lt;/li&gt;&lt;li&gt;15:50 "BlackBerry PlayBook vs. Android Tablets"&lt;/li&gt;&lt;li&gt;15:51 "Happy Verizon iPhone Day!"&lt;/li&gt;&lt;li&gt;15:57 "[$$] Overheard"&lt;/li&gt;&lt;li&gt;15:58 "Verizon to start selling iPhone on Feb. 10"&lt;/li&gt;&lt;li&gt;16:12 "Verizon's Daniel Mead Doesn't Expect IPhone Price War: Video"&lt;/li&gt;&lt;li&gt;16:17 "Verizon to start selling iPhone on Feb. 10"&lt;/li&gt;&lt;li&gt;16:18 "Android to Lose From Verizon iPhone?"&lt;/li&gt;&lt;li&gt;16:20 "Investing for Income in 2011 with Stocks and Options Part 3: High-Yield Stocks"&lt;/li&gt;&lt;li&gt;16:24 "Tech Stocks: Techs post modest gains, but AMD sinks"&lt;/li&gt;&lt;li&gt;16:26 "[audio] Closing Bell from MarketWatch Radio Network [1.0 min]"&lt;/li&gt;&lt;li&gt;16:30 "Apple: Reasons For An LTE iPhone This Year"&lt;/li&gt;&lt;li&gt;16:30 "[video] digits: Verizon iPhone: What You Need to Know [4.1 min]"&lt;/li&gt;&lt;li&gt;16:30 "[video] digits:What Verizon iPhone Means for Apple, Google [2.1 min]"&lt;/li&gt;&lt;li&gt;16:31 "US STOCKS-Energy shares lift Wall St in light volume"&lt;/li&gt;&lt;li&gt;16:32 "Don't Buy The Verizon iPhone Yet, Unless You NEED A New Phone ASAP"&lt;/li&gt;&lt;li&gt;16:48 "[video] PM Report: China Stealth Fighter - Threat to U.S.? [10.2 min]"&lt;/li&gt;&lt;li&gt;16:50 "Stronger earnings reports push stocks higher"&lt;/li&gt;&lt;li&gt;17:50 "[video] Options Action"&lt;/li&gt;&lt;li&gt;17:54 "Stronger earnings reports push stocks higher"&lt;/li&gt;&lt;li&gt;17:55 "Factbox: AT&amp;amp;T and Verizon wage battle over iPhone"&lt;/li&gt;&lt;li&gt;18:09 "All Our iPhone Trades, Right Here &amp;amp; Now"&lt;/li&gt;&lt;li&gt;18:09 "Most active New York Stock Exchange-traded stocks"&lt;/li&gt;&lt;li&gt;18:44 "Doherty Sees 'Exodus' of AT&amp;amp;T IPhone Users to Verizon: Video"&lt;/li&gt;&lt;li&gt;19:00 "Lady Gaga Brags, U2 to Rival R.E.M. for 2011 Rock Crown: Preview"&lt;/li&gt;&lt;li&gt;19:40 "'Fast Money' Recap: Commodities Getting Frothy"&lt;/li&gt;&lt;li&gt;20:21 "MiFi Come, MiFi Go: Verizon Wireless To Sell iPads With 3G Connectivity"&lt;/li&gt;&lt;li&gt;22:02 "Tuesday ETF Roundup: UNG Surges on Weather, IYZ Tumbles on Increased Competition"&lt;/li&gt;&lt;li&gt;23:38 "Adding Up Parts at Goldman Sachs"&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h4&gt;AT&amp;amp;T (T): Tuesday January 11th, 2011&lt;/h4&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7yXJBBnsI/AAAAAAAAAFQ/gp6hH9hTQS0/s1600/t-data.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7yXJBBnsI/AAAAAAAAAFQ/gp6hH9hTQS0/s400/t-data.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_aH5d0LSOPPA/TU7yZCsnrCI/AAAAAAAAAFU/b6xIL5bM91E/s1600/t-deltas.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://4.bp.blogspot.com/_aH5d0LSOPPA/TU7yZCsnrCI/AAAAAAAAAFU/b6xIL5bM91E/s400/t-deltas.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7yb9ttxgI/AAAAAAAAAFY/Y0rHUAYQyPc/s1600/t-price.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7yb9ttxgI/AAAAAAAAAFY/Y0rHUAYQyPc/s400/t-price.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;00:01 "IPhone May Cost Verizon Wireless $5 Billion in Subsidies in First Year"&lt;/li&gt;&lt;li&gt;00:15 "ANALYSIS - AT&amp;amp;T faces tough year after losing iPhone exclusive"&lt;/li&gt;&lt;li&gt;02:47 "Telecoms' Dividend Yields"&lt;/li&gt;&lt;li&gt;03:03 "With iPhone Taunts, AT&amp;amp;T-Verizon Rivalry Escalates"&lt;/li&gt;&lt;li&gt;05:00 "[video] Verizon iPhone Data Plan"&lt;/li&gt;&lt;li&gt;05:57 "Cramer's Lightning Round - Viva Las Vegas (1/10/11)"&lt;/li&gt;&lt;li&gt;06:00 "Nielsen: Young People Across The Globe Love Their Cell Phones (But Use Them Differently)"&lt;/li&gt;&lt;li&gt;06:21 "[$$] RadioShack Needs Verizon Wireless"&lt;/li&gt;&lt;li&gt;06:45 "Half of Dow Companies to Post Profit Gains"&lt;/li&gt;&lt;li&gt;06:50 "Ahead of the Bell: Verizon iPhone"&lt;/li&gt;&lt;li&gt;07:15 "GM returning to Super Bowl for first time since '08."&lt;/li&gt;&lt;li&gt;07:27 "10 Things You Need To Know Before The Opening Bell"&lt;/li&gt;&lt;li&gt;07:27 "10 Things You Need To Know Before The Opening Bell"&lt;/li&gt;&lt;li&gt;07:32 "Lowenstein Says Verizon's Network Ready to Handle IPhone: Video"&lt;/li&gt;&lt;li&gt;07:40 "iPhone Raises Stakes of AT&amp;amp;T-Verizon Rivalry"&lt;/li&gt;&lt;li&gt;07:41 "10 Things You Need to Know Before the Opening Bell"&lt;/li&gt;&lt;li&gt;07:45 "Verizon big winner from having iPhone? Not so fast"&lt;/li&gt;&lt;li&gt;07:58 "AT&amp;amp;T and Verizon Trade Taunts Over iPhone"&lt;/li&gt;&lt;li&gt;08:03 "[video] 3 Stocks I Saw on TV"&lt;/li&gt;&lt;li&gt;08:05 "AT&amp;amp;T-Verizon rivalry kicks up a notch"&lt;/li&gt;&lt;li&gt;08:09 "10 Unusual Stocks Attracting Huge Interest This Morning"&lt;/li&gt;&lt;li&gt;08:22 "Will Verizon Demolish AT&amp;amp;T? It Did So Long Ago"&lt;/li&gt;&lt;li&gt;08:24 "The Big Questions About Verizon's IPhone"&lt;/li&gt;&lt;li&gt;08:24 "Verizon big winner from having iPhone? Not so fast"&lt;/li&gt;&lt;li&gt;08:25 "McCourt Says Verizon's Network Can Handle Apple's IPhone: Video"&lt;/li&gt;&lt;li&gt;08:36 "How Important Is Offering the iPhone on Verizon to Apple?"&lt;/li&gt;&lt;li&gt;08:45 "[$$] ETF Play of the Day: XLK"&lt;/li&gt;&lt;li&gt;09:02 "[video] AM Report: Chinese Stealth Jet Takes Flight [8.0 min]"&lt;/li&gt;&lt;li&gt;09:06 "Three Things to Watch For at Verizon iPhoneapalooza"&lt;/li&gt;&lt;li&gt;09:29 "Verizon iPhone Faces Even Bigger Foe"&lt;/li&gt;&lt;li&gt;09:33 "Trujillo Says 'Many Questions' Remain For Verizon IPhone: Video"&lt;/li&gt;&lt;li&gt;09:45 "Verizon IPhone Event Live Blog"&lt;/li&gt;&lt;li&gt;09:45 "[video] News Hub: Verizon iPhone: How will AT&amp;amp;T Respond? [2.7 min]"&lt;/li&gt;&lt;li&gt;09:52 "[video] Verizon's iPhone Buzz"&lt;/li&gt;&lt;li&gt;09:54 "Mobile Broadband Subs Seen Hitting 1 Billion In 2011"&lt;/li&gt;&lt;li&gt;09:58 "[audio] Opening Bell from MarketWatch Radio Network [1.0 min]"&lt;/li&gt;&lt;li&gt;10:03 "Video: Is the Verizon iPhone a Threat to Android?"&lt;/li&gt;&lt;li&gt;10:05 "Verizon iPhone expected to have unlimited data plan"&lt;/li&gt;&lt;li&gt;10:14 "[video] iPhone or Not, Verizon Is A Winner"&lt;/li&gt;&lt;li&gt;10:19 "Verizon iPhone: Live Blog"&lt;/li&gt;&lt;li&gt;10:37 "Verizon: Stay Tuned"&lt;/li&gt;&lt;li&gt;10:43 "Mobile Broadband Subscriptions to Surpass 1 Billion in 2011"&lt;/li&gt;&lt;li&gt;10:47 "LIVE: The Verizon iPhone Event"&lt;/li&gt;&lt;li&gt;10:54 "LIVE FROM NEW YORK: Verizon Gets the iPhone"&lt;/li&gt;&lt;li&gt;10:54 "Verizon iPhone: Will It Fix Verizon's $100 Billion Conundrum?"&lt;/li&gt;&lt;li&gt;11:10 "Verizon to Partner With Apple, Start Selling IPhone Next Month"&lt;/li&gt;&lt;li&gt;11:14 "Verizon and Apple Make It Official"&lt;/li&gt;&lt;li&gt;11:15 "Verizon Wireless to Start IPhone Sales Next Month, Ending AT&amp;amp;T Exclusivity"&lt;/li&gt;&lt;li&gt;11:15 "iPhone Breaks AT&amp;amp;T Marriage Today for Verizon"&lt;/li&gt;&lt;li&gt;11:18 "Verizon to launch iPhone 4 in February"&lt;/li&gt;&lt;li&gt;11:20 "Verizon iPhone: The basics"&lt;/li&gt;&lt;li&gt;11:24 "The Biggest Surprise About the Verizon iPhone: It's a Mobile Hotspot"&lt;/li&gt;&lt;li&gt;11:24 "Verizon to Carry Apple iPhone, Ending Months of Anticipation"&lt;/li&gt;&lt;li&gt;11:30 "Verizon Will Be Selling The iPhone Starting February 10th"&lt;/li&gt;&lt;li&gt;11:38 "UPDATE 2-Verizon Wireless to sell iPhone in February"&lt;/li&gt;&lt;li&gt;11:39 "PHOTOS: Verizon iPhone Has Different (Better?) Antenna Design Than AT&amp;amp;T iPhone"&lt;/li&gt;&lt;li&gt;11:39 "Verizon, AT&amp;amp;T, And Apple: Guess Which One's Stock Isn't Tanking Today"&lt;/li&gt;&lt;li&gt;11:43 "Verizon Wireless to Offer iPhone 4 in February"&lt;/li&gt;&lt;li&gt;11:45 "Verizon iPhone: 'It Begins'"&lt;/li&gt;&lt;li&gt;11:50 "Verizon to begin selling iPhone 4 in February"&lt;/li&gt;&lt;li&gt;11:51 "Stocks Led by Energy, Conglomerates"&lt;/li&gt;&lt;li&gt;11:51 "Verizon to sell Apple iPhone"&lt;/li&gt;&lt;li&gt;11:51 "Verizon: Began Discussions With Apple In 2008; Tim Cook Talks"&lt;/li&gt;&lt;li&gt;11:54 "Verizon Wireless to sell iPhone in February"&lt;/li&gt;&lt;li&gt;12:08 "Verizon iPhone Will Not Work Overseas"&lt;/li&gt;&lt;li&gt;12:13 "UPDATE 3-Verizon Wireless to sell iPhone in February"&lt;/li&gt;&lt;li&gt;12:16 "FACTBOX - AT&amp;amp;T and Verizon wage battle over iPhone"&lt;/li&gt;&lt;li&gt;12:17 "AT&amp;amp;T Preps for Verizon iPhone With New Ads, Price Cuts"&lt;/li&gt;&lt;li&gt;12:19 "Apple's Phil Schiller on Building the Verizon iPhone"&lt;/li&gt;&lt;li&gt;12:20 "AT&amp;amp;T Shrugs Off Verizon iPhone"&lt;/li&gt;&lt;li&gt;12:20 "COMPARISON: Verizon Vs. AT&amp;amp;T iPhone"&lt;/li&gt;&lt;li&gt;12:26 "FACTBOX - AT&amp;amp;T and Verizon wage battle over iPhone"&lt;/li&gt;&lt;li&gt;12:28 "Verizon iPhone: Yep, The Real Target is Google."&lt;/li&gt;&lt;li&gt;12:30 "AT&amp;amp;T: Five Reasons the Sky Won't Fall When iPhone Goes to Verizon"&lt;/li&gt;&lt;li&gt;12:35 "[audio] Midday Update from MarketWatch Radio Network [1.0 min]"&lt;/li&gt;&lt;li&gt;12:37 "FACTBOX - AT&amp;amp;T and Verizon wage battle over iPhone"&lt;/li&gt;&lt;li&gt;12:37 "Verizon Wireless to sell iPhone in February"&lt;/li&gt;&lt;li&gt;12:43 "Verizon to start selling iPhone on Feb. 10"&lt;/li&gt;&lt;li&gt;12:48 "Corrected: Factbox: AT&amp;amp;T and Verizon wage battle over iPhone"&lt;/li&gt;&lt;li&gt;12:59 "Verizon Wireless to sell iPhone in February"&lt;/li&gt;&lt;li&gt;13:05 "Verizon iPhone set for Feb. 10 release"&lt;/li&gt;&lt;li&gt;13:05 "Verizon to start selling iPhone Feb. 3"&lt;/li&gt;&lt;li&gt;13:07 "Moore Says 3 Million AT&amp;amp;T IPhone Users May Go to Verizon: Video"&lt;/li&gt;&lt;li&gt;13:11 "Cook Says Apple-Verizon Cooperation 'Just the Beginning': Video"&lt;/li&gt;&lt;li&gt;13:12 "COMPARISON: Verizon Vs. AT&amp;amp;T iPhone"&lt;/li&gt;&lt;li&gt;13:18 "Most active New York Stock Exchange-traded stocks"&lt;/li&gt;&lt;li&gt;13:18 "Summary Box: Verizon to start selling iPhone"&lt;/li&gt;&lt;li&gt;13:18 "[video] Apple's iPhone 4 Comes to Verizon [1.4 min]"&lt;/li&gt;&lt;li&gt;13:22 "Apple/Verizon Pt. 2: Should I stay or should I go now?"&lt;/li&gt;&lt;li&gt;13:23 "FACTBOX: AT&amp;amp;T and Verizon wage battle over iPhone"&lt;/li&gt;&lt;li&gt;13:23 "Halftime: Verizon iPhone To Drag Down Verizon Stock 10%?"&lt;/li&gt;&lt;li&gt;13:29 "Wireless Struggles Against FCC Over Possible ""Bill Shock"" Rules"&lt;/li&gt;&lt;li&gt;13:34 "Verizon big winner from having iPhone? Not so fast"&lt;/li&gt;&lt;li&gt;13:43 "FACTBOX: AT&amp;amp;T and Verizon wage battle over iPhone"&lt;/li&gt;&lt;li&gt;13:43 "US stocks up on strong earnings [at Financial times]"&lt;/li&gt;&lt;li&gt;13:45 "Verizon to start selling iPhone on Feb. 10"&lt;/li&gt;&lt;li&gt;13:49 "Apple: No LTE iPhone This Year?"&lt;/li&gt;&lt;li&gt;13:53 "AT&amp;amp;T: We're ""Evaluating"" The New iPhone Mobile Hotspot Feature"&lt;/li&gt;&lt;li&gt;13:58 "Apple: Surprise! Street Expresses Delight With VZ iPhone"&lt;/li&gt;&lt;li&gt;14:02 "Did Apple disappoint with no 4G iPhone?"&lt;/li&gt;&lt;li&gt;14:04 "Verizon to Sell Apple IPad That Connects Directly to Its Network"&lt;/li&gt;&lt;li&gt;14:05 "Verizon begins selling iPhone on Feb. 3"&lt;/li&gt;&lt;li&gt;14:05 "Verizon iPhone sales begin Feb. 3"&lt;/li&gt;&lt;li&gt;14:09 "Verizon Wireless ends long wait for iPhone fans"&lt;/li&gt;&lt;li&gt;14:10 "UPDATE 4-Verizon Wireless ends long wait for iPhone fans"&lt;/li&gt;&lt;li&gt;14:13 "Verizon Wireless ends long wait for iPhone fans"&lt;/li&gt;&lt;li&gt;14:25 "[audio] Before the Close from MarketWatch Radio Network [1.0 min]"&lt;/li&gt;&lt;li&gt;14:27 "Verizon Wireless ends long wait for iPhone fans"&lt;/li&gt;&lt;li&gt;14:27 "Verizon Wireless ends long wait for iPhone fans"&lt;/li&gt;&lt;li&gt;14:33 "Market Snapshot: U.S. stocks rise on earnings optimism"&lt;/li&gt;&lt;li&gt;14:33 "Verizon to start selling iPhone on Feb. 10"&lt;/li&gt;&lt;li&gt;14:41 "Analysts Go Out on Limb, Predict Verizon iPhone Will Be Big for Apple"&lt;/li&gt;&lt;li&gt;14:50 "Motorola: Opportunity In Lack Of LTE iPhone"&lt;/li&gt;&lt;li&gt;14:57 "An iPhone For Valentine's Day and iPad 2 Update"&lt;/li&gt;&lt;li&gt;14:58 Verizon&lt;/li&gt;&lt;li&gt;15:05 "Verizon iPhone sales begin Feb. 3"&lt;/li&gt;&lt;li&gt;15:05 "Verizon iPhone set for Feb. 10 release"&lt;/li&gt;&lt;li&gt;15:09 "How does the Verizon iPhone affect Android and AT&amp;amp;T?"&lt;/li&gt;&lt;li&gt;15:09 "More talk of smartphones diving into featurephone ground"&lt;/li&gt;&lt;li&gt;15:09 "Verizon iPhone unlimited data won't last forever"&lt;/li&gt;&lt;li&gt;15:09 "Why the Verizon 'dream phone' matters"&lt;/li&gt;&lt;li&gt;15:38 "5 'Dogs of the Dow' Worth Betting On"&lt;/li&gt;&lt;li&gt;15:38 "[video] iPhone: No wonders for Verizon stock"&lt;/li&gt;&lt;li&gt;15:50 "Apple: Jefferies Ups Tgt To $450; LTE iPhone In October?"&lt;/li&gt;&lt;li&gt;15:51 "Happy Verizon iPhone Day!"&lt;/li&gt;&lt;li&gt;15:58 "Verizon to start selling iPhone on Feb. 10"&lt;/li&gt;&lt;li&gt;16:06 "Verizon iPhone Is Not Mobile Messiah: Analyst"&lt;/li&gt;&lt;li&gt;16:10 "Stocks March Higher As Earnings Season Begins"&lt;/li&gt;&lt;li&gt;16:12 "Verizon's Daniel Mead Doesn't Expect IPhone Price War: Video"&lt;/li&gt;&lt;li&gt;16:17 "Verizon to start selling iPhone on Feb. 10"&lt;/li&gt;&lt;li&gt;16:18 "Android to Lose From Verizon iPhone?"&lt;/li&gt;&lt;li&gt;16:20 "Investing for Income in 2011 with Stocks and Options Part 3: High-Yield Stocks"&lt;/li&gt;&lt;li&gt;16:28 "Daley Has $7.7 Million of JPMorgan Stock to Divest on Way to White House"&lt;/li&gt;&lt;li&gt;16:30 "Apple: Reasons For An LTE iPhone This Year"&lt;/li&gt;&lt;li&gt;16:32 "Don't Buy The Verizon iPhone Yet, Unless You NEED A New Phone ASAP"&lt;/li&gt;&lt;li&gt;16:50 "Stronger earnings reports push stocks higher"&lt;/li&gt;&lt;li&gt;17:04 "Verizon Wireless ends long wait for iPhone fans"&lt;/li&gt;&lt;li&gt;17:08 "Verizon Wireless ends long wait for iPhone fans"&lt;/li&gt;&lt;li&gt;17:22 "Verizon Wireless ends long wait for iPhone fans"&lt;/li&gt;&lt;li&gt;17:22 "Verizon Wireless ends long wait for iPhone fans"&lt;/li&gt;&lt;li&gt;17:41 "California Reclaims 48,000 State-Owned Mobile Phones"&lt;/li&gt;&lt;li&gt;17:43 "[video] News Hub: Verizon iPhone - What's The Actual Cost? [3.0 min]"&lt;/li&gt;&lt;li&gt;17:54 "Stronger earnings reports push stocks higher"&lt;/li&gt;&lt;li&gt;17:55 "Factbox: AT&amp;amp;T and Verizon wage battle over iPhone"&lt;/li&gt;&lt;li&gt;18:09 "All Our iPhone Trades, Right Here &amp;amp; Now"&lt;/li&gt;&lt;li&gt;18:09 "Most active New York Stock Exchange-traded stocks"&lt;/li&gt;&lt;li&gt;18:44 "Doherty Sees 'Exodus' of AT&amp;amp;T IPhone Users to Verizon: Video"&lt;/li&gt;&lt;li&gt;20:21 "MiFi Come, MiFi Go: Verizon Wireless To Sell iPads With 3G Connectivity"&lt;/li&gt;&lt;li&gt;22:02 "Tuesday ETF Roundup: UNG Surges on Weather, IYZ Tumbles on Increased Competition"&lt;/li&gt;&lt;li&gt;23:38 "Adding Up Parts at Goldman Sachs"&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h4&gt;Merck (MRK): Thursday January 13th, 2011&lt;/h4&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7ynlwFqgI/AAAAAAAAAFc/ArpKacF7DnY/s1600/mrk-data.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="161" src="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7ynlwFqgI/AAAAAAAAAFc/ArpKacF7DnY/s400/mrk-data.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7yrJCi-CI/AAAAAAAAAFg/lR8dsOY6gY8/s1600/mrk-deltas.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7yrJCi-CI/AAAAAAAAAFg/lR8dsOY6gY8/s400/mrk-deltas.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7ytFuf7VI/AAAAAAAAAFk/bOvcFKc519I/s1600/mrk-price.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7ytFuf7VI/AAAAAAAAAFk/bOvcFKc519I/s400/mrk-price.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;08:58 "JP Morgan Healthcare: Sanofi-Aventis, UCSF in Research Pact"&lt;/li&gt;&lt;li&gt;09:20 "Merck Statement on Changes to Clinical Studies of Vorapaxar"&lt;/li&gt;&lt;li&gt;09:41 "U.S. stocks open lower as joblessness rises"&lt;/li&gt;&lt;li&gt;09:41 "UPDATE 1-Merck stops giving clot drug in study, shares drop"&lt;/li&gt;&lt;li&gt;09:47 "Merck Drags on Stocks"&lt;/li&gt;&lt;li&gt;09:49 "Merck tumbles on drug study woes"&lt;/li&gt;&lt;li&gt;09:55 "Market Snapshot: U.S. stocks open lower as joblessness rises"&lt;/li&gt;&lt;li&gt;09:58 "Merck Blood-Thinner Study Ends in Stroke Patients, Limiting Possible Sales"&lt;/li&gt;&lt;li&gt;09:58 "Stocks Fall Slightly After Jobless Claims, PPI"&lt;/li&gt;&lt;li&gt;10:56 "Movers &amp;amp; Shakers: Thursday?s biggest gaining and declining stocks"&lt;/li&gt;&lt;li&gt;11:06 "Idaho in Drug Settlement With Warrick, Schering-Plough"&lt;/li&gt;&lt;li&gt;11:10 "Stocks Weighed by Healthcare Sector"&lt;/li&gt;&lt;li&gt;11:19 "[video] Markets Hub: US Stocks Fall As Merck Leads Decline [2.9 min]"&lt;/li&gt;&lt;li&gt;11:28 "FDA seeks less acetaminophen in prescription drugs"&lt;/li&gt;&lt;li&gt;11:34 "Commodities Look 'Very Attractive' Now: Kass"&lt;/li&gt;&lt;li&gt;11:41 "The Cheaper Way to Build the Best Portfolio"&lt;/li&gt;&lt;li&gt;11:43 "Biotech Stocks: Merck leads drug stocks lower"&lt;/li&gt;&lt;li&gt;11:43 "UPDATE 3-Merck clot drug seen unfit for stroke, shares fall"&lt;/li&gt;&lt;li&gt;11:47 "Merck Pipeline Setback: Big Pharma Losers"&lt;/li&gt;&lt;li&gt;11:49 "Merck clot drug seen unfit for stroke, shares fall"&lt;/li&gt;&lt;li&gt;11:53 "US STOCKS-Market flat, pares losses from jobless claims"&lt;/li&gt;&lt;li&gt;11:54 "Markets Going Nowhere After Weak US Data: Here Are The 10 Trades To Watch"&lt;/li&gt;&lt;li&gt;11:56 "Merck Learns There Is No Magic Pill"&lt;/li&gt;&lt;li&gt;11:56 "U.S. stocks bounce off lows; oil turns up"&lt;/li&gt;&lt;li&gt;12:16 "Bond Report: Treasurys rise after Fed purchases"&lt;/li&gt;&lt;li&gt;12:27 "Merck Shares Drop After Anti-Clotting Drug Trials Curtailed"&lt;/li&gt;&lt;li&gt;12:39 "Market Snapshot: U.S. stocks mixed as joblessness rises"&lt;/li&gt;&lt;li&gt;12:45 "Market flat, pares losses from jobless claims"&lt;/li&gt;&lt;li&gt;12:47 "A Painful Day for Merck"&lt;/li&gt;&lt;li&gt;13:05 "Merck tumbles on drug study update"&lt;/li&gt;&lt;li&gt;13:10 "US STOCKS-Market flat on weak jobless claims, Marathon's rise"&lt;/li&gt;&lt;li&gt;13:12 "Market flat, pares losses from jobless claims"&lt;/li&gt;&lt;li&gt;13:20 "Jobless claims weigh on US equities"&lt;/li&gt;&lt;li&gt;13:24 "Market flat on weak jobless claims, Marathon's rise"&lt;/li&gt;&lt;li&gt;13:29 "Dealpolitik: The Useful Corruption of Shareholder Lawsuits"&lt;/li&gt;&lt;li&gt;13:44 "Does Drug Setback Call Merck's Schering-Plough Purchase Into Question?"&lt;/li&gt;&lt;li&gt;13:47 "UPDATE 4-Merck clot drug seen unfit for stroke, shares fall"&lt;/li&gt;&lt;li&gt;13:48 "Market flat on weak jobless claims, Marathon's rise"&lt;/li&gt;&lt;li&gt;14:27 "Market Snapshot: Merck leads stocks lower"&lt;/li&gt;&lt;li&gt;14:35 "Merck Hurts, Stocks Stagger After Jobless Claims"&lt;/li&gt;&lt;li&gt;14:45 "Drug Study Torpedos Merck Stock"&lt;/li&gt;&lt;li&gt;14:52 "Bond Report: Treasurys lifted by Fed buys; 30-year sale in line"&lt;/li&gt;&lt;li&gt;14:55 "Jan. 13: Unusual Volume Leaders"&lt;/li&gt;&lt;li&gt;15:00 "Credit Suisse: All Isn't Lost for Merck"&lt;/li&gt;&lt;li&gt;15:12 "Merck weighs on Dow, market eyes earnings"&lt;/li&gt;&lt;li&gt;15:23 "This Trendy Pharma Is Making a Stylish Move"&lt;/li&gt;&lt;li&gt;15:36 "The Three Most Profitable Healthcare Trends of 2011"&lt;/li&gt;&lt;li&gt;15:47 "Market Snapshot: Stocks slide into close"&lt;/li&gt;&lt;li&gt;15:55 "Copying biotech medicine attracts more drugmakers"&lt;/li&gt;&lt;li&gt;16:00 "Stocks Slip Up On Awful US Data: Here's What You Need To Know"&lt;/li&gt;&lt;li&gt;16:08 "US STOCKS SNAPSHOT-Wall St dips on Merck, commodities"&lt;/li&gt;&lt;li&gt;16:09 "The Big Culprit In Today's DOW Drop"&lt;/li&gt;&lt;li&gt;16:09 "UPDATE 1-FDA seeks less acetaminophen in prescription drugs"&lt;/li&gt;&lt;li&gt;16:10 "U.S. stocks end lower as joblessness rises"&lt;/li&gt;&lt;li&gt;16:27 "US STOCKS-Merck, materials companies drag Wall St lower"&lt;/li&gt;&lt;li&gt;16:27 "[audio] Closing Bell from MarketWatch Radio Network [1.0 min]"&lt;/li&gt;&lt;li&gt;16:55 "Bond Report: Treasurys rise with jobless claims, auctions? end"&lt;/li&gt;&lt;li&gt;16:56 "[video] PM Report: Economists Turn Upbeat on Recovery [10.6 min]"&lt;/li&gt;&lt;li&gt;16:59 "[$$] Stocks in the Spotlight Thursday"&lt;/li&gt;&lt;li&gt;17:06 "US STOCKS-Merck, materials drag Wall St lower, Intel up late"&lt;/li&gt;&lt;li&gt;17:09 "[video] News Hub: Dow Dragged Down by Merck [1.3 min]"&lt;/li&gt;&lt;li&gt;17:11 "Market Snapshot: Stocks end lower on weak materials, drug shares"&lt;/li&gt;&lt;li&gt;18:29 "The Fed Takes Credit: Dave's Daily"&lt;/li&gt;&lt;li&gt;18:34 "[audio] After the Bell from MarketWatch Radio Network [1.0 min]"&lt;/li&gt;&lt;li&gt;18:39 "Trouble With Thrombin Inhibitor Takes a Toll on Merck Shares"&lt;/li&gt;&lt;li&gt;18:59 "Thursday ETF Roundup: UNG Sinks on Natural Gas Data, BLV Rises on Bond Boost"&lt;/li&gt;&lt;li&gt;20:06 "[$$] Options Traders Target Merck After Drug Study Halted"&lt;/li&gt;&lt;li&gt;20:26 "HK stocks seen lower on weak materials sector"&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h4&gt;Bank of America (BAC): Friday January 14th, 2011&lt;/h4&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_aH5d0LSOPPA/TU7y5jw1hsI/AAAAAAAAAFo/YYu4rnGT0So/s1600/bac-data.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="232" src="http://2.bp.blogspot.com/_aH5d0LSOPPA/TU7y5jw1hsI/AAAAAAAAAFo/YYu4rnGT0So/s400/bac-data.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_aH5d0LSOPPA/TU7y8eKgenI/AAAAAAAAAFs/pwtSs_oAs3Q/s1600/bac-deltas.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://4.bp.blogspot.com/_aH5d0LSOPPA/TU7y8eKgenI/AAAAAAAAAFs/pwtSs_oAs3Q/s400/bac-deltas.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_aH5d0LSOPPA/TU7y_LVH5dI/AAAAAAAAAFw/w9oXHbR9xPQ/s1600/bac-price.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://2.bp.blogspot.com/_aH5d0LSOPPA/TU7y_LVH5dI/AAAAAAAAAFw/w9oXHbR9xPQ/s400/bac-price.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;00:01 "Google, Renault, RBS, Stanford, Kissel, Madoff in Court News"&lt;/li&gt;&lt;li&gt;00:01 "Lehman, Madoff, FGIC, Chemtura, FairPoint, Quigley: Bankruptcy"&lt;/li&gt;&lt;li&gt;00:02 "Geithner starts talks on tax overhaul"&lt;/li&gt;&lt;li&gt;01:07 "Morgan Stanley's Gorman shakes up management"&lt;/li&gt;&lt;li&gt;01:25 "Morgan Stanley's Gorman shakes up management"&lt;/li&gt;&lt;li&gt;02:06 "Banks Are Poised to Pay Dividends After 3-Year Gap"&lt;/li&gt;&lt;li&gt;03:12 "UPDATE 1-India govt to decide on IOC share sale in 3-4 mths"&lt;/li&gt;&lt;li&gt;04:01 "What's Driving Earnings Higher?"&lt;/li&gt;&lt;li&gt;06:00 "5 Bank Stocks Under $5"&lt;/li&gt;&lt;li&gt;06:00 "5 Land Mines in Fourth Quarter Bank Earnings"&lt;/li&gt;&lt;li&gt;06:00 "[video] Hold, Don't Buy Bank Stocks: Fund Manager"&lt;/li&gt;&lt;li&gt;06:59 "Kissel Was Argumentative, Easily Upset, Sister-In-Law Testifies"&lt;/li&gt;&lt;li&gt;07:00 "Filling In FINRA's Gaps"&lt;/li&gt;&lt;li&gt;07:00 "General Electric Earnings: What to Expect"&lt;/li&gt;&lt;li&gt;07:00 "Worst-Performing Financial CEO of 2010"&lt;/li&gt;&lt;li&gt;07:16 "[$$] Earnings Scorecard: Commercial and Investment Banks"&lt;/li&gt;&lt;li&gt;07:18 "JPMorgan profit surges despite mortgage hit"&lt;/li&gt;&lt;li&gt;07:36 "JPMorgan's Fourth Quarter Profit Rises 47% to $4.8 Billion"&lt;/li&gt;&lt;li&gt;07:48 "RPT-UPDATE 4-Morgan Stanley's Gorman shakes up management"&lt;/li&gt;&lt;li&gt;07:59 "J.P. Morgan's Fourth-Quarter Profit Jumps 47%"&lt;/li&gt;&lt;li&gt;08:04 "Floods take major toll on Australian farms, mines"&lt;/li&gt;&lt;li&gt;08:08 "A Stock-Bond Hybrid That Flopped"&lt;/li&gt;&lt;li&gt;08:08 "Big Banks with Big Legal Bills"&lt;/li&gt;&lt;li&gt;08:08 "Home Depot's Fix-It Lady"&lt;/li&gt;&lt;li&gt;08:08 "Why the Private Sector Still Isn't Hiring"&lt;/li&gt;&lt;li&gt;08:12 "Citigroup, Bank of America Active Premarket After JPM Report"&lt;/li&gt;&lt;li&gt;08:19 "What's On: JPMorgan, Intel and Corporate Taxes"&lt;/li&gt;&lt;li&gt;08:24 "UPDATE 4-JPMorgan profit beats, helped by reserve release"&lt;/li&gt;&lt;li&gt;08:26 "Hybrid Debt, Materials Curbs, Tax Cheats: Compliance"&lt;/li&gt;&lt;li&gt;08:26 "JPMorgan profit beats, helped by reserve release"&lt;/li&gt;&lt;li&gt;08:37 "Deals of the Day: Groupon Talks IPO"&lt;/li&gt;&lt;li&gt;08:53 "GLOBAL MARKETS WEEKAHEAD-Reality checks ahead for investors"&lt;/li&gt;&lt;li&gt;08:54 "J.P. Morgan Earnings: Were They As Good as They Looked?"&lt;/li&gt;&lt;li&gt;08:55 "J.P. Morgan Earnings: Still Building Litigation Warchest"&lt;/li&gt;&lt;li&gt;09:01 "[$$] ETF Play of the Day: KBE"&lt;/li&gt;&lt;li&gt;09:10 "Foreclosure Wave Cresting, May Never Break"&lt;/li&gt;&lt;li&gt;09:40 "Earnings Jump At JPMorgan, But Street Stifled By China"&lt;/li&gt;&lt;li&gt;09:42 "U.S. Probes Banks' Mortgage Practices"&lt;/li&gt;&lt;li&gt;09:46 "JPMorgan profit rises 47 percent"&lt;/li&gt;&lt;li&gt;09:49 "J.P. Morgan: Mortgage Fights a 'Long, Ugly Mess'"&lt;/li&gt;&lt;li&gt;09:52 "ManTech International - Upgrades &amp;amp; Downgrades"&lt;/li&gt;&lt;li&gt;09:56 "Lbma Says Merrill Lynch Reclassified as Options Market Maker"&lt;/li&gt;&lt;li&gt;09:57 "UBS Upgrades Financials"&lt;/li&gt;&lt;li&gt;10:04 "American Capital prices share offer at $28 each"&lt;/li&gt;&lt;li&gt;10:09 "Schwab Takes High-Profile Stand Opposing Self-Regulatory Organization"&lt;/li&gt;&lt;li&gt;10:12 "BofA Suffers Online Glitches"&lt;/li&gt;&lt;li&gt;10:16 "JPMorgan: Strong Earnings, Muted Response"&lt;/li&gt;&lt;li&gt;10:23 "Stocks edge up after tepid inflation report"&lt;/li&gt;&lt;li&gt;10:33 "JPM Dividend: Jamie Dimon Balks"&lt;/li&gt;&lt;li&gt;10:34 "Your Index Fund Is Making a Bad Investment"&lt;/li&gt;&lt;li&gt;10:43 "Apple's Magic Share Price: $422"&lt;/li&gt;&lt;li&gt;10:45 "JPMorgan Chase Tops 4Q Estimates. Citigroup Is Up Next."&lt;/li&gt;&lt;li&gt;10:58 "Citigroup: Uncle Sam to Get Out, Completely"&lt;/li&gt;&lt;li&gt;11:01 "[$$] JPMorgan Chase Delivers Insight"&lt;/li&gt;&lt;li&gt;11:05 "Affiliated Managers gets $750M credit line"&lt;/li&gt;&lt;li&gt;11:08 "A Wave of Mergers And Acquisitions for Northeast And Mid-Atlantic Banks: An Exclusive Interview With Stephen M. Moss Of Janney Montgomery Scott LLC"&lt;/li&gt;&lt;li&gt;11:11 "UPDATE 6-JPMorgan profit rises 47 pct, beating estimates"&lt;/li&gt;&lt;li&gt;11:11 "[$$] Big Banks May Boost Dividends in 2011"&lt;/li&gt;&lt;li&gt;11:13 "New Zealand bourse winds up AXE trading venue"&lt;/li&gt;&lt;li&gt;11:20 "Greenspan, Fed Saw Threat of Stagflation After Katrina Devastation in 2005"&lt;/li&gt;&lt;li&gt;11:25 "A Wall Street Mystery: Where Was Goldman's CEO?"&lt;/li&gt;&lt;li&gt;11:29 "Goldman Lost an Extra $5B in Prop Trading: Report"&lt;/li&gt;&lt;li&gt;11:29 "JPMorgan profit rises 47 percent"&lt;/li&gt;&lt;li&gt;11:49 "[video] How JPMorgan's Quarter Affects Bank Stocks"&lt;/li&gt;&lt;li&gt;11:53 "Banks lead stocks higher, led by JPMorgan Chase"&lt;/li&gt;&lt;li&gt;12:03 "JPMorgan Earnings Lift Bank Stocks"&lt;/li&gt;&lt;li&gt;12:03 "John Paulson's Top Picks"&lt;/li&gt;&lt;li&gt;12:04 "CPI Shows Input-Cost Inflation Tempered By Overcapacity"&lt;/li&gt;&lt;li&gt;12:05 "Bank of America website having problems"&lt;/li&gt;&lt;li&gt;12:05 "JPMorgan Chase profit up 47%"&lt;/li&gt;&lt;li&gt;12:08 "Earnings schedule for week of 1/17/2011"&lt;/li&gt;&lt;li&gt;12:10 "CNNMoney stock market report -- Jan. 14, 2011"&lt;/li&gt;&lt;li&gt;12:26 "Banks lead stocks higher, led by JPMorgan Chase"&lt;/li&gt;&lt;li&gt;12:29 "J.P. Morgan?s profit jumps 47%"&lt;/li&gt;&lt;li&gt;12:36 "[$$] ETFs for the Dividend-Minded Investor"&lt;/li&gt;&lt;li&gt;13:44 "UPDATE 7-JPMorgan beats, sets bullish tone for bank earnings"&lt;/li&gt;&lt;li&gt;13:48 "AIG recap deal closes, focus moves to share sale"&lt;/li&gt;&lt;li&gt;13:48 "JPMorgan sets bullish tone for bank earnings"&lt;/li&gt;&lt;li&gt;13:50 "[video] J.P. Morgan Earnings Rise 47%"&lt;/li&gt;&lt;li&gt;13:52 "New Movie Revisits the Last 24 Hours of Lehman Brothers, or a Very Similar Firm"&lt;/li&gt;&lt;li&gt;13:52 "[$$] J.P. Morgan Gives Dow Modest Boost"&lt;/li&gt;&lt;li&gt;13:54 "Fed?s Tarullo Tells CNBC No Reason to Adjust Asset Purchases"&lt;/li&gt;&lt;li&gt;13:55 "Should You Buy 2010's Best Investments?"&lt;/li&gt;&lt;li&gt;14:00 "Banking On A Better 2011"&lt;/li&gt;&lt;li&gt;14:02 "[video] Rush Card"&lt;/li&gt;&lt;li&gt;14:05 "JPMorgan Credit Swaps Climb as Bank Results Rest on Reserves"&lt;/li&gt;&lt;li&gt;14:06 "How Celebrities Buy And Sell Homes"&lt;/li&gt;&lt;li&gt;14:07 "A female quota at Davos? Really?"&lt;/li&gt;&lt;li&gt;14:26 "JPMorgan Rises On Double Beat"&lt;/li&gt;&lt;li&gt;14:40 "Bank of America, Wells Fargo May Lose Under Plan to Simplify Mortgages"&lt;/li&gt;&lt;li&gt;14:43 "PNC, Capital One Advance After JPMorgan Posts Record Profit"&lt;/li&gt;&lt;li&gt;14:45 "Bank of America online banking down for some users"&lt;/li&gt;&lt;li&gt;14:45 "Banks take stock indexes higher"&lt;/li&gt;&lt;li&gt;14:56 "Financial Stocks: Financial shares higher after J.P. Morgan results"&lt;/li&gt;&lt;li&gt;15:01 "Halftime: Trading Financials Ahead of Earnings Next Week"&lt;/li&gt;&lt;li&gt;15:05 "Bank of America website having problems"&lt;/li&gt;&lt;li&gt;15:10 "Bank of America website suffers outage"&lt;/li&gt;&lt;li&gt;15:16 "AIG recap deal closes, focus moves to share sale"&lt;/li&gt;&lt;li&gt;15:21 "JPMorgan beats, sets bullish tone for bank earnings"&lt;/li&gt;&lt;li&gt;15:27 "PREVIEW-UPDATE 1-Volcker rule tests new US systemic risk council"&lt;/li&gt;&lt;li&gt;15:29 "Bank of America online banking down for some users"&lt;/li&gt;&lt;li&gt;15:37 "Bank of America Downplays Web Problems"&lt;/li&gt;&lt;li&gt;15:55 "Volcker rule tests new US systemic risk council"&lt;/li&gt;&lt;li&gt;15:57 "AIG recap deal closes, focus moves to share sale"&lt;/li&gt;&lt;li&gt;15:57 "Bank of America Website is Down For Some"&lt;/li&gt;&lt;li&gt;15:59 "[$$] Treasurys Fall, Giving Up Earlier Rally; Little Change for Week"&lt;/li&gt;&lt;li&gt;16:10 "Assured Guaranty was only muni bond insurer in 2010"&lt;/li&gt;&lt;li&gt;16:17 "U.S. stocks post seventh week of gains"&lt;/li&gt;&lt;li&gt;16:18 "Great Depression Part Deux Averted But Bubble Machine Still Blows"&lt;/li&gt;&lt;li&gt;16:18 "Volcker rule tests new US systemic risk council"&lt;/li&gt;&lt;li&gt;16:23 "Tax talks get good start, companies say"&lt;/li&gt;&lt;li&gt;16:29 "Banks take stock indexes higher, led by JPMorgan"&lt;/li&gt;&lt;li&gt;16:31 "Dow, S&amp;amp;P 500 Extend Seven-Week Winning Streak"&lt;/li&gt;&lt;li&gt;16:31 "[$$] Interesting Rotation in the Big Banks"&lt;/li&gt;&lt;li&gt;16:36 "Banks lead S&amp;amp;P 500 to seventh week of gains"&lt;/li&gt;&lt;li&gt;16:52 "Stocks Reach New Records"&lt;/li&gt;&lt;li&gt;16:54 "Market Snapshot: U.S. stocks rise to 30-month highs"&lt;/li&gt;&lt;li&gt;16:55 "Trading Radar: Key Earnings, China Headline the Week"&lt;/li&gt;&lt;li&gt;17:41 "JPMorgan beats, sets bullish tone for bank earnings"&lt;/li&gt;&lt;li&gt;18:15 "AIG recapitalization deal closes, share sale looms"&lt;/li&gt;&lt;li&gt;18:32 "Wall St Week Ahead: Investors crave more strong bank results"&lt;/li&gt;&lt;li&gt;18:46 "GLOBAL MARKETS WEEKAHEAD - Reality checks ahead for investors"&lt;/li&gt;&lt;li&gt;18:46 "US STOCKS - Banks lead S&amp;amp;P 500 to seventh week of gains"&lt;/li&gt;&lt;li&gt;18:46 "Volcker rule tests new US systemic risk council"&lt;/li&gt;&lt;li&gt;20:55 "AIG recapitalization deal closes, share sale looms"&lt;/li&gt;&lt;li&gt;20:55 "JPMorgan beats, sets bullish tone for bank earnings"&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h4&gt;JP Morgan (JPM): Friday January 14th, 2011&lt;/h4&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7zJFi1dII/AAAAAAAAAF0/P-bPsT1cAWM/s1600/jpm-data.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="136" src="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7zJFi1dII/AAAAAAAAAF0/P-bPsT1cAWM/s400/jpm-data.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7zKtPaKRI/AAAAAAAAAF4/v3yEzD9qjj8/s1600/jpm-deltas.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7zKtPaKRI/AAAAAAAAAF4/v3yEzD9qjj8/s400/jpm-deltas.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7zL5qKpFI/AAAAAAAAAF8/LFHhY14o33c/s1600/jpm-price.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="271" src="http://1.bp.blogspot.com/_aH5d0LSOPPA/TU7zL5qKpFI/AAAAAAAAAF8/LFHhY14o33c/s400/jpm-price.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;07:29 "JPMorgan Profit Beats on Narrowing Loan Losses"&lt;/li&gt;&lt;li&gt;07:29 "JPMorgan reports jump in quarterly profit"&lt;/li&gt;&lt;li&gt;07:33 "US STOCKS-Futures fall, JPMorgan profit jumps"&lt;/li&gt;&lt;li&gt;07:34 "JPMorgan Net Rises 47% on Lower Credit Costs, Tops Estimates"&lt;/li&gt;&lt;li&gt;07:35 "Futures Lower Despite Earnings Beats"&lt;/li&gt;&lt;li&gt;07:35 "[audio] Before the bell from MarketWatch Radio Network [1.0 min]"&lt;/li&gt;&lt;li&gt;07:36 "JPMorgan's Fourth Quarter Profit Rises 47% to $4.8 Billion"&lt;/li&gt;&lt;li&gt;07:37 "J.P. Morgan posts quarterly profit of $4.8 billion"&lt;/li&gt;&lt;li&gt;07:39 "UPDATE 3-JPMorgan profit beats, helped by reserve release"&lt;/li&gt;&lt;li&gt;07:40 "The Good Leads"&lt;/li&gt;&lt;li&gt;07:41 "JPMorgan Chase's profit jumps 47 percent in 4Q"&lt;/li&gt;&lt;li&gt;07:41 "JPMorgan quarterly profits surge 47%"&lt;/li&gt;&lt;li&gt;07:42 "JPMorgan profit beats expectations"&lt;/li&gt;&lt;li&gt;07:42 "UPDATE 1-TABLE-JPMorgan fourth-quarter results"&lt;/li&gt;&lt;li&gt;07:45 "CNNMoney premarket report -- Jan. 14, 2011"&lt;/li&gt;&lt;li&gt;07:48 "RPT-UPDATE 4-Morgan Stanley's Gorman shakes up management"&lt;/li&gt;&lt;li&gt;07:56 "[$$] J.P. Morgan Profit Jumps 47%"&lt;/li&gt;&lt;li&gt;07:57 "Indications: U.S. stock futures point to soft open"&lt;/li&gt;&lt;li&gt;07:57 "Stock futures off, Dow, S&amp;amp;P seek 7th week of gains"&lt;/li&gt;&lt;li&gt;07:59 "J.P. Morgan's Fourth-Quarter Profit Jumps 47%"&lt;/li&gt;&lt;li&gt;08:26 "JPMorgan profit beats, helped by reserve release"&lt;/li&gt;&lt;li&gt;08:28 "JPMorgan Beats Street, Net Rises 47%"&lt;/li&gt;&lt;li&gt;08:28 "Morning Take-Out"&lt;/li&gt;&lt;li&gt;08:30 "Minyanville's T3 Morning Call: JPM, INTC Earnings Beats Can't Lift Futures"&lt;/li&gt;&lt;li&gt;08:30 "[$$] The Day Ahead: Follow the Leaders"&lt;/li&gt;&lt;li&gt;08:31 "JP Morgan results ahead of forecasts"&lt;/li&gt;&lt;li&gt;08:36 "[$$] It's All Baked In"&lt;/li&gt;&lt;li&gt;08:41 "Deals wrap: JPMorgan surprises"&lt;/li&gt;&lt;li&gt;08:41 "[$$] Futures Stay Weak After U.S. Data"&lt;/li&gt;&lt;li&gt;08:43 "M&amp;amp;T Beats Estimates"&lt;/li&gt;&lt;li&gt;08:44 "US STOCKS-Futures briefly tick lower after CPI, retail sales"&lt;/li&gt;&lt;li&gt;08:45 "Futures Hold Losses After CPI, Retail Sales"&lt;/li&gt;&lt;li&gt;08:46 "[$$] Will Intel Be the Downside Catalyst?"&lt;/li&gt;&lt;li&gt;08:52 "Live coverage of JPMorgan?s earnings call"&lt;/li&gt;&lt;li&gt;08:54 "J.P. Morgan Earnings: Were They As Good as They Looked?"&lt;/li&gt;&lt;li&gt;08:55 "J.P. Morgan Earnings: Still Building Litigation Warchest"&lt;/li&gt;&lt;li&gt;08:56 "JPMorgan Chase's profit jumps 47 percent in 4Q"&lt;/li&gt;&lt;li&gt;08:57 "JPMorgan reports jump in quarterly profit"&lt;/li&gt;&lt;li&gt;09:38 "CNNMoney stock market report -- Jan. 14, 2011"&lt;/li&gt;&lt;li&gt;09:38 "JPMorgan's Biggest Cost? Pay, Not Mortgages"&lt;/li&gt;&lt;li&gt;09:38 "UPDATE 5-JPMorgan profit rises 47 pct, beating estimates"&lt;/li&gt;&lt;li&gt;09:40 "Earnings Jump At JPMorgan, But Street Stifled By China"&lt;/li&gt;&lt;li&gt;09:41 "Financials open higher after J.P. Morgan earnings"&lt;/li&gt;&lt;li&gt;09:41 "Stocks Slip at Open After Mixed Economic News"&lt;/li&gt;&lt;li&gt;09:42 "U.S. Probes Banks' Mortgage Practices"&lt;/li&gt;&lt;li&gt;09:45 "Unique Convert Offering From Dendreon via JPMorgan"&lt;/li&gt;&lt;li&gt;09:46 "JPMorgan profit rises 47 percent"&lt;/li&gt;&lt;li&gt;09:47 "Friday Look Ahead: Intel, JPMorgan Earnings and Consumer Mood"&lt;/li&gt;&lt;li&gt;09:48 "J.P. Morgan?s profit jumps 47%"&lt;/li&gt;&lt;li&gt;09:49 "J.P. Morgan: Mortgage Fights a 'Long, Ugly Mess'"&lt;/li&gt;&lt;li&gt;09:50 "Stock Market Story: Jan. 14"&lt;/li&gt;&lt;li&gt;09:57 "JPMorgan Reports Q4 Results"&lt;/li&gt;&lt;li&gt;09:57 "Markets dip after JPMorgan earns, retail sales"&lt;/li&gt;&lt;li&gt;09:57 "UBS Upgrades Financials"&lt;/li&gt;&lt;li&gt;10:16 "JPMorgan: Strong Earnings, Muted Response"&lt;/li&gt;&lt;li&gt;10:23 "Stocks edge up after tepid inflation report"&lt;/li&gt;&lt;li&gt;10:24 "[$$] JPMorgan Worries Down the Drain"&lt;/li&gt;&lt;li&gt;10:25 "[$$] Mixed Start"&lt;/li&gt;&lt;li&gt;10:28 "US open: Dow trades flat"&lt;/li&gt;&lt;li&gt;10:30 "[$$] A Short-Term Retail Play"&lt;/li&gt;&lt;li&gt;10:31 "China's inflation move weighs on Wall St"&lt;/li&gt;&lt;li&gt;10:33 "JPM Dividend: Jamie Dimon Balks"&lt;/li&gt;&lt;li&gt;10:45 "[$$] Dip-Buyers' Delight"&lt;/li&gt;&lt;li&gt;10:47 "UPDATE 1-JPMorgan Q4 commods risk steady after market rally"&lt;/li&gt;&lt;li&gt;10:54 "Friday's ETF to Watch: Merrill Lynch Regional Bank HOLDR"&lt;/li&gt;&lt;li&gt;10:55 "J.P. Morgan banker pay slips -- to $369,651"&lt;/li&gt;&lt;li&gt;10:57 "Markets flat as JPMorgan results offset Intel"&lt;/li&gt;&lt;li&gt;10:58 "Citigroup: Uncle Sam to Get Out, Completely"&lt;/li&gt;&lt;li&gt;10:58 "Financial Stocks: Financial shares higher after J.P. Morgan results"&lt;/li&gt;&lt;li&gt;11:08 "A Wave of Mergers And Acquisitions for Northeast And Mid-Atlantic Banks: An Exclusive Interview With Stephen M. Moss Of Janney Montgomery Scott LLC"&lt;/li&gt;&lt;li&gt;11:11 "[$$] Big Banks May Boost Dividends in 2011"&lt;/li&gt;&lt;li&gt;11:16 "[$$] J.P. Morgan Bankers Earn Less"&lt;/li&gt;&lt;li&gt;11:20 "Greenspan, Fed Saw Threat of Stagflation After Katrina Devastation in 2005"&lt;/li&gt;&lt;li&gt;11:20 "JPMorgan Paces Bank Gains After Earnings"&lt;/li&gt;&lt;li&gt;11:25 "A Wall Street Mystery: Where Was Goldman's CEO?"&lt;/li&gt;&lt;li&gt;11:26 "Debit fee caps may hurt poorest customers: Dimon"&lt;/li&gt;&lt;li&gt;11:28 "Market Snapshot: Stocks edge up as J.P. Morgan lifts banks"&lt;/li&gt;&lt;li&gt;11:29 "Goldman Lost an Extra $5B in Prop Trading: Report"&lt;/li&gt;&lt;li&gt;11:29 "JPMorgan profit rises 47 percent"&lt;/li&gt;&lt;li&gt;11:29 "News Corp. in Talks to Buy Shine"&lt;/li&gt;&lt;li&gt;11:33 "JPM Earnings"&lt;/li&gt;&lt;li&gt;11:41 "Complacency, Fleeing Insiders Point to Earnings Season Drop"&lt;/li&gt;&lt;li&gt;11:42 "Gold Prices Tank on Moves to Curb Inflation"&lt;/li&gt;&lt;li&gt;11:42 "JPMorgan: Bonus Numbers Look Good But Bankers Are Wary"&lt;/li&gt;&lt;li&gt;11:46 "UPDATE 2-TABLE-JPMorgan fourth-quarter results"&lt;/li&gt;&lt;li&gt;11:49 "[video] How JPMorgan's Quarter Affects Bank Stocks"&lt;/li&gt;&lt;li&gt;11:53 "Banks lead stocks higher, led by JPMorgan Chase"&lt;/li&gt;&lt;li&gt;12:03 "JPMorgan Earnings Lift Bank Stocks"&lt;/li&gt;&lt;li&gt;12:05 "Bank of America website having problems"&lt;/li&gt;&lt;li&gt;12:05 "JPMorgan Chase profit soars 47%"&lt;/li&gt;&lt;li&gt;12:05 "JPMorgan Chase profit up 47%"&lt;/li&gt;&lt;li&gt;12:09 "A Buying Opportunity Before Stress-Test Round 2?"&lt;/li&gt;&lt;li&gt;12:10 "S&amp;amp;P 500 Is Poised to Drop; RBC, JPMorgan Differ on the Timing"&lt;/li&gt;&lt;li&gt;12:11 "[$$] Comeback After Dazzling Comeback"&lt;/li&gt;&lt;li&gt;12:12 "US STOCKS-Market rises as JPMorgan profit outweighs data"&lt;/li&gt;&lt;li&gt;12:26 "Banks lead stocks higher, led by JPMorgan Chase"&lt;/li&gt;&lt;li&gt;12:32 "[audio] Midday Update from MarketWatch Radio Network [1.0 min]"&lt;/li&gt;&lt;li&gt;12:39 "Hey, Jamie Dimon, You Sure About that Acronym?"&lt;/li&gt;&lt;li&gt;12:55 "Stocks hurt by global inflation concerns"&lt;/li&gt;&lt;li&gt;12:56 "China, U.S. Weigh on Europe's Markets"&lt;/li&gt;&lt;li&gt;13:10 "Market Snapshot: Stocks find footing J.P. Morgan lifts banks"&lt;/li&gt;&lt;li&gt;13:11 "US STOCKS-Wall St rises as JPMorgan earnings spur optimism"&lt;/li&gt;&lt;li&gt;13:14 "Spain, Portugal And A Virtuous Circle Of Bailouts"&lt;/li&gt;&lt;li&gt;13:20 "Most active New York Stock Exchange-traded stocks"&lt;/li&gt;&lt;li&gt;13:34 "4 Top Financials for 1Q from Bernstein Research"&lt;/li&gt;&lt;li&gt;13:36 "Bank Of America Website Down, 6 Hours And Counting"&lt;/li&gt;&lt;li&gt;13:43 "UPDATE 3-AIG recap deal closes, focus moves to share sale"&lt;/li&gt;&lt;li&gt;13:44 "JPMorgan Chase's income jumps 47 percent"&lt;/li&gt;&lt;li&gt;13:48 "AIG recap deal closes, focus moves to share sale"&lt;/li&gt;&lt;li&gt;13:48 "JPMorgan sets bullish tone for bank earnings"&lt;/li&gt;&lt;li&gt;13:50 "[video] J.P. Morgan Earnings Rise 47%"&lt;/li&gt;&lt;li&gt;13:52 "New Movie Revisits the Last 24 Hours of Lehman Brothers, or a Very Similar Firm"&lt;/li&gt;&lt;li&gt;13:53 "JPMorgan: JV Approval in China Positions Stock for Upside"&lt;/li&gt;&lt;li&gt;13:54 "Fed?s Tarullo Tells CNBC No Reason to Adjust Asset Purchases"&lt;/li&gt;&lt;li&gt;13:59 "Elisabeth Murdoch's Shine hires bank for review"&lt;/li&gt;&lt;li&gt;14:00 "Banking On A Better 2011"&lt;/li&gt;&lt;li&gt;14:03 "J P MORGAN CHASE &amp;amp; CO Files SEC form 8-K, Regulation FD Disclosure"&lt;/li&gt;&lt;li&gt;14:05 "JPMorgan Credit Swaps Climb as Bank Results Rest on Reserves"&lt;/li&gt;&lt;li&gt;14:06 "How Celebrities Buy And Sell Homes"&lt;/li&gt;&lt;li&gt;14:07 "A female quota at Davos? Really?"&lt;/li&gt;&lt;li&gt;14:10 "M&amp;amp;T Bank Tops Zacks Estimate"&lt;/li&gt;&lt;li&gt;14:17 "Summary Box: JPMorgan Chase's profit jumps in 4Q"&lt;/li&gt;&lt;li&gt;14:17 "What's Behind JP Morgan's $1.12 Shares"&lt;/li&gt;&lt;li&gt;14:20 "Hedge funds appeal ruling on WaMu trust securities"&lt;/li&gt;&lt;li&gt;14:26 "JPMorgan Rises On Double Beat"&lt;/li&gt;&lt;li&gt;14:31 "UK unmatched for bank bonus-bashing"&lt;/li&gt;&lt;li&gt;14:34 "VIX Falls to Three-Week Low After JPMorgan Posts Record Profit"&lt;/li&gt;&lt;li&gt;14:35 "US STOCKS-JPMorgan lifts Wall St, but banks due for pause"&lt;/li&gt;&lt;li&gt;14:40 "Bank of America, Wells Fargo May Lose Under Plan to Simplify Mortgages"&lt;/li&gt;&lt;li&gt;14:43 "PNC, Capital One Advance After JPMorgan Posts Record Profit"&lt;/li&gt;&lt;li&gt;14:45 "Banks take stock indexes higher"&lt;/li&gt;&lt;li&gt;14:50 "Explaining Confirmation"&lt;/li&gt;&lt;li&gt;14:58 "JPMorgan Chase Plans $3.25 Billion Debt Offering"&lt;/li&gt;&lt;li&gt;15:01 "Gold Prices Sink on Rising Inflation Concerns"&lt;/li&gt;&lt;li&gt;15:05 "Bank of America website having problems"&lt;/li&gt;&lt;li&gt;15:16 "AIG recap deal closes, focus moves to share sale"&lt;/li&gt;&lt;li&gt;15:17 "Bond Report: Treasurys fall as relief rally dissipates"&lt;/li&gt;&lt;li&gt;15:21 "JPMorgan beats, sets bullish tone for bank earnings"&lt;/li&gt;&lt;li&gt;15:22 "Market Snapshot: U.S. stocks rise as J.P. Morgan lifts banks"&lt;/li&gt;&lt;li&gt;15:27 "PREVIEW-UPDATE 1-Volcker rule tests new US systemic risk council"&lt;/li&gt;&lt;li&gt;15:39 "[$$] Crude Settles Above $91"&lt;/li&gt;&lt;li&gt;15:40 "Why Citi Won't Repeat JPMorgan's Success"&lt;/li&gt;&lt;li&gt;15:43 "What to Do With JP Morgan Now?"&lt;/li&gt;&lt;li&gt;15:50 "JPMorgan Chase Declares Preferred Stock Dividend"&lt;/li&gt;&lt;li&gt;16:15 "PNC, Capital One: Winners &amp;amp; Losers"&lt;/li&gt;&lt;li&gt;16:17 "U.S. stocks post seventh week of gains"&lt;/li&gt;&lt;li&gt;16:18 "Great Depression Part Deux Averted But Bubble Machine Still Blows"&lt;/li&gt;&lt;li&gt;16:18 "Volcker rule tests new US systemic risk council"&lt;/li&gt;&lt;li&gt;16:25 "[audio] Closing Bell from MarketWatch Radio Network [1.0 min]"&lt;/li&gt;&lt;li&gt;16:29 "Banks take stock indexes higher, led by JPMorgan"&lt;/li&gt;&lt;li&gt;16:30 "MarketWatch?s top 10 stories: Jan. 10-14"&lt;/li&gt;&lt;li&gt;16:31 "HCP, Kimco: REIT Broker Action"&lt;/li&gt;&lt;li&gt;16:31 "New Issue-JP Morgan sells $3.25 bln in 2 parts"&lt;/li&gt;&lt;li&gt;16:31 "US STOCKS-Banks lead S&amp;amp;P 500 to seventh week of gains"&lt;/li&gt;&lt;li&gt;16:31 "[$$] Interesting Rotation in the Big Banks"&lt;/li&gt;&lt;li&gt;16:32 "Coinstar, JPMorgan, Borders are big market movers"&lt;/li&gt;&lt;li&gt;16:48 "Stocks Buck Mixed Data to Head Higher"&lt;/li&gt;&lt;li&gt;16:50 "Buzz on the Street: Tech Stocks Continue to Lead the Way"&lt;/li&gt;&lt;li&gt;16:52 "Stocks Reach New Records"&lt;/li&gt;&lt;li&gt;16:54 "Market Snapshot: U.S. stocks rise to 30-month highs"&lt;/li&gt;&lt;li&gt;16:55 "[video] News Hub: Dow Ends at 2-1/2 Year High [1.5 min]"&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4261954431421179908-9171024246124561190?l=blog.q-media.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.q-media.com/feeds/9171024246124561190/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.q-media.com/2011/02/fun-with-financial-data-or-as-much-fun.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4261954431421179908/posts/default/9171024246124561190'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4261954431421179908/posts/default/9171024246124561190'/><link rel='alternate' type='text/html' href='http://blog.q-media.com/2011/02/fun-with-financial-data-or-as-much-fun.html' title='Fun with Financial Data (or As Much Fun As You Can Have Without Actually Making Any Money)'/><author><name>stuffinq</name><uri>http://www.blogger.com/profile/05111167194178030567</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_aH5d0LSOPPA/TSoM3Xxa4-I/AAAAAAAAADI/rcrzFXUO3eI/S220/robocloud.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_aH5d0LSOPPA/TU7t9ToaMMI/AAAAAAAAAEE/_pbetTkPBYU/s72-c/DowJones-Jan10-14.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4261954431421179908.post-4911205194639998615</id><published>2010-12-13T19:00:00.008Z</published><updated>2011-02-01T11:10:52.180Z</updated><title type='text'>Ceci n'est ce pas une Haskell Monad Tutorial</title><content type='html'>With apologies to the great surrealist painter &lt;a href="http://en.wikipedia.org/wiki/Ren%C3%A9_Magritte"&gt;Magritte&lt;/a&gt;, this really isn't a Haskell Monad Tutorial (HMT), or is it?&lt;br /&gt;&lt;br /&gt;If you are reading this, you are probably aware that the blogosphere is littered with HMTs. The progression usually goes like this:&lt;br /&gt;&lt;br /&gt;A programmer...&lt;br /&gt;&lt;ol&gt;&lt;li&gt;undertakes to learn Haskell,&amp;nbsp;&lt;/li&gt;&lt;li&gt;struggles for some period of time with understanding Monads,&amp;nbsp;&lt;/li&gt;&lt;li&gt;suddenly has that "ah-ha" moment, and&amp;nbsp;&lt;/li&gt;&lt;li&gt;writes a tutorial (convinced that they can finally explain it to the beginner better than anyone else who has gone before could).&lt;/li&gt;&lt;/ol&gt;In my case, I choose to diverge from this pattern (slightly):&lt;br /&gt;&lt;br /&gt;I...&lt;br /&gt;&lt;ol&gt;&lt;li&gt;took it upon myself to learn Haskell&lt;/li&gt;&lt;li&gt;struggled with Monads for some time&lt;/li&gt;&lt;li&gt;kind of slowly and grudgingly eventually "got" monads, but not with any kind of dramatic "eureka!", more like a "oh... right... ok" kind of feeling (which was remarkably anti-climactic considering how much hair I'd pulled out over this), and&amp;nbsp;&lt;/li&gt;&lt;li&gt;am writing not so much a tutorial as a: "Here's what I did to understand it. Hope it helps." &lt;/li&gt;&lt;/ol&gt;I am not going to talk about Monad laws, IO, or Maybe. I am not even going to try to concoct a flowery definition of what it is (like "contanier" or "computational context"). I am just going to give two examples, which I wrote, specificially with a view to clarifying my own understanding (as per the suggestion of Brian Beckman, see video link below).&lt;br /&gt;&lt;br /&gt;If this helps you, great. I wrote this to help me, and it served that purpose. It's called "learn by doing" (practical, rather than theoretical). I therefore must also suggest that you try this yourself, rather than just expect to get it by reading this. I'm willing to concede that there are probably serious problems with these definitions, given that I am not a schooled Haskell programmer, and don't know a lot about the conventions that serious people follow.&lt;br /&gt;&lt;br /&gt;I'd like to start by throwing down a few references which were helpful to me in my understanding:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://channel9.msdn.com/Shows/Going+Deep/Brian-Beckman-Dont-fear-the-Monads"&gt;Brian Beckman: Don't Fear the Monads&lt;/a&gt; (video)&lt;/li&gt;&lt;li&gt;&lt;a href="http://channel9.msdn.com/Blogs/Charles/C9-Lectures-Greg-Meredith-Monadic-Design-Patterns-for-the-Web-Introduction-to-Monads"&gt;Greg Meredith: Intro to Monads&lt;/a&gt; (video)&lt;/li&gt;&lt;li&gt;&lt;a href="http://learnyouahaskell.com/a-fistful-of-monads"&gt;Learn You a Haskell for Great Good&lt;/a&gt; (best "book" for beginners)&lt;/li&gt;&lt;li&gt;&lt;a href="http://james-iry.blogspot.com/search/label/monads"&gt;James Iry's blog: One Div Zero&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;Now, those folks really know what they're talking about. Look to them for a real education. But, if you still want to see my rudimentary examples, here they are...&lt;br /&gt;&lt;br /&gt;The two examples I implemented: the identity monad (the simplest of all!), and the state monad (because I really wanted to understand this for a project that I'm working on). Implementing the identity monad really helped me, because it's just so simple, yet has all the necessary properties of a "real" monad. State was far more difficult, mainly because of the fact that the monad wraps a function, rather than a value (although we know that these are the same thing in Haskell!), but once I cracked this, it really did "all make sense."&lt;br /&gt;&lt;br /&gt;The Identity Monad&lt;br /&gt;&lt;br /&gt;First, we need a type and type constructor (by Haskell convention I will use the same expression for both, which for this example is "Id").&lt;br /&gt;&lt;code&gt;&lt;br /&gt;newtype Id a = Id a deriving Show&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Note: the "deriving Show" bit is there just in case I want to print these to the console. It could have just been:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;newtype Id a = Id a&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Then I'm going to need a function to extract the value from the "Id" thingy:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;runId :: Id a -&amp;gt; a&lt;br /&gt;runId (Id x) = x&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Simple enough, so far?&lt;br /&gt;&lt;br /&gt;Note: this could also have been acheived using Haskell's "record syntax" as follows (saving the trouble of writing the extra function):&lt;br /&gt;&lt;code&gt;&lt;br /&gt;newtype Id a = Id { runId :: a } deriving Show&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Now, the Monad bit. Don't blink, or you might miss it!&lt;br /&gt;&lt;code&gt;&lt;br /&gt;instance Monad Id where&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;return a = Id a&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;m &amp;gt;&amp;gt;= f = f (runId m)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Identity monad... done!&lt;br /&gt;&lt;br /&gt;I'm not going to explain this in any great detail. You've probably read about "return" and "bind (&amp;gt;&amp;gt;=)" elsewhere. In bind: "m" is the monadic value, "f" is a function (a -&amp;gt; mb) and the bit on the right of the equals just says "run f on the value inside of m."&lt;br /&gt;&lt;br /&gt;Some functions that operate on Id thingies:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;idPlusBar :: Id String -&amp;gt; Id String&lt;br /&gt;idPlusBar x = do&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;y &amp;lt;- x&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;z &amp;lt;- Id (y ++ "bar")&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;return z&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;This function operates on Id Strings appends "bar" to the value.&lt;br /&gt;&lt;br /&gt;Note: the function above could be written more simply as:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;idPlusBar x = Id $ (runId x) ++ "bar"&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;but I included it in long form for illustration purposes.&lt;br /&gt;&lt;br /&gt;Here are a couple that operate on Id Ints:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;idPlusTwo :: Id Int -&amp;gt; Id Int&lt;br /&gt;idPlusTwo x = Id $ (runId x) + 2&lt;br /&gt;&lt;br /&gt;idTimesThree :: Id Int -&amp;gt; Id Int&lt;br /&gt;idTimesThree x = Id $ (runId x) * 3&lt;br /&gt;&lt;br /&gt;idComposed :: Id Int -&amp;gt; Id Int&lt;br /&gt;idComposed = idPlusTwo . idTimesThree&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Now, here's a function ("main" in this case, as I can run this from the command line), which uses HUnit (you'll need to "import Test.HUnit") to test some assumptions:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;main = do&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;putStrLn "running tests"&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;-- Id String tests&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let x = Id "foo"&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let test1 = TestCase $ assertEqual "String foo" "foo" $ runId x&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let test2 = TestCase $ assertEqual "String foobar" "foobar" $ runId $ idPlusBar x&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;-- Id Int tests&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let y = Id 3 :: Id Int&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let test3 = TestCase $ assertEqual "Int 3" 3 $ runId y&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let test4 = TestCase $ assertEqual "Int 5" 5 $ runId $ idPlusTwo y&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let test5 = TestCase $ assertEqual "Int 11" 11 $ runId $ idComposed y&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;-- run tests&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let tests = TestList [&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; TestLabel "foo" test1&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;       &lt;/span&gt;,TestLabel "foobar" test2&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;       &lt;/span&gt;,TestLabel "int 3" test3&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;       &lt;/span&gt;,TestLabel "int 5" test4&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;       &lt;/span&gt;,TestLabel "int + *" test5&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;      &lt;/span&gt;]&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;runTestTT tests&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;So far, none of this is too challenging. You may have noticed that there's absolutely no point to any of it, however. That's OK! The idea was just to get used to wrapping stuff up and operating on it. The first version of "idPlusBar" is interesting because it illustrates how things work in "do-notation."&lt;br /&gt;&lt;br /&gt;If you've got this monad loaded up into ghci, you can see this, too:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;*Main&amp;gt; let x = Id "foo"&lt;br /&gt;*Main&amp;gt; x&lt;br /&gt;Id "foo"&lt;br /&gt;*Main&amp;gt; idPlusBar x&lt;br /&gt;Id "foobar"&lt;br /&gt;*Main&amp;gt; let f x = x &amp;gt;&amp;gt;= \a -&amp;gt; Id (a ++ "bar")&lt;br /&gt;*Main&amp;gt; f x&lt;br /&gt;Id "foobar"&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;where "f" doesn't use "do", but rather "bind" directly.&lt;br /&gt;&lt;br /&gt;Now, if you're still with me, we shall leave the tidy, simple world of identity, and enter the mysterious wonderment that is "state."&lt;br /&gt;&lt;br /&gt;The first part of this excercise involved coming up with a super simple case where I needed to have a state parameter of some kind and a bunch of functions that depended on it. I elected to make the State an "Int" and the value the functions operate on a List of Ints. The key is that in order to be sequenced correctly the state would have to be passed from one function to the next. The functions use the state int to transform the lists, and to add complication (and make it easy to prove it's working properly) I will increment the state with each function that gets executed.&lt;br /&gt;&lt;br /&gt;As you probably know, you can do it by "threading the state" and not using monads at all. Let's try that first:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;addS :: Int -&amp;gt; [Int] -&amp;gt; [Int]&lt;br /&gt;addS s xs = map (+ s) xs&lt;br /&gt;&lt;br /&gt;timesSminus1 :: Int -&amp;gt; [Int] -&amp;gt; [Int]&lt;br /&gt;timesSminus1 s xs = map (* (s-1)) xs&lt;br /&gt;&lt;br /&gt;addStimes2 :: Int -&amp;gt; [Int] -&amp;gt; [Int]&lt;br /&gt;addStimes2 s xs = map (+ (s*2)) xs&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;where s::Int is the 'state' and the list is the thing to be transformed&lt;br /&gt;&lt;br /&gt;Now we can sequence these functions, and "manually" increment the state:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;allS :: Int -&amp;gt; [Int] -&amp;gt; [Int]&lt;br /&gt;allS s xs = let as = addS s xs;&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;    &lt;/span&gt;s' = s+1;&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;    &lt;/span&gt;as' = timesSminus1 s' as;&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;    &lt;/span&gt;s'' = s'+1;&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;   &lt;/span&gt;in&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;addStimes2 s'' as'&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;It's already easy to see, even with this simple example, that this has the potential to become quite cumbersome. It's also obvious that the incrementing of the state is going to get awfully repetitive and should be abstracted in some way.&lt;br /&gt;&lt;br /&gt;Enter the state monad...&lt;br /&gt;&lt;code&gt;&lt;br /&gt;type S = Int&lt;br /&gt;&lt;br /&gt;newtype St s a = St { runState :: S -&amp;gt; (S, a) }&lt;br /&gt;&lt;br /&gt;instance Monad (St s) where&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;return value = St $ \state -&amp;gt; (state, value)&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;monadSt &amp;gt;&amp;gt;= func = St $ \state-&amp;gt;let (state', value) = runState monadSt state&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;         &lt;/span&gt;in runState (func value) (state'+1)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Note: this is not a generic state monad, but one which works exclusively on Ints and increments the state with each function execution (in "bind").&lt;br /&gt;&lt;br /&gt;Here are our functions again, using the monad:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;addS' :: [Int] -&amp;gt; St Int [Int]&lt;br /&gt;addS' xs = St $ \state -&amp;gt; (state,map (+ state) xs)&lt;br /&gt;&lt;br /&gt;timesSminus1' :: [Int] -&amp;gt; St Int [Int]&lt;br /&gt;timesSminus1' xs = St $ \state -&amp;gt; (state,map (* (state-1)) xs)&lt;br /&gt;&lt;br /&gt;addStimes2' :: [Int] -&amp;gt; St Int [Int]&lt;br /&gt;addStimes2' xs = St $ \state -&amp;gt; (state,map (+ (state*2)) xs)&lt;br /&gt;&lt;br /&gt;allS' :: [Int] -&amp;gt; St Int [Int]&lt;br /&gt;allS' xs = do&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;    &lt;/span&gt;as &amp;lt;- addS' xs&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;    &lt;/span&gt;as' &amp;lt;- timesSminus1' as&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;    &lt;/span&gt;as'' &amp;lt;- addStimes2' as'&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;    &lt;/span&gt;return as''&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The last function in particular is quite clean compared to its non-monadic cousin.&lt;br /&gt;&lt;br /&gt;Once again, we'll use HUnit to test some assumptions...&lt;br /&gt;&lt;code&gt;&lt;br /&gt;main = do&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;putStrLn "running tests"&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;-- set up some values to use&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let xs = [1,2,3]&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let state1 = 3&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let state2 = 4&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let state3 = 5&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;-- threading state tests&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let test1 = TestCase $ assertEqual "increment by s" [4,5,6] $ addS state1 xs&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let test2 = TestCase $ assertEqual "times (s-1)" [3,6,9] $ timesSminus1 state2 xs&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let test3 = TestCase $ assertEqual "increment by s*2" [11,12,13] $ addStimes2 state3 xs&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;-- put it together in allS&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let test4 = TestCase $ assertEqual "the lot" [22,25,28] $ allS state1 xs&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;-- state monad tests&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let test1' = TestCase $ assertEqual "m increment by s" [4,5,6] $ snd $ (runState $ addS' xs) state1&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let test2' = TestCase $ assertEqual "m times (s-1)" [3,6,9] $ snd $ (runState $ timesSminus1' xs) state2&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let test3' = TestCase $ assertEqual "m increment by s*2" [11,12,13] $ snd $ (runState $ addStimes2' xs) state3&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;-- monadic state passing in allS'&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let test4' = TestCase $ assertEqual "m the lot" [22,25,28] $ snd $ (runState $ allS' xs) state1&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;-- run tests&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;let tests = TestList [&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt; TestLabel "test1" test1&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;       &lt;/span&gt;,TestLabel "test2" test2&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;       &lt;/span&gt;,TestLabel "test3" test3&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;       &lt;/span&gt;,TestLabel "test4" test4&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;       &lt;/span&gt;,TestLabel "m test1'" test1'&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;       &lt;/span&gt;,TestLabel "m test2'" test2'&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;       &lt;/span&gt;,TestLabel "m test3'" test3'&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;       &lt;/span&gt;,TestLabel "m test4'" test4'&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;      &lt;/span&gt; ]&lt;br /&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt; &lt;/span&gt;runTestTT tests&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The real eye-opening thing here is that allS' doesn't take a state parameter at all. That's because allS' is returning a state monad (a function from state to state,value). So, we pass the state to the function (or value) which is returned by this function. This is the trick... and it is a little bit like magic!&lt;br /&gt;&lt;br /&gt;So,&lt;br /&gt;&lt;br /&gt;allS' takes the list ("xs") and returns the monad. runState extracts the function from the monad (\s -&amp;gt; (s,a)), and we pass the initial state to that. That in turn returns the state,value pair, so we take the "snd" value of the tuple.&lt;br /&gt;&lt;br /&gt;This is it again:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;snd $ (runState $ allS' xs) state1&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Nice, huh?&lt;br /&gt;&lt;br /&gt;So, our function doesn't have to mess around with state at all. It's all handled in the monad's bind function, including the incrementing of the state's value with each function call.&lt;br /&gt;&lt;br /&gt;Once again, I would just add that the most striking thing in all this is its simplicity (a trait I've come to expect from Haskell). It seems that monads weren't that complicated after all.&lt;br /&gt;&lt;br /&gt;Now, what are these Monad Transformer things?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4261954431421179908-4911205194639998615?l=blog.q-media.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.q-media.com/feeds/4911205194639998615/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.q-media.com/2010/12/ceci-nest-ce-pas-une-haskell-monad.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4261954431421179908/posts/default/4911205194639998615'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4261954431421179908/posts/default/4911205194639998615'/><link rel='alternate' type='text/html' href='http://blog.q-media.com/2010/12/ceci-nest-ce-pas-une-haskell-monad.html' title='Ceci n&apos;est ce pas une Haskell Monad Tutorial'/><author><name>stuffinq</name><uri>http://www.blogger.com/profile/05111167194178030567</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_aH5d0LSOPPA/TSoM3Xxa4-I/AAAAAAAAADI/rcrzFXUO3eI/S220/robocloud.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4261954431421179908.post-8851656355454631636</id><published>2010-09-09T21:48:00.002+01:00</published><updated>2010-09-09T21:50:01.966+01:00</updated><title type='text'>Automated Unit Testing with ScalaCheck</title><content type='html'>It just sounds like a great idea, doesn't it? &lt;i&gt;Automated&lt;/i&gt; testing? Fantastic! I can see the tagline now: Say Goodbye to Technical Debt Forever!&lt;br /&gt;&lt;br /&gt;Before I begin, let's establish what we're talking about: What do we really mean when we say automated testing? In this case, what I &lt;i&gt;do not&lt;/i&gt; mean is "test automation". I'm not talking about &lt;i&gt;running&lt;/i&gt; tests automatically, by, say, using Continuous Integration. I'm talking about test &lt;i&gt;generation&lt;/i&gt;. Test generation means, if you've got a function that operates on a particular input, you specify a "property" (or several properties) about that function, and then "generators" will give you variations on the input data to test all the edge cases, etc., and tell you whether the property holds. You can use default generators for known datatypes, or define your own.&lt;br /&gt;&lt;br /&gt;I've been interested in the idea of automated testing for some time, having come across &lt;a href="http://en.wikipedia.org/wiki/QuickCheck"&gt;QuickCheck&lt;/a&gt; (a Haskell library). Please see this quite interesting interview with &lt;a href="http://www.infoq.com/interviews/john-hughes-fp"&gt;John Hughes&lt;/a&gt; (one of QuickCheck's creators) on Functional Programming.&lt;br /&gt;&lt;br /&gt;I don't know Haskell very well, and have done more with Scala to date, so I thought I'd have a look at &lt;a href="http://code.google.com/p/scalacheck/"&gt;ScalaCheck&lt;/a&gt; to satisfy my curiosity. The following represents a quick walk through of the basics of ScalaCheck.&lt;br /&gt;&lt;br /&gt;Please note: I'm using Scala 2.7.7 and ScalaCheck 1.6, and I'll do all this via the interpreter.&lt;br /&gt;&lt;br /&gt;So, on the command line...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala -cp scalacheck_2.7.7-1.6.jar&lt;br /&gt;scala&amp;gt; import org.scalacheck.Prop&lt;br /&gt;&lt;br /&gt;scala&amp;gt; Prop.forAll( (a:Int, b:Int) =&amp;gt; a+b == b+a ).check &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;+ OK, passed 100 tests.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Nice. I just generated a hundred tests. This TDD business is going to be a breeze! But what was really going on here?&lt;br /&gt;&lt;br /&gt;"forAll" is a method of the Prop ("property") object that returns a Prop instance. The parameter in this case is a function which is implicitly converted into a Prop. That Prop's "check" method is called and the result of the property check is displayed.&lt;br /&gt;&lt;br /&gt;What was the property under test? For any given two Ints, if they are added together, the result of that addition will be equal regardless of the order in which they are added.&lt;br /&gt;&lt;br /&gt;Admittedly, not very interesting, but nonetheless, this example illustrates a number of things, not least of which is that ScalaCheck generated, by way of its default Int generator (or Gen), 100 tests which all passed.&lt;br /&gt;&lt;br /&gt;Here's a longer way to write the same thing which may make some of this clear:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; val f = (a:Int, b:Int) =&amp;gt; a+b == b+a&lt;br /&gt;f: (Int, Int) =&amp;gt; Boolean = &lt;function&gt;&lt;/function&gt;&lt;br /&gt;&lt;br /&gt;scala&amp;gt; val p = Prop.forAll(f)&lt;br /&gt;p: org.scalacheck.Prop = Prop&lt;br /&gt;&lt;br /&gt;scala&amp;gt; p.check&lt;br /&gt;+ OK, passed 100 tests.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Great.&lt;br /&gt;&lt;br /&gt;And you can easily combine multiple properties. Let's say you have 3 Props (p1,p2,p3) the first two of which are correct and the last is not:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; (p1 &amp;amp;&amp;amp; p2).check&lt;br /&gt;+ OK, passed 100 tests.&lt;br /&gt;&lt;br /&gt;scala&amp;gt; (p1 &amp;amp;&amp;amp; p3).check&lt;br /&gt;! Falsified after 1 passed tests.&lt;br /&gt;&amp;gt; ARG_0: T(8,true)&lt;br /&gt;&lt;br /&gt;scala&amp;gt; (p1 || p3).check&lt;br /&gt;+ OK, passed 100 tests.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, if you're like me, and you really want to know what's going on, note that it is possible to "collect" the data that is generated for the tests.&lt;br /&gt;&lt;br /&gt;For example...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; val p = Prop.forAll((a:Int,b:Int) =&amp;gt; Prop.collect(a,b) { a+b==b+a })&lt;br /&gt;p: org.scalacheck.Prop = Prop&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is essentially the same thing, but notice the insertion of the call to "Prop.collect".&lt;br /&gt;&lt;br /&gt;Now when we check it, we get this...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; p.check&lt;br /&gt;+ OK, passed 100 tests.&lt;br /&gt;&amp;gt; Collected test data:&lt;br /&gt;4% (0,0)&lt;br /&gt;1% (-22,38)&lt;br /&gt;1% (4,13)&lt;br /&gt;1% (50,30)&lt;br /&gt;1% (-1,44)&lt;br /&gt;1% (4,28)&lt;br /&gt;1% (12,-16)&lt;br /&gt;1% (-1,-16)&lt;br /&gt;1% (-11,2147483647)&lt;br /&gt;1% (72,-17)&lt;br /&gt;1% (8,-1)&lt;br /&gt;1% (-3,-77)&lt;br /&gt;1% (-46,-19)&lt;br /&gt;1% (-23,-1)&lt;br /&gt;1% (-26,-22)&lt;br /&gt;1% (-21,46)&lt;br /&gt;1% (-48,48)&lt;br /&gt;1% (1,-13)&lt;br /&gt;1% (18,1)&lt;br /&gt;1% (19,-1)&lt;br /&gt;1% (34,-3)&lt;br /&gt;1% (-1,-29)&lt;br /&gt;1% (-58,-63)&lt;br /&gt;1% (1,15)&lt;br /&gt;1% (4,11)&lt;br /&gt;1% (17,-22)&lt;br /&gt;1% (-8,-8)&lt;br /&gt;1% (3,-15)&lt;br /&gt;1% (-9,-36)&lt;br /&gt;1% (-92,78)&lt;br /&gt;1% (28,-42)&lt;br /&gt;1% (-18,0)&lt;br /&gt;1% (3,1)&lt;br /&gt;1% (4,2147483647)&lt;br /&gt;1% (-2147483648,10)&lt;br /&gt;1% (37,-2147483648)&lt;br /&gt;1% (8,-2147483648)&lt;br /&gt;1% (21,0)&lt;br /&gt;1% (1,39)&lt;br /&gt;1% (17,88)&lt;br /&gt;1% (0,-48)&lt;br /&gt;1% (38,-44)&lt;br /&gt;1% (0,6)&lt;br /&gt;1% (2147483647,29)&lt;br /&gt;1% (-1,-24)&lt;br /&gt;1% (-28,11)&lt;br /&gt;1% (-4,46)&lt;br /&gt;1% (28,-63)&lt;br /&gt;1% (35,8)&lt;br /&gt;1% (9,12)&lt;br /&gt;1% (18,29)&lt;br /&gt;1% (8,-18)&lt;br /&gt;1% (-3,-1)&lt;br /&gt;1% (0,14)&lt;br /&gt;1% (47,1)&lt;br /&gt;1% (4,3)&lt;br /&gt;1% (-84,-7)&lt;br /&gt;1% (13,31)&lt;br /&gt;1% (-60,29)&lt;br /&gt;1% (-41,-43)&lt;br /&gt;1% (-75,67)&lt;br /&gt;1% (-3,2)&lt;br /&gt;1% (-73,-57)&lt;br /&gt;1% (-10,-13)&lt;br /&gt;1% (15,-3)&lt;br /&gt;1% (2147483647,-25)&lt;br /&gt;1% (8,0)&lt;br /&gt;1% (-79,76)&lt;br /&gt;1% (48,3)&lt;br /&gt;1% (-5,9)&lt;br /&gt;1% (4,0)&lt;br /&gt;1% (-2147483648,8)&lt;br /&gt;1% (43,-25)&lt;br /&gt;1% (-46,54)&lt;br /&gt;1% (-9,-2147483648)&lt;br /&gt;1% (52,33)&lt;br /&gt;1% (-10,4)&lt;br /&gt;1% (36,-20)&lt;br /&gt;1% (1,0)&lt;br /&gt;1% (30,1)&lt;br /&gt;1% (-2147483648,22)&lt;br /&gt;1% (-27,-12)&lt;br /&gt;1% (0,1)&lt;br /&gt;1% (-58,2147483647)&lt;br /&gt;1% (38,-27)&lt;br /&gt;1% (-38,19)&lt;br /&gt;1% (23,38)&lt;br /&gt;1% (11,-15)&lt;br /&gt;1% (-2147483648,17)&lt;br /&gt;1% (-17,51)&lt;br /&gt;1% (-36,-59)&lt;br /&gt;1% (1,14)&lt;br /&gt;1% (-3,-11)&lt;br /&gt;1% (-31,29)&lt;br /&gt;1% (-60,-44)&lt;br /&gt;1% (2147483647,1)&lt;br /&gt;1% (4,16)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, we can see what's at work behind ScalaCheck's default generator (Gen) for "Int". A lot of random numbers there. Edge cases, positive and negative, zero, etc. Thanks, ScalaCheck! You just saved me a lot of test writing!&lt;br /&gt;&lt;br /&gt;Let's look at a couple of things you can do with "Gen".&lt;br /&gt;&lt;br /&gt;First, there's a "choose" method to choose amongst options. Say, we only wanted to check our property with Ints between 1 and 100.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; Prop.forAll(Gen.choose(1,100),Gen.choose(1,100))((a:Int,b:Int) =&amp;gt; Prop.collect(a,b) { a+b==b+a }).check&lt;br /&gt;+ OK, passed 100 tests.&lt;br /&gt;&amp;gt; Collected test data:&lt;br /&gt;1% (32,48)&lt;br /&gt;1% (80,30)&lt;br /&gt;1% (26,31)&lt;br /&gt;1% (15,91)&lt;br /&gt;1% (39,3)&lt;br /&gt;1% (12,68)&lt;br /&gt;1% (93,71)&lt;br /&gt;1% (8,22)&lt;br /&gt;1% (44,58)&lt;br /&gt;1% (4,73)&lt;br /&gt;1% (76,96)&lt;br /&gt;1% (30,98)&lt;br /&gt;1% (55,100)&lt;br /&gt;1% (76,28)&lt;br /&gt;1% (93,13)&lt;br /&gt;1% (46,47)&lt;br /&gt;1% (78,56)&lt;br /&gt;1% (77,30)&lt;br /&gt;1% (69,17)&lt;br /&gt;1% (29,81)&lt;br /&gt;1% (11,39)&lt;br /&gt;1% (40,3)&lt;br /&gt;1% (17,56)&lt;br /&gt;1% (2,81)&lt;br /&gt;1% (31,2)&lt;br /&gt;1% (42,7)&lt;br /&gt;1% (47,22)&lt;br /&gt;1% (48,17)&lt;br /&gt;1% (79,33)&lt;br /&gt;1% (48,40)&lt;br /&gt;1% (52,83)&lt;br /&gt;1% (95,50)&lt;br /&gt;1% (63,30)&lt;br /&gt;1% (21,96)&lt;br /&gt;1% (29,30)&lt;br /&gt;1% (72,33)&lt;br /&gt;1% (70,56)&lt;br /&gt;1% (24,79)&lt;br /&gt;1% (68,16)&lt;br /&gt;1% (89,7)&lt;br /&gt;1% (82,21)&lt;br /&gt;1% (13,35)&lt;br /&gt;1% (73,88)&lt;br /&gt;1% (99,25)&lt;br /&gt;1% (79,8)&lt;br /&gt;1% (52,84)&lt;br /&gt;1% (92,32)&lt;br /&gt;1% (90,77)&lt;br /&gt;1% (88,60)&lt;br /&gt;1% (51,4)&lt;br /&gt;1% (32,93)&lt;br /&gt;1% (62,90)&lt;br /&gt;1% (9,14)&lt;br /&gt;1% (40,36)&lt;br /&gt;1% (78,80)&lt;br /&gt;1% (84,5)&lt;br /&gt;1% (31,63)&lt;br /&gt;1% (7,14)&lt;br /&gt;1% (48,88)&lt;br /&gt;1% (92,21)&lt;br /&gt;1% (31,52)&lt;br /&gt;1% (58,2)&lt;br /&gt;1% (69,82)&lt;br /&gt;1% (84,7)&lt;br /&gt;1% (19,69)&lt;br /&gt;1% (48,94)&lt;br /&gt;1% (50,16)&lt;br /&gt;1% (59,52)&lt;br /&gt;1% (63,63)&lt;br /&gt;1% (55,40)&lt;br /&gt;1% (50,39)&lt;br /&gt;1% (30,26)&lt;br /&gt;1% (15,61)&lt;br /&gt;1% (85,30)&lt;br /&gt;1% (93,90)&lt;br /&gt;1% (19,49)&lt;br /&gt;1% (5,61)&lt;br /&gt;1% (28,56)&lt;br /&gt;1% (41,60)&lt;br /&gt;1% (88,82)&lt;br /&gt;1% (40,62)&lt;br /&gt;1% (96,48)&lt;br /&gt;1% (85,96)&lt;br /&gt;1% (88,7)&lt;br /&gt;1% (1,91)&lt;br /&gt;1% (14,82)&lt;br /&gt;1% (1,56)&lt;br /&gt;1% (24,98)&lt;br /&gt;1% (86,41)&lt;br /&gt;1% (77,65)&lt;br /&gt;1% (4,38)&lt;br /&gt;1% (78,77)&lt;br /&gt;1% (4,62)&lt;br /&gt;1% (84,36)&lt;br /&gt;1% (90,33)&lt;br /&gt;1% (12,67)&lt;br /&gt;1% (45,45)&lt;br /&gt;1% (54,75)&lt;br /&gt;1% (38,94)&lt;br /&gt;1% (14,91)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Or, we can see that the range is inclusive.&lt;br /&gt;&lt;br /&gt;Or, we can define a Gen which is based on conditional statements using "suchThat".&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;val smallOdds = Gen.choose(1,100) suchThat (_ % 2 == 1)&lt;br /&gt;smallOdds: org.scalacheck.Gen[Int] = Gen()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And use that for both our Ints:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; Prop.forAll(smallOdds,smallOdds)((a:Int,b:Int) =&amp;gt; Prop.collect(a,b) { a+b==b+a }).check&lt;br /&gt;+ OK, passed 100 tests.&lt;br /&gt;&amp;gt; Collected test data:&lt;br /&gt;2% (19,75)&lt;br /&gt;2% (53,55)&lt;br /&gt;1% (35,47)&lt;br /&gt;1% (15,75)&lt;br /&gt;1% (13,41)&lt;br /&gt;1% (51,99)&lt;br /&gt;1% (39,23)&lt;br /&gt;1% (97,35)&lt;br /&gt;1% (47,99)&lt;br /&gt;1% (79,87)&lt;br /&gt;1% (95,55)&lt;br /&gt;1% (45,49)&lt;br /&gt;1% (3,61)&lt;br /&gt;1% (73,93)&lt;br /&gt;1% (13,5)&lt;br /&gt;1% (45,45)&lt;br /&gt;1% (31,51)&lt;br /&gt;1% (61,9)&lt;br /&gt;1% (55,55)&lt;br /&gt;1% (25,47)&lt;br /&gt;1% (99,21)&lt;br /&gt;1% (65,29)&lt;br /&gt;1% (47,49)&lt;br /&gt;1% (47,89)&lt;br /&gt;1% (95,39)&lt;br /&gt;1% (43,73)&lt;br /&gt;1% (39,49)&lt;br /&gt;1% (41,17)&lt;br /&gt;1% (89,25)&lt;br /&gt;1% (25,39)&lt;br /&gt;1% (37,27)&lt;br /&gt;1% (17,49)&lt;br /&gt;1% (77,37)&lt;br /&gt;1% (11,9)&lt;br /&gt;1% (15,13)&lt;br /&gt;1% (37,47)&lt;br /&gt;1% (93,77)&lt;br /&gt;1% (5,75)&lt;br /&gt;1% (19,87)&lt;br /&gt;1% (35,39)&lt;br /&gt;1% (3,21)&lt;br /&gt;1% (65,53)&lt;br /&gt;1% (73,83)&lt;br /&gt;1% (39,63)&lt;br /&gt;1% (31,53)&lt;br /&gt;1% (69,31)&lt;br /&gt;1% (99,65)&lt;br /&gt;1% (9,97)&lt;br /&gt;1% (55,57)&lt;br /&gt;1% (83,71)&lt;br /&gt;1% (41,35)&lt;br /&gt;1% (59,69)&lt;br /&gt;1% (3,55)&lt;br /&gt;1% (85,9)&lt;br /&gt;1% (87,71)&lt;br /&gt;1% (33,33)&lt;br /&gt;1% (9,83)&lt;br /&gt;1% (59,55)&lt;br /&gt;1% (19,51)&lt;br /&gt;1% (49,45)&lt;br /&gt;1% (95,61)&lt;br /&gt;1% (45,21)&lt;br /&gt;1% (57,61)&lt;br /&gt;1% (87,89)&lt;br /&gt;1% (35,71)&lt;br /&gt;1% (85,89)&lt;br /&gt;1% (61,91)&lt;br /&gt;1% (47,5)&lt;br /&gt;1% (3,69)&lt;br /&gt;1% (3,17)&lt;br /&gt;1% (39,85)&lt;br /&gt;1% (95,89)&lt;br /&gt;1% (87,11)&lt;br /&gt;1% (85,49)&lt;br /&gt;1% (55,79)&lt;br /&gt;1% (7,97)&lt;br /&gt;1% (93,29)&lt;br /&gt;1% (37,61)&lt;br /&gt;1% (51,19)&lt;br /&gt;1% (67,53)&lt;br /&gt;1% (71,9)&lt;br /&gt;1% (57,11)&lt;br /&gt;1% (41,85)&lt;br /&gt;1% (31,27)&lt;br /&gt;1% (31,57)&lt;br /&gt;1% (87,49)&lt;br /&gt;1% (69,53)&lt;br /&gt;1% (59,77)&lt;br /&gt;1% (23,87)&lt;br /&gt;1% (29,21)&lt;br /&gt;1% (11,59)&lt;br /&gt;1% (69,69)&lt;br /&gt;1% (77,79)&lt;br /&gt;1% (85,65)&lt;br /&gt;1% (31,31)&lt;br /&gt;1% (97,15)&lt;br /&gt;1% (11,15)&lt;br /&gt;1% (75,85)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Let's say you wanted to generate data for an arbitrary case class. ScalaCheck comes with an "Arbitrary" class to do just that:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; case class T(a:Int, b:Boolean)&lt;br /&gt;defined class T&lt;br /&gt;&lt;br /&gt;scala&amp;gt;import org.scalacheck.Arbitrary&lt;br /&gt;import org.scalacheck.Arbitrary&lt;br /&gt;&lt;br /&gt;scala&amp;gt; val genT = for { a &amp;lt;- Gen.choose(1,10); b &amp;lt;- Arbitrary.arbitrary[Boolean] } yield T(a,b)&lt;br /&gt;genT: org.scalacheck.Gen[T] = Gen()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, our class should be comprised of an Int value 1 to 10 (inclusive) and a boolean.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; Prop.forAll(genT)((t:T) =&amp;gt; Prop.collect(t) { t.a &amp;gt;= 1 &amp;amp;&amp;amp; t.a &amp;lt;= 10 &amp;amp;&amp;amp; (t.b == true || t.b == false) }).check&lt;br /&gt;+ OK, passed 100 tests.&lt;br /&gt;&amp;gt; Collected test data:&lt;br /&gt;9% T(3,true)&lt;br /&gt;8% T(10,true)&lt;br /&gt;7% T(6,false)&lt;br /&gt;7% T(2,true)&lt;br /&gt;6% T(4,false)&lt;br /&gt;6% T(7,false)&lt;br /&gt;6% T(2,false)&lt;br /&gt;6% T(1,true)&lt;br /&gt;6% T(4,true)&lt;br /&gt;5% T(5,false)&lt;br /&gt;5% T(8,true)&lt;br /&gt;5% T(8,false)&lt;br /&gt;5% T(3,false)&lt;br /&gt;4% T(5,true)&lt;br /&gt;4% T(7,true)&lt;br /&gt;3% T(10,false)&lt;br /&gt;3% T(1,false)&lt;br /&gt;3% T(9,false)&lt;br /&gt;1% T(9,true)&lt;br /&gt;1% T(6,true)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Lovely.&lt;br /&gt;&lt;br /&gt;Another interesting feature is the ability to classify values that are chosen by the generator. For example...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; Prop.forAll(genT)(t =&amp;gt; Prop.classify({ t.b==true }, "b is true")(p)).check&lt;br /&gt;+ OK, passed 100 tests.&lt;br /&gt;&amp;gt; Collected test data:&lt;br /&gt;60% b is true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note: "p" is a property which holds in this case.&lt;br /&gt;&lt;br /&gt;Labels:&lt;br /&gt;&lt;br /&gt;Let's say you have three properties you are testing, and two of the three are correct...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; val p1 = Prop.forAll(genT)((t:T) =&amp;gt; t.a &amp;gt;= 1)&lt;br /&gt;p1: org.scalacheck.Prop = Prop&lt;br /&gt;&lt;br /&gt;scala&amp;gt; p1.check&lt;br /&gt;+ OK, passed 100 tests.&lt;br /&gt;&lt;br /&gt;scala&amp;gt; val p2 = Prop.forAll(genT)((t:T) =&amp;gt; t.a &amp;lt;= 10)&lt;br /&gt;p2: org.scalacheck.Prop = Prop&lt;br /&gt;&lt;br /&gt;scala&amp;gt; p2.check&lt;br /&gt;+ OK, passed 100 tests.&lt;br /&gt;&lt;br /&gt;scala&amp;gt; val p3 = Prop.forAll(genT)((t:T) =&amp;gt; t.b == false)&lt;br /&gt;p3: org.scalacheck.Prop = Prop&lt;br /&gt;&lt;br /&gt;scala&amp;gt; p3.check&lt;br /&gt;! Falsified after 2 passed tests.&lt;br /&gt;&amp;gt; ARG_0: T(1,true)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Run in combination, it's impossible to know which property failed:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; (p1 &amp;amp;&amp;amp; p2 &amp;amp;&amp;amp; p3).check&lt;br /&gt;! Falsified after 0 passed tests.&lt;br /&gt;&amp;gt; ARG_0: T(2,true)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, you can label the individual properties using a special ":|" operator:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; (p1 :| "a greater than or equal to 1" &amp;amp;&amp;amp; p2 :| "a less than or equal to 10" &amp;amp;&amp;amp; p3 :| "b always false").check&lt;br /&gt;! Falsified after 0 passed tests.&lt;br /&gt;&amp;gt; Labels of failing property:&lt;br /&gt;b always false&lt;br /&gt;&amp;gt; ARG_0: T(8,true)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Lastly, another cool feature which is very "scala-ish": implicit generator definitions. Basically, ScalaCheck uses implicit definitions to decide which generator to use. By default, there are implicit generators for basic types (Int, String, etc., as seen above where no generator definition was necessary for Ints). However, here's how implicit def works for our case class (T):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; implicit def arbT: Arbitrary[T] = Arbitrary(genT)&lt;br /&gt;arbT: org.scalacheck.Arbitrary[T]&lt;br /&gt;&lt;br /&gt;scala&amp;gt; Prop.forAll((t:T) =&amp;gt; t.a &amp;gt;= 1).check &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;br /&gt;+ OK, passed 100 tests.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Don't blink or you'll miss the magic!&lt;br /&gt;&lt;br /&gt;This is all gone into in much more detail in the &lt;a href="http://code.google.com/p/scalacheck/wiki/UserGuide"&gt;ScalaCheck User Guide&lt;/a&gt;, but I hope this overview will prove helpful to some.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4261954431421179908-8851656355454631636?l=blog.q-media.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.q-media.com/feeds/8851656355454631636/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.q-media.com/2010/09/automated-unit-testing-with-scalacheck.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4261954431421179908/posts/default/8851656355454631636'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4261954431421179908/posts/default/8851656355454631636'/><link rel='alternate' type='text/html' href='http://blog.q-media.com/2010/09/automated-unit-testing-with-scalacheck.html' title='Automated Unit Testing with ScalaCheck'/><author><name>stuffinq</name><uri>http://www.blogger.com/profile/05111167194178030567</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_aH5d0LSOPPA/TSoM3Xxa4-I/AAAAAAAAADI/rcrzFXUO3eI/S220/robocloud.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4261954431421179908.post-1750883472247664701</id><published>2010-08-21T14:45:00.004+01:00</published><updated>2011-01-17T16:12:30.501Z</updated><title type='text'>Developing a Project Projection Chart in Excel When the Backlog Contains Sub-Projects (Agile/Scrum)</title><content type='html'>With the keyword being "Excel". (Oh, it's a particular beast, alright.)&lt;br /&gt;&lt;br /&gt;Lest I be burned at the stake by a ferocious pack of Excelites, I begin with the following admission: I am a newbie when it comes to Excel. I write this blog partly to get constructive feedback (should anyone have any), and partly to help anyone who might struggle with a similar problem and who might receive some modicum of joy from this method.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Probelm&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;First, let me define a problem: You have a backlog that contains work which is categorized in some way. The categories might be "sub-projects", "feature sets", or similar. You want to get a picture of when a particuar category is going to be worked on, or when work overall on a particuar category is going to be done. Furthermore, you don't just want to know, you want to be able to make a big, visible chart out of the data.&lt;br /&gt;&lt;br /&gt;This excercise was in part inspired by this blog post: &lt;a href="http://www.management30.com/posts/2010/7/23/is-the-gantt-chart-useless-in-agile-projects.html"&gt;Is the Gantt Chart Useless in Agile Projects?&lt;/a&gt; written by Michael Cardus on &lt;a href="http://www.noop.nl/"&gt;Jurgen Appelo's&lt;/a&gt; Management 3.0 site. That post deals specifically with Gantt charts in Agile, and a Gantt chart wasn't exactly what we needed, but is quite similar. So the "literature", so to speak, on doing Gantt charts in Excel should prove useful.&lt;br /&gt;&lt;br /&gt;Let's say your backlog looks like this:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_aH5d0LSOPPA/TG_RfRuXh5I/AAAAAAAAABQ/gA2IX-loPbc/s1600/backlogshort.JPG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img alt="" border="0" id="BLOGGER_PHOTO_ID_5507851204561438610" src="http://2.bp.blogspot.com/_aH5d0LSOPPA/TG_RfRuXh5I/AAAAAAAAABQ/gA2IX-loPbc/s320/backlogshort.JPG" style="cursor: hand; cursor: pointer; float: left; height: 306px; margin: 0 10px 10px 0; width: 320px;" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So, we have four categories with various stories which have been put in a completely random (uh, I mean very sensible and thought-out) order by the Product Owner. Each story has a size. We also (and maybe not everyone does this) keep old backlog items around and mark as "done", so we'll need to take that into consideration.&lt;br /&gt;&lt;br /&gt;Let's also assume that using Excel is a requirement. Please don't question this requirement; I did, and my arse still hurts from the spanking.&lt;br /&gt;&lt;br /&gt;The first thing I did when trying to "solve" this problem was to try a load of various ways of summarizing the data and visualizing it with built in Excel chart tools. Once I managed to reinsert all the hair that I had pulled out of my head (actually, there's still a spot on top which isn't covered and I'm pretty sure that wasn't there before ;-), I eventually settled on this method: Convert the story points to days based on velocity, calculate the work activity on a per day basis, then use a stacked bar chart to visualize.&lt;br /&gt;&lt;br /&gt;Let's look at those steps individually:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 1: Points to Days&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Now, I know what you're thinking: story points are not a measure of time! Yes, but that doesn't mean to say that we're not allowed to use our average (and worst case) velocity to make loose predictions for release planning purposes. Work with me here, I'm pretty sure I'm not violating some law of Scrum physics that's going to open up a hole in the Points/Time Continuum.&lt;br /&gt;&lt;br /&gt;The way I did this conversion was to use a few hidden columns in the backlog to show "preceding points" for each story (i.e. story points up to this story in the backlog which were not yet complete), as well as "days before" and "days at completion" both based on velocity.&lt;br /&gt;&lt;br /&gt;The formula for getting the preceding points was a bit tricky. To sum the "undone" work in the backlog that precedes this story:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: monospace; white-space: pre-wrap;"&gt;{=SUM(IF(($E4&amp;lt;&amp;gt;"y")*($D4&amp;gt;0)*($E$4:$E$14&amp;lt;&amp;gt;"y")*(ROW($D$4:$D$14)&amp;lt;ROW()),$D$4:$D$14,0))}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Where D is the Points column, and E is the Done? column.&lt;br /&gt;&lt;br /&gt;It's an array formula (a key fact: look up array formulas if you don't know about them, they're quite cool) that basically looks at the whole table to get points for stories where "Done?" is not "y" and the row number is less than the current row number. Looks easy in hindsight, doesn't it!&lt;br /&gt;&lt;br /&gt;Note that the "*" is how we "AND" conditions in an array formula. Excel's "AND" function won't work here.&lt;br /&gt;&lt;br /&gt;The next two calculations really are easy.&lt;br /&gt;&lt;br /&gt;Calculate the number of days until this story will start (based on current velocity):&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: monospace; white-space: pre-wrap;"&gt;=($F4/($C$23/7))&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Calculate the number of days until this work will be complete (based on current velocity and story size):&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: monospace; white-space: pre-wrap;"&gt;=IF($E4&amp;lt;&amp;gt;"y",($F4+$D4)/($C$23/7),0)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;Where F is the "preceding points" column we calculted above, C23 is the average sprint velocity and 7 is the number of days per sprint.&lt;/div&gt;&lt;br /&gt;Now, with those columns unhidden, the backlog looks like this:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_aH5d0LSOPPA/TG_SdWluWSI/AAAAAAAAABY/slDTsQwXVtE/s1600/backlog.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_aH5d0LSOPPA/TG_SdWluWSI/AAAAAAAAABY/slDTsQwXVtE/s320/backlog.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;So, with that chore out of the way, let's talk about what we want this graph to look like. I always find with Excel that it's easier to think about what data you need to create a workable chart first, and then format the data accordingly. It's too easy to disappear down a rabbit hole if you take what seems to be sensible data and try to make the chart fit *it*.&lt;br /&gt;&lt;br /&gt;Ultimately, this is what we end up with (what we are going for):&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_aH5d0LSOPPA/TG_S08uaJcI/AAAAAAAAABg/G6D_wArQvhQ/s1600/backlogchart2.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_aH5d0LSOPPA/TG_S08uaJcI/AAAAAAAAABg/G6D_wArQvhQ/s320/backlogchart2.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The Gantt chart literature made it quite clear that a stacked bar chart would be our friend. I played around a bit with line charts and scatter plots, but nothing worked quite right. One thing that slightly bugs me about Excel is not being able to do a chart which is categorical on both axes. If that were possible then a mapping of our categories to a Sprint date (or Sprint number) would be relatively easy. I'm sure there's a mathematical reason for it (surely there's a reason for everything Microsoft does?), but Excel only supports value/value (scatter plot) and category/value (everything else).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Step 2: The Chart Data&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Now this is where it gets ugly. How the hell are we going to get a chart that looks like that from our data? Turns out, it's pretty easy if we just stop thinking about it and lay the data out in the dumbest way we can think of! (I've always had this theory that computers are really, really dumb, and programmers are just people who are good at breaking stuff down into simple instructions that make sense to a dummy!)&lt;br /&gt;&lt;br /&gt;Take a look at the picture below to see how this was done. Better yet, download the spreadsheet &lt;a href="http://www.q-media.com/files/ProductBacklogChartTest.xlsx.zip"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_aH5d0LSOPPA/TG_S8ic76lI/AAAAAAAAABo/8yWFlirNWnI/s1600/backlogchartdata.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_aH5d0LSOPPA/TG_S8ic76lI/AAAAAAAAABo/8yWFlirNWnI/s320/backlogchartdata.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Note: the pic doesn't show the whole thing. There is data for every day that we want to show (42 days, or 6 sprints of 7 days, in this case).&lt;br /&gt;&lt;br /&gt;So, literally, there's a column for every day, and there's a cell for every category/day that has a 1 or 0 depending on whether there is work in that category for that day. The formula looks like this:&lt;br /&gt;&lt;br /&gt;Determine if this Category on this Day contains work (show 1 or 0):&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: monospace; white-space: pre-wrap;"&gt;{=SUM(IF((Table1[category]=$U5)*(Table1[done?]&amp;lt;&amp;gt;"y")*(V$3&amp;gt;=Table1[days to start])*(V$3&amp;lt;Table1[days to finish]),1,0))}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Where U is the Category column, and V3 points to the day number of the current column.&lt;br /&gt;&lt;br /&gt;So, basically, for all items in this category: is this day in between the start and finish day of these pieces of work?&lt;br /&gt;&lt;br /&gt;The "1" actually creates the "height" for our bar in the graph. The trick here is the use of the inverse items (blanks) to fill in the vertical space below other categories. The "blanks" are just set to "No Fill" in the chart, so they are literally blanks.Figuring that one out was my proudest moment using Excel to date (OK, you caught me, I'm easily impressed).&lt;br /&gt;&lt;br /&gt;A few other bits of trickery: eliminate gaps in bars so they look continuous; set horizontal axis major unit to 7 for sprint dates; delete left axis; delete "blanks" from legend; and we're done!&lt;br /&gt;&lt;br /&gt;So, please download that spreadsheet and use this if you like it. Or, if you have a better idea, I'd love to hear it!&lt;br /&gt;&lt;br /&gt;'Bye for now...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4261954431421179908-1750883472247664701?l=blog.q-media.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.q-media.com/feeds/1750883472247664701/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.q-media.com/2010/08/developing-project-projection-chart-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4261954431421179908/posts/default/1750883472247664701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4261954431421179908/posts/default/1750883472247664701'/><link rel='alternate' type='text/html' href='http://blog.q-media.com/2010/08/developing-project-projection-chart-in.html' title='Developing a Project Projection Chart in Excel When the Backlog Contains Sub-Projects (Agile/Scrum)'/><author><name>stuffinq</name><uri>http://www.blogger.com/profile/05111167194178030567</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_aH5d0LSOPPA/TSoM3Xxa4-I/AAAAAAAAADI/rcrzFXUO3eI/S220/robocloud.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_aH5d0LSOPPA/TG_RfRuXh5I/AAAAAAAAABQ/gA2IX-loPbc/s72-c/backlogshort.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4261954431421179908.post-1786986345182095166</id><published>2010-06-08T22:52:00.014+01:00</published><updated>2011-01-21T13:39:06.421Z</updated><title type='text'>What's So Funny About ATDD, BDD, and Dependency Injection?</title><content type='html'>With apologies to Elvis Costello...&lt;br /&gt;&lt;br /&gt;So, I've been thinking about a way to introduce the concepts of Acceptance Test Driven Development (ATDD) and Behavior Driven Development (BDD) to our developers. I'd also like to cover Dependency Injection as this seems to be something that comes up a lot when we discuss TDD in our little neck of the woods.&lt;br /&gt;&lt;br /&gt;As an example, I've used an abstraction from a large project that we've been working on. The project is a data conversion project. I won't go into details, but the gist is this: take some data (probably from a database), perform some validation checks on it, filter stuff that we don't want, convert some stuff that we do want, and send it to some kind of output stream.&lt;br /&gt;&lt;br /&gt;Our project is Java-based, but I've chosen to use Scala for this example for a number of reasons:&lt;br /&gt;1. Scala seemed interesting, and I wanted to learn more.&lt;br /&gt;2. Scala has parser combinators which is perfect to test my idea of using a DSL to define our conversion rules.&lt;br /&gt;3. ScalaTest supports ATDD and BDD beautifully via its FeatureSpec and FlatSpec traits, respectively.&lt;br /&gt;4. Scala has some neat ways of dealing with dependency injection (see Chapter 27 of the Odersky book).&lt;br /&gt;5. Scala is fully interoperable with Java, so we can reuse any libraries or code from the existing project.&lt;br /&gt;&lt;br /&gt;See Scala and ScalaTest &lt;a href="http://www.scala-lang.org/"&gt;here&lt;/a&gt; and &lt;a href="http://www.scalatest.org/"&gt;here&lt;/a&gt;, respectively.&lt;br /&gt;&lt;br /&gt;So, let's get started... (please bear in mind that I am well aware that my Scala code, being a relative newcomer to it, is likely to be a bit rough... I welcome suggestions!)&lt;br /&gt;&lt;br /&gt;Firstly, I set up an ant build.xml that would compile my Scala code and run my ScalaTest tests. There are basically 3 files (not counting the build.xml): 1) test/UnitTests.scala, 2) test/AcceptanceTests.scala, and 3) src/Conversion.scala. It's a pretty small project for purposes of this exercise, so there's no need to get crazy here, plus it's nice to be able to easily ":load" files in the Scala interpreter (note: by the end it was just approaching large enough to warrant breaking some stuff out into more files, but I chose not to go there).&lt;br /&gt;&lt;br /&gt;Note: the code for this project can be downloaded &lt;a href="http://www.q-media.com/files/conversion.zip"&gt;here&lt;/a&gt;. Please take a look. I am just going to focus this blog on a few of the more interesting points.&lt;br /&gt;&lt;br /&gt;The application design was set up with the idea of ATDD in mind. There would be a Conversion class that would take all the necessary bits as parameters: those bits being the datasource, the rules for the conversions, and some kind of output stream device. So, let's look at some of the acceptance tests first.&lt;br /&gt;&lt;br /&gt;The first thing to bear in mind was that I was going to need some kind of Data type for retrieving and carrying the input data. I knew that this might come from a database, but I wanted to be able to "inject" it for purposes of these tests. I decided to go with a simple Map[String,String] to capture field names and values. There would be an abstract class called "Data" and one called "DataSet" that I could implement to get my tests set up. The DataSet class would have a foreach method, so that rows could be read from a database one at a time for processing (rather than pass a whole set of data at once, as our datasets are quite large in reality).&lt;br /&gt;&lt;br /&gt;Additionally, I would need "Rules". I figured that these would be read from a file, and would comprise our "DSL" bit, so I opted to have these be simple Strings. Again, an abstract class called RuleSet (List[String] for testing) would also have the potential for some other functionality, such as reading the Rules from a file.&lt;br /&gt;&lt;br /&gt;I also used a static "Log" object to capture system output for monitoring. The default Log would just write to STDOUT, but I also implemented a version that would write to a list buffer, so that I could capture that stuff for testing, too.&lt;br /&gt;&lt;br /&gt;Also, an Output class which I implemented as a ListBuffer as well for testing. In reality, this Output class could write to a database, or send data across the network, etc.&lt;br /&gt;&lt;br /&gt;Lastly, a Conversion object which takes each of our little dependencies via its apply method, and does it's magic (basically apply all Rules to each Data object in the DataSet and capturing the output via our Output object).&lt;br /&gt;&lt;br /&gt;Here's a snapshot of the acceptance tests...&lt;br /&gt;&lt;br /&gt;&lt;pre class="scala" name="code"&gt;class RuleApiFeatureSpec extends FeatureSpec with GivenWhenThen with MustMatchers {&lt;br /&gt;&lt;br /&gt; val d1 = MapData(Map(("id","1"),("a","5foo"),("b","1234bar234")))&lt;br /&gt; val d2 = MapData(Map(("id","2"),("a","foo"),("b","bar45")))&lt;br /&gt; val d3 = MapData(Map(("id","3"),("a","454foo45"),("b","445bar")))&lt;br /&gt; val d4 = MapData(Map(("id","4"),("a","43foo45"),("b","bar87")))&lt;br /&gt; val d5 = MapData(Map(("id","5"),("a","foo45"),("b","3bar4546")))&lt;br /&gt; val d6 = MapData(Map(("id","6"),("a","cfoo"),("b","bar")))&lt;br /&gt;&lt;br /&gt; object MapDataSet extends DataSet {&lt;br /&gt;  // in production, DataSet may come from a database (can be read one record at a time in foreach)&lt;br /&gt;  val data = List(d1,d2,d3,d4,d5,d6)&lt;br /&gt;     def foreach(f:(Data)=&amp;gt;Unit):Unit = { data.foreach(f) }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; val r0 = "f:b /^[0-9][0-9]+.*/ Convert x:transformAandB" // convert a and b if b starts with two numbers&lt;br /&gt; val r1 = "f:b /^[0-9]+.*/ Convert x:transformA" // convert a if b starts with a number&lt;br /&gt; val r2 = "f:a /^[0-9]+.*/ Filter" // filter if a starts with a number&lt;br /&gt; val r3 = "f:b /^b.*/ Convert x:transformB" // convert b if b starts with "b"&lt;br /&gt; val r4 = "f:a /^c.*/ NoMatch" // bad rule&lt;br /&gt; &lt;br /&gt; val rules = RuleList(List(r0,r1,r2,r3,r4),MyRuleParser)&lt;br /&gt;&lt;br /&gt; class ListOut extends Output {&lt;br /&gt;     var container = new ListBuffer[Data]()&lt;br /&gt;     def apply(dataIn:Data) = {&lt;br /&gt;        container+dataIn&lt;br /&gt;     }&lt;br /&gt;  def getById(n:String):Data = { container.filter( (d) =&amp;gt; d("id") == n )(0) } // assuming one match here&lt;br /&gt;     override def toString = "***OUTPUT***\n"+container.mkString("\n")&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; Log.setType("list")&lt;br /&gt; &lt;br /&gt; feature("Conversion API"){&lt;br /&gt;  scenario("Filtered Output"){&lt;br /&gt;   given("The default testing scenario set up above")&lt;br /&gt;   when("I run the conversion")&lt;br /&gt;   val out = new ListOut&lt;br /&gt;   Conversion(MapDataSet,rules,out)&lt;br /&gt;   then("I expect to see five records in the output")&lt;br /&gt;   out.container must have length 5&lt;br /&gt;   then("The filtered record should be id 4")&lt;br /&gt;   out.container.map( d =&amp;gt; d("id") ).contains("4") must be === false&lt;br /&gt;   then("The records not filtered would be 1,2,3,5,6")&lt;br /&gt;   out.container.map( d =&amp;gt; d("id") ).sameElements(List("1","2","3","5","6")) must be === true&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, this system is geared toward Acceptance Testing, via dependency injection. And ScalaTest's FeatureSpec is great for this. It even gives you a nice "Given When Then" syntax, so that your real world acceptance criteria can easily be translated into automated acceptance tests. You are writing acceptance criteria, aren't you!?!? ;-)&lt;br /&gt;&lt;br /&gt;Other acceptance tests follow more or less the same pattern.&lt;br /&gt;&lt;br /&gt;BTW, Here's the code for the Conversion object:&lt;br /&gt;&lt;br /&gt;&lt;pre class="scala" name="code"&gt;object Conversion extends Logging {&lt;br /&gt;    def apply(data:DataSet, rules:RuleSet, output:Output) = {&lt;br /&gt;        data.foreach(&lt;br /&gt;            d =&amp;gt; {&lt;br /&gt;                log("PRE PROCESSING: "+d)&lt;br /&gt;                val o = rules(d)&lt;br /&gt;                log("POST PROCESSING: "+o)&lt;br /&gt;                if(!o.isFiltered()) { log("ADDING TO OUTPUT: "+o); output(o); }&lt;br /&gt;            }&lt;br /&gt;        )&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The foreach method on the DataSet is called and the rules applied to each resulting object. So, by implementing a "DbDataSet", for example, these records could be pulled from a database.&lt;br /&gt;&lt;br /&gt;I'll just show the RuleSet code, because I love this little Scala trick:&lt;br /&gt;&lt;br /&gt;&lt;pre class="scala" name="code"&gt;case class RuleList(rs:List[String],rp:RuleParser) extends RuleSet with Logging {&lt;br /&gt;    val rules = rs.map { r =&amp;gt; rp.parse(r) }.filter{ r =&amp;gt; r != None }.map{ r =&amp;gt; r.get }.toSeq&lt;br /&gt;    def apply(dataIn:Data):Data = {&lt;br /&gt;        log("APPLYING RULES")&lt;br /&gt;        Function.chain(rules)(dataIn)&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I suspect there might be a cleaner way to do that map/filter/map thing, but the cool thing is the line: Function.chain(rules)(dataIn). Basically, with the "rules" being implementations of Function[Data,Data], it's basically saying "apply this data object to this chain of Rule functions". Nice.&lt;br /&gt;&lt;br /&gt;This line sets up the rules, and references the RuleParser:&lt;br /&gt;&lt;br /&gt;&lt;pre class="scala" name="code"&gt;val rules = RuleList(List(r0,r1,r2,r3,r4),MyRuleParser)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here's the code for the RuleParser:&lt;br /&gt;&lt;br /&gt;&lt;pre class="scala" name="code"&gt;object MyRuleParser extends RegexParsers with RuleParser {&lt;br /&gt;    def fieldStart:Parser[String] = "f:"&lt;br /&gt;    def field:Parser[String] = "[A-Za-z]+".r ^^ { _.toString }&lt;br /&gt;    def functionStart:Parser[String] = "x:"&lt;br /&gt;    def function:Parser[String] = "[A-Za-z]+".r ^^ { _.toString }&lt;br /&gt;    def pattern:Parser[Regex] = "/.*?/".r ^^ { case p =&amp;gt; new Regex("/".r.replaceAllIn(p,"")) }&lt;br /&gt;    def action:Parser[String] = "Filter|Convert".r ^^ { _.toString }&lt;br /&gt;    def rule1:Parser[Rule] = fieldStart~field~pattern~action~functionStart~function ^^ { case fs~f~p~a~xs~x =&amp;gt; Rule(new PatternMatcher(f,p),a,ConvertFuns.functions(x)) }&lt;br /&gt;    def rule2:Parser[Rule] = fieldStart~field~pattern~action ^^ { case fs~f~p~a =&amp;gt; Rule(new PatternMatcher(f,p),a,NoConvert) }&lt;br /&gt;    def rule:Parser[Rule] = rule1 | rule2&lt;br /&gt;    def parse(input:String):Option[Rule] = parseAll(rule,input) match {&lt;br /&gt;        case Success(e,_) =&amp;gt; Some(e)&lt;br /&gt;        case f: NoSuccess =&amp;gt; None&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This uses parser combinators to turn our "Rule" (strings) into "Rule" objects. Rule itself looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="scala" name="code"&gt;case class Rule(matcher:MatchFun, action:String, conversion:ConvertFun) extends Function[Data,Data] with Logging {&lt;br /&gt;    def apply(dataIn:Data):Data = {&lt;br /&gt;        log("RULE: "+this.action+" on data "+dataIn+":")&lt;br /&gt;  if(dataIn.isFiltered){ log("FILTERED"); dataIn }&lt;br /&gt;  else if(matcher(dataIn)){&lt;br /&gt;   action match {&lt;br /&gt;    case "Filter" =&amp;gt; { log("FILTERING"); dataIn.setFiltered() }&lt;br /&gt;    case "Convert" =&amp;gt; { log("CONVERTING"); conversion(dataIn) }&lt;br /&gt;    case _ =&amp;gt; { log("BAD ACTION"); dataIn }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  else{ log("NO MATCH"); dataIn }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, I'd like to talk about unit tests.&lt;br /&gt;&lt;br /&gt;For unit tests, I chose to use the FlatSpec trait in ScalaTest. The reason is because I wanted to do BDD, with the basic "Object when something should something in...{ code }" type syntax, but I really didn't like all the nesting that goes on in most libraries. Bill Venners has solved that problem for us with FlatSpec. The name is as it implies. Here are some examples...&lt;br /&gt;&lt;br /&gt;&lt;pre class="scala" name="code"&gt;class MapDataSpec extends FlatSpec {&lt;br /&gt; val m = MapData(Map(("id","1")))&lt;br /&gt; "A MapData object with an 'id' field" should "have an 'id' key" in {&lt;br /&gt;  expect(true){ m.has("id") }&lt;br /&gt; }&lt;br /&gt; "A MapData object with an 'id' field with value '1'" should "return that value for that key" in {&lt;br /&gt;  expect("1"){ m("id") }&lt;br /&gt; }&lt;br /&gt; "A MapData object which has no 'a' field set" should "throw error when accessed" in {&lt;br /&gt;  expect(false){ m.has("a") }&lt;br /&gt;  intercept[NoSuchElementException]{ m("a") }&lt;br /&gt; }&lt;br /&gt; "A MapData object which has a field set" should "recognize that key and return that value for that key" in {&lt;br /&gt;  val m2 = m.set("a", "foo")&lt;br /&gt;  expect(true){ m2.has("a") }&lt;br /&gt;  expect("foo"){ m2("a") }&lt;br /&gt; }&lt;br /&gt; "A MapData object which has not had filtered set" should "return false for isFiltered" in {&lt;br /&gt;  expect(false){ m.isFiltered }&lt;br /&gt; }&lt;br /&gt; "A MapData object which has filtered set" should "return true for isFiltered" in {&lt;br /&gt;  val m2 = m.setFiltered&lt;br /&gt;  expect(true){ m2.isFiltered }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And the unit tests continue on like that (please see linked code for details). Nice, simple syntax. Very intuitive.&lt;br /&gt;&lt;br /&gt;I hope this makes some sense, and proves helpful to some.&lt;br /&gt;&lt;br /&gt;Edit: BTW, I have found a slightly more elegant way of doing this:&lt;br /&gt;&lt;pre class="scala" name="code"&gt;val rules = rs.map { r =&amp;gt; rp.parse(r) }.filter{ r =&amp;gt; r != None }.map{ r =&amp;gt; r.get }.toSeq&lt;br /&gt;&lt;/pre&gt;which is this:&lt;br /&gt;&lt;pre class="scala" name="code"&gt;val rules = rs.map { r =&amp;gt; rp.parse(r) }.flatMap{ x=&amp;gt;x }.toSeq&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4261954431421179908-1786986345182095166?l=blog.q-media.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.q-media.com/feeds/1786986345182095166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.q-media.com/2010/06/whats-so-funny-about-atdd-bdd-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4261954431421179908/posts/default/1786986345182095166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4261954431421179908/posts/default/1786986345182095166'/><link rel='alternate' type='text/html' href='http://blog.q-media.com/2010/06/whats-so-funny-about-atdd-bdd-and.html' title='What&apos;s So Funny About ATDD, BDD, and Dependency Injection?'/><author><name>stuffinq</name><uri>http://www.blogger.com/profile/05111167194178030567</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_aH5d0LSOPPA/TSoM3Xxa4-I/AAAAAAAAADI/rcrzFXUO3eI/S220/robocloud.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4261954431421179908.post-7136366847544980652</id><published>2010-03-09T02:03:00.006Z</published><updated>2010-03-11T10:54:34.623Z</updated><title type='text'>On the Fence: API vs DSL for programming interactive music in the DOME</title><content type='html'>&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;object height="282" width="470"&gt;&lt;param name="movie" value="http://www.youtube.com/v/UU-RX_Auvd4&amp;hl=en_US&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/UU-RX_Auvd4&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="470" height="282"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;I've been working on an "online" version of the &lt;b&gt;DOME&lt;/b&gt;, which is an interactive music engine for video games that I've been developing on and off for a number of years (for more info see &lt;a href="http://www.dometechnics.com/"&gt;Dometechnics)&lt;/a&gt;. By "online" I mean a version of the engine that could be embedded in a web page, so that it could be made available for developers of Flash and Javascript games.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;The engine itself is a Java applet, which is scriptable via Javascript. Java, for all its faults, is still the best platform for producing MIDI-based synthesized music in a web page. Thanks for nothin', Adobe. Keeping an eye on Google's &lt;a href="http://code.google.com/p/nativeclient/"&gt;NativeClient&lt;/a&gt;, though.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;This blog post will take a look at the challenges of getting Javascript and Java to talk to one another in a web page, but mostly, I want to look at the design decisions that I've been making about the API for doing music programming and whether or not it would be more appropriate to call it a "DSL" (although the distinction might just be syntactic sugar).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;Lastly, I've embedded a screencast (see above) to demo the concepts discussed here.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;In order to make sense of this post, I should explain a little bit about how the DOME works. It's based on the idea of creating a "score" in the sense of a theme and variations on that theme, followed perhaps by another theme. Loosely, it's designed to enable "cinema-like" scores in interactive contexts: "Films often have different themes for important characters, events, ideas or objects, taking the idea from Wagner's use of leitmotif. These may be played in different variations depending on the situation they represent, scattered amongst incidental music." (&lt;a href="http://en.wikipedia.org/wiki/Film_score"&gt;source Wikipedia&lt;/a&gt;).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;It sort of looks like this (from Javascript):&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;dome.setTheme( _JSON-formatted-theme-data_ );&lt;br /&gt;dome.play();&lt;br /&gt;var minor_variation = 'E-&amp;gt;Eb,B-&amp;gt;Bb|!bar:4'; // C major to C minor, but not in bar 4&lt;br /&gt;dome.bind( _some-dom-element_, 'onclick', function(){ dome.vary(minor_variation) } );&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;See how easy!?!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;The interesting part in terms of this API vs DSL discussion is the line where the variation is defined. Because of limitations in passing anything other than strings or numbers from Javascript to Java, we might have ended up with something like this:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;dome.vary('pitch', '+', 5); // not a very attractive looking command to use (and operators as strings? ...bleh)&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;Instead, we can do this:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;dome.vary('pitch+5'); // better. now it's like a mini-script (will require some parsing magic, though)&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;...Great. (gratuitous &lt;a href="http://www.youtube.com/watch?v=TebUMhJAKSM"&gt;Fast Show&lt;/a&gt; reference)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;That's all well and good, but in fact, the API approach works better in certain situations. Namely when there's a lot of data to pass (for instance, when the initial theme is established), because an API call can just take a JSON-ified Javascript object as a string. That's because we can take advantage of existing libraries for parsing JSON. But having a DSL for creating variations, in this case, allows for the music programming to occur in a more natural-language type of way.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;"The key point is that at each layer the API/DSL should allow the user ... to express the intent of what they want to do as easily as possible." (&lt;a href="http://www.testdrivensoftware.com/?p=85"&gt;http://www.testdrivensoftware.com/?p=85&lt;/a&gt;)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;As a side note, if you're the type of person who gets a kick out of making fun of DSL fan boys, I would direct you to chromatic's &lt;a href="http://www.oreillynet.com/onlamp/blog/2007/05/the_is_it_a_dsl_or_an_api_ten.html"&gt;amusing discourse&lt;/a&gt; on the topic.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;So, I've got an API to dump a Javascript object on Java and a DSL for scripting that object, once it's deserialized and appropriately instantiated in Java. In a sense this is still API, because it's essentially a function call on an object, passing in a string. There is no interpreter in the traditional sense of a scripting language. But because the string format for defining variations is a mini-language, it kind of becomes a DSL.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;...Great.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;Another option might have been to use Java 1.6's scripting API (&lt;a href="http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/"&gt;javax.script&lt;/a&gt;), but I had made the decision early on that I wanted this system to be compatible with older versions of Java. I believe the difference is that with Java 1.6, one could theoretically write Javascript code and have that evaluated in the Java application (thereby allowing Javascript functions to manipulate the Java objects). It's a nice idea, but, not critical, and Java 1.5 (and previous) compatibility seemed more important. Why? Because at the time of this writing, the MIDI functionality in Java 1.6 on Mac OS X is broken... that's good enough for me!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;Another fun point I'd like to make: At one point during the development of this API/DSL, I fell afoul of a little difference between scripting an applet via the Rhino shell and doing it in a web page. In Rhino, you can return a Java object to Javascript from a method in the applet, and pass that object back to the applet in another function call. Not so via &lt;a href="http://en.wikipedia.org/wiki/LiveConnect"&gt;LiveConnect&lt;/a&gt;!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;For example:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;var applet = document.MyApplet;&lt;br /&gt;var obj = applet.getSomeObject(); // return a Java object&lt;br /&gt;applet.doSomethingWithObject(obj); // OK in Rhino! Will cause 40 days and nights of floods if you try this in a browser!&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;So, one kind of work around, is to have Java use a method on the object to execute the relevant code. This could involve having to share state between the applet and the object in question.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;var applet = document.MyApplet;&lt;br /&gt;var obj = applet.getSomeObject(); // return a Java object&lt;br /&gt;obj.doSomethingWithMe(); // works OK in a browser&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;...Great.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;So, let's summarize: Embed the DOME in a web page as an applet and make it "scriptable" via Javascript, and support Java 1.5 for unbroken MIDI. With the goal being the ability to create a theme and define variations on that theme, and invoke those variations by binding them to Javascript events (hence making interactive music possible in Flash and Javascript based games). Done! Alright, not so much done, but it is a working prototype.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;Because this is not really ready for prime time, I have produced the following screencast to demonstrate the ideas discussed in this blog. I am actively looking for development resource and industry contacts to bring this technology to fruition. Please come back to follow further progress.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Trebuchet MS',sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4261954431421179908-7136366847544980652?l=blog.q-media.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.q-media.com/feeds/7136366847544980652/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.q-media.com/2010/03/on-fence-api-vs-dsl-for-programming.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4261954431421179908/posts/default/7136366847544980652'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4261954431421179908/posts/default/7136366847544980652'/><link rel='alternate' type='text/html' href='http://blog.q-media.com/2010/03/on-fence-api-vs-dsl-for-programming.html' title='On the Fence: API vs DSL for programming interactive music in the DOME'/><author><name>stuffinq</name><uri>http://www.blogger.com/profile/05111167194178030567</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://4.bp.blogspot.com/_aH5d0LSOPPA/TSoM3Xxa4-I/AAAAAAAAADI/rcrzFXUO3eI/S220/robocloud.jpg'/></author><thr:total>0</thr:total></entry></feed>
