<?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-2076691526526730952</id><updated>2011-10-13T08:45:18.064-05:00</updated><category term='chaco'/><category term='travel'/><category term='numpy'/><category term='data visualization'/><category term='finance'/><category term='python'/><category term='trading'/><category term='enthought'/><category term='scipy'/><category term='sqlite'/><category term='gis'/><category term='test driven development'/><category term='traits'/><category term='mapnik'/><category term='open source'/><category term='modern portfolio theory'/><title type='text'>Travis Vaught</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://travisvaught.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://travisvaught.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Travis</name><uri>http://www.blogger.com/profile/00720286668168584326</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://2.bp.blogspot.com/_3SMSqEHAuEQ/SnCHvdf6zII/AAAAAAAAAOc/FJxkHVZ0fvo/S220/IMG_8035.JPG'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>10</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2076691526526730952.post-268416090745896684</id><published>2011-09-01T18:47:00.001-05:00</published><updated>2011-09-01T18:52:56.505-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='modern portfolio theory'/><category scheme='http://www.blogger.com/atom/ns#' term='scipy'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='numpy'/><category scheme='http://www.blogger.com/atom/ns#' term='chaco'/><title type='text'>Modern Portfolio Theory - A Python Implementation</title><content type='html'>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;br /&gt;I was surprised last week to find there was no accessible Python implementation of the calculation of the Efficient Frontier (as defined by &lt;a href="http://en.wikipedia.org/wiki/Harry_Markowitz"&gt;Markowitz&lt;/a&gt; in his presentation of &lt;a href="http://en.wikipedia.org/wiki/Modern_portfolio_theory"&gt;Modern Portfolio Theory&lt;/a&gt; ~1957).*   Since the problem seemed simple to solve with the tools at hand, I set out to "right the wrong" and develop an open implementation (available under an open, BSD license &lt;a href="https://github.com/tvaught/experimental/tree/master/portfolio_metrics"&gt;via github&lt;/a&gt;) that meets my needs.&lt;br /&gt;&lt;br /&gt;In the process of building the basic metrics, I was able to experiment with NumPy's datetime support -- as mentioned in my &lt;a href="http://travisvaught.blogspot.com/2011/08/numpys-new-datetime-support-and.html"&gt;earlier post&lt;/a&gt;.  The API's are still a bit in flux, but I thought I would write up a full workflow using the tools as they are.  I would greatly appreciate feedback/contributions/suggestions. &amp;nbsp;So let's dive in ...&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Modern Portfolio Theory&lt;/span&gt;&lt;br /&gt;While there are much better sources for an explanation of MPT, I'll take a stab at a high level introduction to the basic concepts. MPT is based on the idea that a diversified portfolio--a portfolio that holds several assets, or asset classes, that have some inverse correlation--may be constructed which provides &lt;i&gt;less risk&lt;/i&gt; and the &lt;i&gt;same return&lt;/i&gt; as any of the single assets. &amp;nbsp;Or similarly, an asset mix may be chosen that has the &lt;i&gt;same risk&lt;/i&gt; as any single asset, but a &lt;i&gt;higher return&lt;/i&gt;. If one plots the returns of an asset along the value (&lt;code&gt;y)&lt;/code&gt; axis versus the risk&amp;nbsp;of an asset along the index (&lt;code&gt;x)&lt;/code&gt; axis, it's easy to spot desirable assets based on their location on the chart. &amp;nbsp;While an efficient market may imply that there is a close correlation between risk and return, some assets may lie closer to the upper left portion of the chart. &amp;nbsp;These would provide less risk and higher returns than assets falling in the lower right portion of the chart.&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/-VbwSb5brJnE/Tl27mJk04LI/AAAAAAAAAtU/389qZtLtx_w/s1600/Screen+Shot+2011-08-30+at+11.41.16+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="308" src="http://3.bp.blogspot.com/-VbwSb5brJnE/Tl27mJk04LI/AAAAAAAAAtU/389qZtLtx_w/s320/Screen+Shot+2011-08-30+at+11.41.16+PM.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;span class="Apple-style-span" style="font-size: x-small;"&gt;Figure: 1 Plot of Risk (Annual Volatility: x axis) vs. Return (Annualized Returns: y axis)&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;For example, it's easy to see in &lt;u&gt;Figure 1&lt;/u&gt; that BA might be a less&amp;nbsp;preferable&amp;nbsp;asset than COP, since COP has a higher return for less risk (historically, at least).&lt;br /&gt;&lt;br /&gt;While there are acknowledged problems with using historical standard deviation as a proxy for risk, we'll continue to implement the &lt;i&gt;standard model&lt;/i&gt; for now. &amp;nbsp;In this standard model for MPT, one may construct a portfolio, by holding assets in &lt;i&gt;optimal&lt;/i&gt; quantities--or relative weights--that lies along a line of all optimal portfolios, or along the &lt;a href="http://en.wikipedia.org/wiki/Modern_portfolio_theory#The_efficient_frontier_with_no_risk-free_asset"&gt;Efficient Frontier&lt;/a&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/-moaLRFIf9Y0/Tl2_t4e9YwI/AAAAAAAAAtY/qSjfQBTanuA/s1600/chaco_ef.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://2.bp.blogspot.com/-moaLRFIf9Y0/Tl2_t4e9YwI/AAAAAAAAAtY/qSjfQBTanuA/s320/chaco_ef.png" width="274" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Points along the Efficient Frontier may be found by solving an optimization problem which finds, for example, the best weighting of assets to maximize return for a given risk tolerance. This is the approach I take.  The &lt;a href="https://github.com/tvaught/experimental/blob/master/portfolio_metrics/mpt.py"&gt;mpt.py&lt;/a&gt; module closely follows the gradient method &lt;a href="http://www.stanford.edu/~wfsharpe/mia/opt/mia_opt1.htm"&gt;described by William Sharpe&lt;/a&gt; to accomplish this.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Software Model&lt;/b&gt;&lt;br /&gt;While I've tried to keep things fairly functionally oriented, I have created two objects that accomplish the tasks of managing the data structures and providing methods. The first object is defined by the &lt;code&gt;Stock&lt;/code&gt; class. It has methods to load data and to store metrics about an asset. &amp;nbsp;The metrics are populated when the class calls metrics functions from the metrics module on its data.&lt;br /&gt;&lt;br /&gt;Before I use my &lt;code&gt;Stock&lt;/code&gt; object, I'll start from scratch by pre-populating a sqlite database file with some useful price data.  You only have to do this once, or as many times as you want to keep your data fresh:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://pastebin.com/embed_js.php?i=yw66wEeh"&gt;&lt;/script&gt;&lt;br /&gt;&lt;br /&gt;Notice that I've loaded &lt;code&gt;symbols&lt;/code&gt; from a csv file (available in the github repo) that has a short sampling of assets from the S&amp;amp;P 500.  (There is also a complete list of S&amp;amp;P symbols in the repo).  The &lt;code&gt;populate_db&lt;/code&gt; method iterates over these symbols and pulls the price data from Yahoo Finance.  Not all the assets loaded from Yahoo (I may have some symbol issues), but I got what I could.  An additional asset's price data is also pulled down to be used as a benchmark.  "^GSPC" is the symbol for the S&amp;amp;P 500 index itself.  Note that passing a list or a filename as the &lt;code&gt;symbols&lt;/code&gt; argument both work.&lt;br /&gt;&lt;br /&gt;Now, on to an example session using a &lt;code&gt;Stock&lt;/code&gt; object:&lt;br /&gt;&lt;script src="http://pastebin.com/embed_js.php?i=uWrrNFCm"&gt;&lt;/script&gt;&lt;br /&gt;&lt;br /&gt;This is pretty straightforward.  A &lt;code&gt;Stock&lt;/code&gt; object and its metrics are generated, as expected.  Things begin to get interesting when the other object in the MPT workflow is introduced: the &lt;code&gt;Portfolio&lt;/code&gt; object. As one might guess, it contains a collection of &lt;code&gt;Stock&lt;/code&gt; objects.  Each &lt;code&gt;Stock&lt;/code&gt; object has had its &lt;code&gt;ratearray&lt;/code&gt; appropriately truncated and imputed in order for every asset's data length to match.&lt;br /&gt;&lt;br /&gt;Here's an example of its usage:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://pastebin.com/embed_js.php?i=YEKfZG47"&gt;&lt;/script&gt;&lt;br /&gt;&lt;br /&gt;Notice that the recommended allocation for a risk tolerance of 2.0 has AAPL weighted to 100% of the portfolio.  This indicates that AAPL is actually on the Efficient Frontier for the given asset options--i.e. there is no combination of assets that gives the same return for less risk.  When the risk tolerance is reduced to 0.5, the allocations include KO as well -- reducing the risk and return, and revealing another point on the Frontier.  So, let's take a look at how the calculations were implemented in Python.&lt;br /&gt;&lt;br /&gt;At a high level, the approach for the optimization is to begin with equal weights of all assets. The marginal utility of all the assets is then calculated and used to determine an optimal two-asset swap.  The process is repeated until two-asset swaps result in minimal (below a threshold) improvement. This is done within the confines of the upper and lower bounds specifically imposed on our weightings.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Marginal Utility&lt;/b&gt;&lt;br /&gt;The marginal utility calculation is given by:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://latex.codecogs.com/gif.latex?mu=e-(1/rt)\cdot2 \cdot \textbf{C}\cdot x" title="e-(1/rt)\cdot2 \cdot \textbf{C}\cdot x" /&gt;&lt;br /&gt;&lt;br /&gt;Where:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://latex.codecogs.com/gif.latex?e" title="e" /&gt; : Vector of expected return of assets.  Uses annualized_adjusted_return for each asset.&lt;br /&gt;&lt;img src="http://latex.codecogs.com/gif.latex?rt" title="rt" /&gt; : Scalar value of risk tolerance in daily volatility.&lt;br /&gt;&lt;img src="http://latex.codecogs.com/gif.latex?\textbf{C}" title="\textbf{C}" /&gt; : Covariance matrix of assets in portfolio.&lt;br /&gt;&lt;img src="http://latex.codecogs.com/gif.latex?x" title="x" /&gt; : Current portfolio weighting.&lt;br /&gt;&lt;br /&gt;For example, the marginal utility in the portfolio we created earlier can be gotten by:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://pastebin.com/embed_js.php?i=TAnMWckW"&gt;&lt;/script&gt;&lt;br /&gt;&lt;br /&gt;This is interpreted as: the utility for AAPL increases at a rate of 33.19% for every unit change in the amount invested in AAPL.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Two Asset Swap&lt;/b&gt;&lt;br /&gt;Taking the min and max of the &lt;img src="http://latex.codecogs.com/gif.latex?mu" title="mu" /&gt; matrix elements reveals which two assets to swap, and in which direction.  What remains is to calculate the optimal amount to swap.  This is given by:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://latex.codecogs.com/gif.latex?a = k_{0} / ( 2 \cdot k_{1} )" title="a = k_{0} / ( 2 \cdot k_{1} )" /&gt;&lt;br /&gt;&lt;br /&gt;Where:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://latex.codecogs.com/gif.latex?a" title="a" /&gt;  : The optimal amount to swap.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://latex.codecogs.com/gif.latex?k_{0}" title="k_{0}" /&gt; : &lt;img src="http://latex.codecogs.com/gif.latex?s' \cdot ( e-(1/rt)\cdot2 \cdot \textbf{C}\cdot x)" title="s' \cdot ( e-(1/rt)\cdot2 \cdot \textbf{C}\cdot x)" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://latex.codecogs.com/gif.latex?k_{1}" title="k_{1}" /&gt; : &lt;img src="http://latex.codecogs.com/gif.latex?\frac{(s' \cdot \textbf{C} \cdot s)}{rt}" title="\frac{(s' \cdot \textbf{C} \cdot s)}{rt}" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://latex.codecogs.com/gif.latex?s" title="s" /&gt;  : A vector representing the stocks to be swapped. For our example, this looks like:&lt;br /&gt;&lt;blockquote&gt;&lt;img src="http://latex.codecogs.com/gif.latex?\begin{bmatrix} 1.0 \\ -1.0\\ 0.0\\ 0.0\\ 0.0 \end{bmatrix}" title="\begin{bmatrix} 1.0 \\ -1.0\\ 0.0\\ 0.0\\ 0.0 \end{bmatrix}" /&gt;&lt;/blockquote&gt;&lt;br /&gt;I'll leave the derivation to &lt;a href="http://www.stanford.edu/~wfsharpe/mia/opt/mia_opt1.htm"&gt;William Sharpe&lt;/a&gt;. The implementation, given SciPy's and NumPy's tool set and expressiveness, was quite simple. Though the bounds checking is a bit of sausage making, the actual methods on the portfolio almost write themselves.&lt;br /&gt;&lt;br /&gt;All this is nice algorithmically, but sometimes it helps to have some graphical interaction, and a visualization of what's happening. This is where the Enthought Tool Suite (ETS) came in handy.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;A GUI App&lt;/b&gt;&lt;br /&gt;Up to this point the only dependencies for the MPT workflow are SQLite, NumPy, and SciPy. To get a visualization, we use a separate module (&lt;a href="https://github.com/tvaught/experimental/blob/master/portfolio_metrics/chaco_mpt_display.py"&gt;chaco_mpt_display.py&lt;/a&gt;) to achieve coolness. We do, however, add the full stack of Enthought tools as a dependency for this module. The good news is that all dependencies can be easily gotten with the &lt;a href="http://www.enthought.com/products/epd.php"&gt;Enthought Python Distribution&lt;/a&gt;. Below is a quick video of this afternoon's version of the GUI--it's changing fairly rapidly as I pile in new features.&lt;br /&gt;&lt;br /&gt;&lt;iframe width="425" height="349" src="http://www.youtube.com/embed/d8_4yTFZAso?hl=en&amp;fs=1" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;Any feedback or corrections are welcome -- feel free to &lt;a href="https://github.com/tvaught/experimental/tree/master/portfolio_metrics"&gt;get the code&lt;/a&gt; from github.  Pull requests are welcome!&lt;br /&gt;&lt;br /&gt;* I recently found a &lt;a href="http://code.google.com/p/risky-business/source/browse/trunk/mpt.py"&gt;nascent implementation&lt;/a&gt; that is fairly tied to a visualization -- I like my approach much better.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2076691526526730952-268416090745896684?l=travisvaught.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://travisvaught.blogspot.com/feeds/268416090745896684/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://travisvaught.blogspot.com/2011/09/modern-portfolio-theory-python.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/268416090745896684'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/268416090745896684'/><link rel='alternate' type='text/html' href='http://travisvaught.blogspot.com/2011/09/modern-portfolio-theory-python.html' title='Modern Portfolio Theory - A Python Implementation'/><author><name>Travis</name><uri>http://www.blogger.com/profile/00720286668168584326</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://2.bp.blogspot.com/_3SMSqEHAuEQ/SnCHvdf6zII/AAAAAAAAAOc/FJxkHVZ0fvo/S220/IMG_8035.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-VbwSb5brJnE/Tl27mJk04LI/AAAAAAAAAtU/389qZtLtx_w/s72-c/Screen+Shot+2011-08-30+at+11.41.16+PM.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2076691526526730952.post-6313935528069643490</id><published>2011-08-25T14:17:00.036-05:00</published><updated>2011-08-30T13:33:12.667-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sqlite'/><category scheme='http://www.blogger.com/atom/ns#' term='scipy'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='finance'/><category scheme='http://www.blogger.com/atom/ns#' term='numpy'/><title type='text'>NumPy's New datetime Support and a Financial Database Roundtrip</title><content type='html'>Last month when I was in Austin, TX at the 10th Annual (!) &lt;a href="http://conference.scipy.org/scipy2011/"&gt;SciPy Conference&lt;/a&gt; I asked around about the status of native datetime support in &lt;a href="http://numpy.scipy.org/"&gt;NumPy&lt;/a&gt;. Based on the response of my good friends at Enthought, I've been living under a rock for a while -- it's been finished for some time now.&lt;br /&gt;&lt;br /&gt;So, a month later, I've wrangled some time to take a look.  I also happened to have a particular problem I wanted to solve. Here's a teaser preview of the output of my Modern Portfolio Theory implementation:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-L-h6PmG00cQ/TlauqGrW4mI/AAAAAAAAAs4/HiUYWlsrGMs/s1600/Screen%2BShot%2B2011-08-25%2Bat%2B3.20.09%2BPM.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 314px;" src="http://3.bp.blogspot.com/-L-h6PmG00cQ/TlauqGrW4mI/AAAAAAAAAs4/HiUYWlsrGMs/s320/Screen%2BShot%2B2011-08-25%2Bat%2B3.20.09%2BPM.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5644891221325701730" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I'll get into the details of that implementation in a later post.  For now, let's tackle some data handling.&lt;br /&gt;&lt;br /&gt;The immediate task I undertook was to refactor a simple asset price data fetcher (from Yahoo Finance, in this particular case) that I use.  I always end up hammering the Yahoo Finance server to pull down price data when I'm doing some ad-hoc analysis, and I thought I should build a simple function to stash that data away locally in a &lt;a href="http://www.sqlite.org/"&gt;sqlite&lt;/a&gt; database file.  I know there are other approaches for fetching and manipulating this data out there -- notably &lt;a href="https://github.com/wesm/pandas/blob/master/examples/finance.py"&gt;pandas&lt;/a&gt; aggregation functions, paired with the &lt;a href="https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/finance.py"&gt;matplotlib finance functions&lt;/a&gt;.  I've looked to those tools for guidance, but wanted to have a bit more functional approach, fewer (or at least different) dependencies, and SQL as an interface to the data.  I'll say up front that all the &lt;a href="https://github.com/tvaught/experimental/tree/master/portfolio_metrics"&gt;code is available&lt;/a&gt; under a BSD license via github.&lt;br /&gt;&lt;br /&gt;First, I cleaned up the old data fetcher and made it more functional (in the programming sense -- rather than object oriented).  Really, I just wanted to be able to do this:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://pastebin.com/embed_js.php?i=WfMZASQn"&gt;&lt;/script&gt;&lt;br /&gt;&lt;br /&gt;I prefer my api for this over the matplotlib api mainly because I don't want to &lt;code&gt;import datetime&lt;/code&gt;, followed by all the &lt;code&gt;startdate = datetime.datetime(2000,1,1)&lt;/code&gt; nonsense, just to get a date (pun intended).  Anyway, once I've got my data, I want to stuff it into a sqlite db file and be able to (losslessly) pull it back out into a numpy array.  This seems fairly simple because pysqlite offers conversion support for python datetime objects.  Unfortunately, I wanted dates stored as floats of seconds since the epoch, so I had to do a bit more work.  This resulted in the code:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://pastebin.com/embed_js.php?i=cuyP382k"&gt;&lt;/script&gt;&lt;br /&gt;&lt;br /&gt;These two functions provide for accurate round-tripping despite the nuances of daylight savings time treatment by numpy and sqlite.&lt;br /&gt;&lt;br /&gt;Another important point in the use of the converter function is how one determines that the type is a "datetime."  Sqlite uses &lt;code&gt;datetime&lt;/code&gt; as a designator for a built-in type -- I'm just overriding that when I register the converter function.  In order to have the proper type recognized, we need to specify the &lt;code&gt;detect_types&lt;/code&gt; argument when we instantiate our connection to the database for &lt;code&gt;INSERT&lt;/code&gt;-ing or &lt;code&gt;SELECT&lt;/code&gt;-ing.  Also, notice how I specify the type using the &lt;code&gt;as&lt;/code&gt; keyword in my &lt;code&gt;SELECT&lt;/code&gt; statement:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://pastebin.com/embed_js.php?i=vpASDHzd"&gt;&lt;/script&gt;&lt;br /&gt;&lt;br /&gt;Now, I have a nice interface for this kind of interaction with my data:&lt;br /&gt;&lt;br /&gt;&lt;script src="http://pastebin.com/embed_js.php?i=SjisExmF"&gt;&lt;/script&gt;&lt;br /&gt;&lt;br /&gt;I'm still looking into potential problems with how queries might perform where I've indexed a float column for the date.  Also, any suggestions about how to better handle the DST issue would be most welcome.  It's pretty hackish now and I'd like to avoid wading into the morass that is Python's congenital &lt;a href="http://bugs.python.org/issue2736"&gt;complex&lt;/a&gt; about &lt;a href="http://bugs.python.org/issue665194"&gt;round-tripping&lt;/a&gt; of timestamps and how to properly do &lt;a href="http://bugs.python.org/issue7584"&gt;timezones&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Next step:&lt;/em&gt;  Port some metrics code over to use the numpy datetimes rather than my manual handling of dates.  I'll be sharing this code as well as an explanation in a later post.&lt;br /&gt;&lt;br /&gt;UPDATE: I've added a handy convenience function, &lt;code&gt;price_utils.symbol_exists&lt;/code&gt; to check what data is in the sqlite database for a particular symbol.&lt;br /&gt;&lt;br /&gt;&lt;script src="http://pastebin.com/embed_js.php?i=vZzDrz4h"&gt;&lt;/script&gt;&lt;br /&gt;&lt;br /&gt;It returns a tuple of the form (num_recs, first_date, last_date).  Again, the code is &lt;a href="https://github.com/tvaught/experimental/tree/master/portfolio_metrics"&gt;available via github&lt;/a&gt;.  Enjoy!&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2076691526526730952-6313935528069643490?l=travisvaught.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://travisvaught.blogspot.com/feeds/6313935528069643490/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://travisvaught.blogspot.com/2011/08/numpys-new-datetime-support-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/6313935528069643490'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/6313935528069643490'/><link rel='alternate' type='text/html' href='http://travisvaught.blogspot.com/2011/08/numpys-new-datetime-support-and.html' title='NumPy&apos;s New datetime Support and a Financial Database Roundtrip'/><author><name>Travis</name><uri>http://www.blogger.com/profile/00720286668168584326</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://2.bp.blogspot.com/_3SMSqEHAuEQ/SnCHvdf6zII/AAAAAAAAAOc/FJxkHVZ0fvo/S220/IMG_8035.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-L-h6PmG00cQ/TlauqGrW4mI/AAAAAAAAAs4/HiUYWlsrGMs/s72-c/Screen%2BShot%2B2011-08-25%2Bat%2B3.20.09%2BPM.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2076691526526730952.post-5766155647450894140</id><published>2010-03-03T16:57:00.051-06:00</published><updated>2010-03-09T18:19:28.956-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='travel'/><title type='text'>Culture Whiplash: India &amp; Singapore Trip</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_3SMSqEHAuEQ/S5Uu0vUM9TI/AAAAAAAAATc/HeNnc4adTwU/s1600-h/100_0990.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_3SMSqEHAuEQ/S5Uu0vUM9TI/AAAAAAAAATc/HeNnc4adTwU/s320/100_0990.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446310807963759922" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I'm a few weeks back from a 1 week around-the-world trip to India and Singapore.  I travelled with an old friend to visit some of his friends and coworkers in India.  The Singapore trip was business related and very short.  Like everyone, I had my preconceived notions of India.  I must say, I was at turns, surprised, impressed and bewildered by what I saw.  I'll recount a few things here -- mostly to aid my own remembering&lt;sup&gt;1&lt;/sup&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;India&lt;/h3&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_3SMSqEHAuEQ/S5U8lPIVc6I/AAAAAAAAAT8/-hquDlY3j6I/s1600-h/100_0924+(1).jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_3SMSqEHAuEQ/S5U8lPIVc6I/AAAAAAAAAT8/-hquDlY3j6I/s320/100_0924+(1).jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446325934788801442" /&gt;&lt;/a&gt;&lt;br /&gt;Poverty in India is ubiquitous &lt;em&gt;and&lt;/em&gt; acute.  While the most heartbreaking cases are the destitute beggars, orphans, and widows who are starving "out in the open," -- the real tragedy is that vast areas of rural India are dotted with villages wherein the majority of people are living &lt;a href="http://www.thefreedictionary.com/hand-to-mouth"&gt;hand to mouth&lt;/a&gt;.  These villages are in a bizarre state of pre-industrialization.  Most have a common well from which people haul water with a cell tower in sight and a cell phone in most pockets.  I found myself asking why a generous, resourceful Indian culture allows these backward conditions to persist.  My initial impulse to assume that a Western template of development offers a solution is, on further consideration, incorrect.  Perhaps the "solution" to India's problems will never come from Westernization.  Western patterns of infrastructure can serve as a distant example only.  For instance, adding a million wastebaskets around India will not solve India's garbage problem (every public waste can I saw was empty, with litter lining the streets).  The big, obvious problems that I witnessed -- &lt;a href="http://en.wikipedia.org/wiki/Water_supply_and_sanitation_in_India"&gt;sanitation&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Malnutrition_in_India"&gt;nutrition&lt;/a&gt; (India contributes to over half the world total of child deaths each year, primarily due to malnutrition) -- are so rooted in cultural norms that they call for uniquely Indian solutions.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_3SMSqEHAuEQ/S5biSI7p5kI/AAAAAAAAAVI/1up43naRRyA/s1600-h/100_0963.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 150px; height: 200px;" src="http://2.bp.blogspot.com/_3SMSqEHAuEQ/S5biSI7p5kI/AAAAAAAAAVI/1up43naRRyA/s200/100_0963.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446789600614409794" /&gt;&lt;/a&gt;&lt;br /&gt;To offer another example, in the &lt;a href="http://en.wikipedia.org/wiki/Other_Backward_Class"&gt;backward&lt;/a&gt; villages through which we travelled, women who have lost their husbands are either &lt;a href="http://www.cnn.com/2007/WORLD/asiapcf/07/05/damon.india.widows/index.html"&gt;banished from their homes&lt;/a&gt;, or not allowed outside the home because of superstition (seeing a widow is a "bad omen").  The Western solution to this would be legislation mandating various agencies to provide a social safety net, education initiatives, etc., etc.  The scale of this &lt;em&gt;one&lt;/em&gt; problem is, by itself, enough (given Western approaches) to completely overwhelm the coffers of any Indian state.  It seems hopeless.&lt;br /&gt;&lt;br /&gt;I &lt;em&gt;am&lt;/em&gt; encouraged by the work of my new friends Paul and Grace Moses.  A part of &lt;a href="http://www.sangitacharitabletrust.com/community.php"&gt;their ministry&lt;/a&gt; (in addition to a children's home) is to serve food to widows (over 250 of them currently) once a month.  The rations are around US$4.00 per widow and consist of a kilo each of rice, sugar and &lt;a href="http://en.wikipedia.org/wiki/Dal"&gt;dal&lt;/a&gt; (lentils) as well as some vitamins.  Not only does this meet the immediate need of feeding the widows, it provides a mechanism by which they may be welcomed back into their families.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_3SMSqEHAuEQ/S5VI3epv3QI/AAAAAAAAAUE/efOrCnBVe9E/s1600-h/graceandwidow.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 213px;" src="http://4.bp.blogspot.com/_3SMSqEHAuEQ/S5VI3epv3QI/AAAAAAAAAUE/efOrCnBVe9E/s320/graceandwidow.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446339442332654850" /&gt;&lt;/a&gt;&lt;br /&gt;Now, to my Western sensibilities (and sense of justice), the idea of reintegrating a widow into a family strictly because of what she has to &lt;em&gt;offer&lt;/em&gt; is unseemly at best.  However, this program works with all the leverage available to it.  My sense is that India will need many more indirect steps such as this -- particularly to development at the village level.  Applying traditional Western thought to these problems is like saying that "in order to solve a math problem, we must first build a computer."&lt;br /&gt;&lt;br /&gt;Anyway, that's probably quite enough opinion for one post.&lt;br /&gt;&lt;br /&gt;We visited a beach and resort town on the southeast coast in the state of Tamil Nadu.  The town of Mamallapuram (also referred to as &lt;a href="http://en.wikipedia.org/wiki/Mahabalipuram"&gt;Mahabalipuram&lt;/a&gt;) has a couple of interesting attractions: a beachside Hindu temple and a &lt;a href="http://en.wikipedia.org/wiki/Batholith"&gt;batholith&lt;/a&gt;/&lt;a href="http://en.wikipedia.org/wiki/Stock_(geology)"&gt;stock&lt;/a&gt; that has been carved with some &lt;a href="http://en.wikipedia.org/wiki/File:Mamallapuram_rock_carvings.jpg"&gt;fantastic art&lt;/a&gt;.  Off the coast are some undersea remains of construction that are the subject of &lt;a href="http://en.wikipedia.org/wiki/Seven_Pagodas_of_Mahabalipuram"&gt;much speculation&lt;/a&gt;.  The town was hit by a 30 ft wave in the 2004 tsunami, though very little of the tsunami's affects are visible.  We were told that there was a large migration from the coastal areas since the tsunami, but its hard for me to discern movement within a population like India's -- people are everywhere.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_3SMSqEHAuEQ/S5Uwcr5osYI/AAAAAAAAATk/na5-S4n5dNI/s1600-h/100_1001.jpg"&gt;&lt;img style="margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_3SMSqEHAuEQ/S5Uwcr5osYI/AAAAAAAAATk/na5-S4n5dNI/s320/100_1001.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446312593753420162" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_3SMSqEHAuEQ/S5U76m9ZSxI/AAAAAAAAAT0/bCVxHHmVUkI/s1600-h/100_0919.jpg"&gt;&lt;img style="margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_3SMSqEHAuEQ/S5U76m9ZSxI/AAAAAAAAAT0/bCVxHHmVUkI/s320/100_0919.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446325202450991890" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_3SMSqEHAuEQ/S5Vx050n74I/AAAAAAAAAUM/GSMEFSY_E64/s1600-h/100_0953.jpg"&gt;&lt;img style="margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_3SMSqEHAuEQ/S5Vx050n74I/AAAAAAAAAUM/GSMEFSY_E64/s320/100_0953.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446384478063161218" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_3SMSqEHAuEQ/S5bd_kjxgdI/AAAAAAAAAUk/s2LQc6vPWyQ/s1600-h/100_1016.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_3SMSqEHAuEQ/S5bd_kjxgdI/AAAAAAAAAUk/s2LQc6vPWyQ/s320/100_1016.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446784883566412242" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_3SMSqEHAuEQ/S5beSmoZ4YI/AAAAAAAAAU0/IvMR5B70fIw/s1600-h/100_1130.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_3SMSqEHAuEQ/S5beSmoZ4YI/AAAAAAAAAU0/IvMR5B70fIw/s320/100_1130.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446785210540220802" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_3SMSqEHAuEQ/S5beHeBiBOI/AAAAAAAAAUs/PFKJ0ntZ3Vg/s1600-h/100_1063.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_3SMSqEHAuEQ/S5beHeBiBOI/AAAAAAAAAUs/PFKJ0ntZ3Vg/s320/100_1063.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446785019251131618" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Singapore&lt;/h3&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_3SMSqEHAuEQ/S5bdy8smZJI/AAAAAAAAAUc/M6UuuUEaiuc/s1600-h/100_1164.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_3SMSqEHAuEQ/S5bdy8smZJI/AAAAAAAAAUc/M6UuuUEaiuc/s320/100_1164.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446784666707584146" /&gt;&lt;/a&gt;&lt;br /&gt;We departed from the Chennai Airport late at night and arrived in Singapore early the next morning.  That overnight flight may as well have been a trip to another planet.  Singapore is culturally opposite India in many ways.  The economics are vastly different, of course, and attitudes and the general societal posture are also markedly different.  For example, upon disembarking at the airport in Chennai, India there was a row of machine-gun armed officers lining the exit breezeway to the parking lots, with filth and waste behind every column.  Conversely, in Singapore the only police presence (besides a few cameras dotting the train terminals) was the lone police car we saw driving through downtown -- and the streets, sidewalks, terminals and stations were spotless.  I'm sure money has a lot to do with this difference, but I've never seen a more glaring example of the &lt;a href="http://en.wikipedia.org/wiki/Broken_windows"&gt;broken window theory&lt;/a&gt;.   Of course, Singapore had the highest per-capita &lt;a href="http://en.wikipedia.org/wiki/Capital_punishment_in_Singapore"&gt;execution rate&lt;/a&gt; in the world between 1994 and 1999.  Twenty four of the 136 folks executed during that time were foreign nationals.&lt;br /&gt;&lt;br /&gt;That said, Singapore is a beautiful place.  The climate was warm, but pleasant.  There is quite a bit to do (particularly if you like to shop) with tons of development taking place -- even in the midst of a global recession.  It's hard to imagine that many of the development projects are in the billions of dollars (notably, a casino) while malnutrition is rampant in the same hemisphere.  Crazy stuff.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_3SMSqEHAuEQ/S5bebp_duEI/AAAAAAAAAU8/VDvvk62gYdw/s1600-h/IMG_1262.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_3SMSqEHAuEQ/S5bebp_duEI/AAAAAAAAAU8/VDvvk62gYdw/s320/IMG_1262.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5446785366061070402" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This was the trip of a lifetime -- and one I hope to repeat soon.  I'm anxious to see some of the friends I met along the way again.&lt;br /&gt;&lt;br /&gt;&lt;sup&gt;1&lt;/sup&gt; I fully expect these first impressions of India to be corrected and refined as I see and learn more.  So, as G.K. Chesterton once said, "There is nothing the matter with Americans except their ideals. The real American is all right; it is the ideal American who is all wrong."&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2076691526526730952-5766155647450894140?l=travisvaught.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://travisvaught.blogspot.com/feeds/5766155647450894140/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://travisvaught.blogspot.com/2010/03/culture-whiplash-india-singapore-trip.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/5766155647450894140'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/5766155647450894140'/><link rel='alternate' type='text/html' href='http://travisvaught.blogspot.com/2010/03/culture-whiplash-india-singapore-trip.html' title='Culture Whiplash: India &amp; Singapore Trip'/><author><name>Travis</name><uri>http://www.blogger.com/profile/00720286668168584326</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://2.bp.blogspot.com/_3SMSqEHAuEQ/SnCHvdf6zII/AAAAAAAAAOc/FJxkHVZ0fvo/S220/IMG_8035.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_3SMSqEHAuEQ/S5Uu0vUM9TI/AAAAAAAAATc/HeNnc4adTwU/s72-c/100_0990.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2076691526526730952.post-3216578384197618756</id><published>2010-02-17T06:36:00.007-06:00</published><updated>2010-02-17T15:00:07.587-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Python Jobs Trend</title><content type='html'>As Matt Asay &lt;a href="ttp://news.cnet.com/8301-13505_3-10453213-16.html?tag=mncol;title"&gt;pointed out&lt;/a&gt;, there seems to be a real uptick in jobs in the Python space.  Here's the chart:&lt;br /&gt;&lt;br /&gt;&lt;div style="width:540px"&gt;&lt;br /&gt;&lt;a href="http://www.indeed.com/jobtrends?q=Java%2C+PHP%2C+Perl%2C+.Net%2C+Python&amp;relative=1&amp;relative=1" title="Java, PHP, Perl, .Net, Python Job Trends"&gt;&lt;br /&gt;&lt;img width="540" height="300" src="http://www.indeed.com/trendgraph/jobgraph.png?q=Java%2C+PHP%2C+Perl%2C+.Net%2C+Python&amp;relative=1" border="0" alt="Java, PHP, Perl, .Net, Python Job Trends graph"&gt;&lt;br /&gt;&lt;/a&gt;&lt;br /&gt;&lt;table width="100%" cellpadding="6" cellspacing="0" border="0" style="font-size:80%"&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;a href="http://www.indeed.com/jobtrends?q=Java%2C+PHP%2C+Perl%2C+.Net%2C+Python&amp;relative=1&amp;relative=1"&gt;Java, PHP, Perl, .Net, Python Job Trends&lt;/a&gt;&lt;/td&gt;&lt;br /&gt;&lt;td align="right"&gt;&lt;a href="http://www.indeed.com/q-Java-jobs.html"&gt;Java jobs&lt;/a&gt; - &lt;a href="http://www.indeed.com/q-PHP-jobs.html"&gt;PHP jobs&lt;/a&gt; - &lt;a href="http://www.indeed.com/q-Perl-jobs.html"&gt;Perl jobs&lt;/a&gt; - &lt;a href="http://www.indeed.com/q-.Net-jobs.html"&gt;.Net jobs&lt;/a&gt; - &lt;a href="http://www.indeed.com/q-Python-jobs.html"&gt;Python jobs&lt;/a&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;You can &lt;a href="http://www.indeed.com/jobtrends?q=Java,+PHP,+Perl,+.Net,+Python&amp;relative=1"&gt;play with the chart here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;While the raw number of jobs is still much lower, the rates of change are encouraging for open languages.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2076691526526730952-3216578384197618756?l=travisvaught.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://travisvaught.blogspot.com/feeds/3216578384197618756/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://travisvaught.blogspot.com/2010/02/python-jobs-trend.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/3216578384197618756'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/3216578384197618756'/><link rel='alternate' type='text/html' href='http://travisvaught.blogspot.com/2010/02/python-jobs-trend.html' title='Python Jobs Trend'/><author><name>Travis</name><uri>http://www.blogger.com/profile/00720286668168584326</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://2.bp.blogspot.com/_3SMSqEHAuEQ/SnCHvdf6zII/AAAAAAAAAOc/FJxkHVZ0fvo/S220/IMG_8035.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2076691526526730952.post-9195593657622296331</id><published>2009-10-06T12:27:00.013-05:00</published><updated>2009-10-07T00:02:47.437-05:00</updated><title type='text'>TDD in Python: P&amp;L Statement - Step 2</title><content type='html'>As a continuation of my TDD experiment, I've implemented a bit more functionality toward a P&amp;L statement.  As usual, I've &lt;a href="http://github.com/tvaught/experimental/tree/master/trade_queue"&gt;posted all the code&lt;/a&gt; to github.  This exercise implements the queueing I listed as a need in my &lt;a href="http://travisvaught.blogspot.com/2009/09/p-statement-step-1-simple-position.html"&gt;first post&lt;/a&gt; on this subject.  So, for a "story problem":&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Eddie buys 1000 shares of AAPL at 10:30am for 185.25 and later, at 11:00am he buys another 1500 shares for 184.00.  As he unrolls his holdings, Eddie sells 1500 shares for 186.00.  What is Eddie's realized gain/loss?  What's the cost basis for Eddie's current holdings?&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;For this post, I'd like to take a first step in solving my story problem by defining my queue a bit better.  The real answer to the problem above is "it depends on whether you use fifo or lifo queueing."    I've conceived a &lt;code&gt;Holding&lt;/code&gt; object to serve as my queue for any positions I've entered for a particular instrument (as uniquely identified by its symbol).  So, let's define the behavior we expect out of a fifo-ordered &lt;code&gt;Holding&lt;/code&gt; queue in the form of a test:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;def portfolio_holding_fifo_test():&lt;br /&gt;    """ test of 'fifo' and 'lifo' queuing by adding and removing&lt;br /&gt;        a position.&lt;br /&gt;    """&lt;br /&gt;    p1 = position.Position(symbol="AAPL", qty=1000, price=185.25,&lt;br /&gt;                          multiplier=1., fee=7.0, total_amt=185250.,&lt;br /&gt;                          trans_date=1053605468.54)&lt;br /&gt;    p2 = position.Position(symbol="AAPL", qty=1500, price=184.00,&lt;br /&gt;                          multiplier=1., fee=7.0, total_amt=276000.,&lt;br /&gt;                          trans_date=1054202245.63)&lt;br /&gt;    p3 = position.Position(symbol="AAPL", qty=-1500, price=186.00,&lt;br /&gt;                          multiplier=1., fee=7.0, total_amt=279000.,&lt;br /&gt;                          trans_date=1055902486.22)&lt;br /&gt;                          &lt;br /&gt;    h = portfolio.Holding()&lt;br /&gt;    &lt;br /&gt;    h.add_to(p1)&lt;br /&gt;    h.add_to(p2)&lt;br /&gt;    &lt;br /&gt;    h.remove_from(p3, order='fifo')&lt;br /&gt;    &lt;br /&gt;    assert h.qty==1000&lt;br /&gt;    assert len(h.positions)==1&lt;br /&gt;    # simple check to make sure the position that we expect is left over...&lt;br /&gt;    p = h.positions[0]&lt;br /&gt;    assert p.price==184.00&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see from lines 5-13 we use our &lt;a href="http://github.com/tvaught/experimental/blob/master/trade_queue/position.py"&gt;earlier Position object&lt;/a&gt; to define the positions that we're holding or removing.  I then introduce the notion of a holding object which I can &lt;code&gt;add_to&lt;/code&gt; or &lt;code&gt;remove_from&lt;/code&gt;.  My assertions at the end of my test are some not-very-comprehensive checks to see if the queue behaves as I would expect ... the first position is drawn from when removing the final position, then any remaining shares are drawn from the second position.&lt;br /&gt;&lt;br /&gt;Since I expect I'll need to generalize my queuing, I'll write a similar test with 'lifo' ordering:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;def portfolio_holding_lifo_test():&lt;br /&gt;    """ test of 'lifo' queuing by adding and removing&lt;br /&gt;        a position.&lt;br /&gt;    """&lt;br /&gt;    p1 = position.Position(symbol="AAPL", qty=1000, price=185.25,&lt;br /&gt;                          multiplier=1., fee=7.0, total_amt=185250.,&lt;br /&gt;                          trans_date=1053605468.54)&lt;br /&gt;    p2 = position.Position(symbol="AAPL", qty=1500, price=184.00,&lt;br /&gt;                          multiplier=1., fee=7.0, total_amt=276000.,&lt;br /&gt;                          trans_date=1054202245.63)&lt;br /&gt;    p3 = position.Position(symbol="AAPL", qty=-1500, price=186.00,&lt;br /&gt;                          multiplier=1., fee=7.0, total_amt=279000.,&lt;br /&gt;                          trans_date=1055902486.22)&lt;br /&gt;                          &lt;br /&gt;    h = portfolio.Holding()&lt;br /&gt;    &lt;br /&gt;    h.add_to(p1)&lt;br /&gt;    h.add_to(p2)&lt;br /&gt;    &lt;br /&gt;    h.remove_from(p3, order='lifo')&lt;br /&gt;&lt;br /&gt;    assert h.qty==1000&lt;br /&gt;    assert len(h.positions)==1&lt;br /&gt;    # simple check to make sure the position that we expect is left over...&lt;br /&gt;    p = h.positions[0]&lt;br /&gt;    assert p.price==185.25&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, I've written my tests first -- now for the code.  I create my Holding class and put it in a file called &lt;code&gt;portfolio.py&lt;/code&gt;.  Setting up the class and creating my add_to method was a cinch:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;# Enthought imports&lt;br /&gt;from enthought.traits.api import (HasTraits, Float, Instance, List)&lt;br /&gt;&lt;br /&gt;# Local imports&lt;br /&gt;from position import Position&lt;br /&gt;&lt;br /&gt;class Holding(HasTraits):&lt;br /&gt;    """ Queue for held positions in the same security (as identified&lt;br /&gt;        by symbol).  The removal of entries are handled in a 'fifo'&lt;br /&gt;        or a 'lifo' order, depending on the order argument of the&lt;br /&gt;        remove_from method.&lt;br /&gt;    """&lt;br /&gt;&lt;br /&gt;    # Total quantity for a particular holding&lt;br /&gt;    qty = Float&lt;br /&gt;    &lt;br /&gt;    # List of positions making up the holding&lt;br /&gt;    positions = List(Instance(Position))&lt;br /&gt;    &lt;br /&gt;    def add_to(self, position):&lt;br /&gt;        self.qty += position.qty&lt;br /&gt;        self.positions.append(position)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, I've again used &lt;a href="http://code.enthought.com/projects/traits/"&gt;Traits&lt;/a&gt; to make my life easier.  In this case I've got a simple &lt;code&gt;qty&lt;/code&gt; Float value which I intend to use for ease in checking the total quantity held in my Holding object (as well as a checksum).  I also have defined a List of positions as another Trait of my class.  From there, the &lt;code&gt;add_to&lt;/code&gt; method is pretty much the obvious implementation.&lt;br /&gt;&lt;br /&gt;The hard part is in the &lt;code&gt;remove_from&lt;/code&gt;, which actually implements the various behavior of the queue ordering.  I've punted (for now) on my original goal of a 'wifo' (worst-in-first-out) queue order, as I suspect I'll need to refactor my position class to handle different sorting.  The important bits of the code are in the handling of a full position, a partial position, or multiple positions depending of the quantity to be removed.  Rather than reproducing all the code here, I'll just &lt;a href="http://github.com/tvaught/experimental/blob/master/trade_queue/portfolio.py"&gt;provide the link&lt;/a&gt;.  I use recursion in the case of multiple removals, otherwise it's pretty easy to follow.&lt;br /&gt;&lt;br /&gt;This was a fairly simple step, yet I feel much closer to my portfolio/statement goals -- and I feel like I can "turn my back" to some extent on the code I've written so far.  That is, it is reasonably tested code that meets the api I've defined by my tests.&lt;br /&gt;&lt;br /&gt;Some TDD observations:&lt;br /&gt;1. It is getting a little easier to think in terms of tests first as I've done this second exercise.&lt;br /&gt;2. I &lt;em&gt;did&lt;/em&gt; write some additional tests to try to get at the edges of the functionality I was looking for, and to provide more comprehensive regression coverage.&lt;br /&gt;3. Small tests force you into a good habit of writing small chunks of code that work, heightening the impression of progress.&lt;br /&gt;&lt;br /&gt;Next up: 1) aggregating the holdings into a real portfolio and 2) introducing a method of logging activity and reporting performance in the form of a statement.&lt;br /&gt;&lt;br /&gt;As always, comments or corrections are welcome!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2076691526526730952-9195593657622296331?l=travisvaught.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://travisvaught.blogspot.com/feeds/9195593657622296331/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://travisvaught.blogspot.com/2009/10/tdd-in-python-p-statement-step-2.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/9195593657622296331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/9195593657622296331'/><link rel='alternate' type='text/html' href='http://travisvaught.blogspot.com/2009/10/tdd-in-python-p-statement-step-2.html' title='TDD in Python: P&amp;L Statement - Step 2'/><author><name>Travis</name><uri>http://www.blogger.com/profile/00720286668168584326</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://2.bp.blogspot.com/_3SMSqEHAuEQ/SnCHvdf6zII/AAAAAAAAAOc/FJxkHVZ0fvo/S220/IMG_8035.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2076691526526730952.post-395846230879882291</id><published>2009-09-29T09:37:00.033-05:00</published><updated>2009-09-29T15:35:28.927-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='trading'/><category scheme='http://www.blogger.com/atom/ns#' term='test driven development'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='traits'/><title type='text'>Test Driven Python Development Experiment: P&amp;L Statement - Step 1</title><content type='html'>I began trading equities in my personal portfolio earlier this Spring using an account with &lt;a href="http://www.interactivebrokers.com"&gt;Interactive Brokers&lt;/a&gt; (whom I'd recommend for their nice tools, good access to products, and &lt;a href="http://www.interactivebrokers.com/en/p.php?f=programInterface&amp;ib_entity=llc"&gt;published APIs&lt;/a&gt;).  One of the issues I've had with them, however, is the lack of clarity in a daily &lt;a href="http://www.investopedia.com/terms/p/plstatement.asp"&gt;P&amp;L statement&lt;/a&gt;.  Without bogging down in too many details, it boils down to the fact that I'd like to see a few things that are not provided out-of-the box by Interactive Brokers:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Realized gain rolled up for each day.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;An intraday, calculated P&amp;L in the event that I wish to use it as part of an automated trading system.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A choice between &lt;a href="http://www.investopedia.com/terms/f/fifo.asp"&gt;FIFO&lt;/a&gt;, &lt;a href="http://www.investopedia.com/terms/l/lifo.asp"&gt;LIFO&lt;/a&gt; and what I call WIFO (worst-in-first-out, a more accurate and better-rhyming name for &lt;a href="http://www.investopedia.com/terms/h/hifo.asp"&gt;HIFO&lt;/a&gt;) handling of trades.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Developing these capabilities seems like an excellent exercise for me to experiment with &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development"&gt;Test Driven Development&lt;/a&gt; (TDD) and share my experiences with others, so consider this a test for TDD using a below-average developer with simple needs.  The &lt;a href="http://github.com/tvaught/experimental/tree/master/trade_queue"&gt;current state of the code&lt;/a&gt; is available at github.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Position Object&lt;/h3&gt;&lt;br /&gt;The first thing needed is a data structure to hold information about each position.  The data populating this data structure will come from a text log of transactions, or, potentially, a database with transaction records.  Interactive Brokers provides several formats of text output.  I chose to use a text file provided in a &lt;a href="http://www.armencomp.com/tradelog/"&gt;TradeLog&lt;/a&gt; format.  Looking at a few lines of a typical log gives an idea of the type and format of data provided:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ACCOUNT_INFORMATION&lt;br /&gt;ACT_INF|UXXXXXXX|Test Company|Advisor Client|1111 Main Street, San Antonio TX 78201 United States&lt;br /&gt;&lt;br /&gt;OPTION_TRANSACTIONS&lt;br /&gt;OPT_TRD|305665068|APVGG|AAPL JUL09 135 C|ISE|BUYTOOPEN|O|20090512|10:22:55|USD|23.00|100.00|5.55|12765.00|-16.10|1.00&lt;br /&gt;OPT_TRD|305668016|APVGG|AAPL JUL09 135 C|NASDAQOM|SELLTOCLOSE|C|20090512|10:24:59|USD|-23.00|100.00|5.61|-12903.00|-10.35|1.00&lt;br /&gt;OPT_TRD|305681552|APVGG|AAPL JUL09 135 C|ISE|BUYTOOPEN|O|20090512|10:36:59|USD|46.00|100.00|5.30|24380.00|-32.20|1.00&lt;br /&gt;OPT_TRD|305721409|APVGG|AAPL JUL09 135 C|ISE|BUYTOOPEN|O|20090512|11:25:29|USD|47.00|100.00|5.15|24205.00|-32.90|1.00&lt;br /&gt;OPT_TRD|305767844|APVGG|AAPL JUL09 135 C|ISE|BUYTOOPEN|O|20090512|12:30:38|USD|23.00|100.00|4.70|10810.00|-16.10|1.00&lt;br /&gt;OPT_TRD|306301447|APVGG|AAPL JUL09 135 C|NASDAQOM|BUYTOOPEN|O|20090513|14:19:58|USD|30.00|100.00|3.19|9570.00|-13.50|1.00&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The optimal way to put this information into a data structure in python would probably be a to use a numpy structured array.  However, since performance is not an issue (at this time), and I'd like to use this problem as a TDD test for myself, I'll forgo the numpy approach and use more of a brute-force, object-oriented method.&lt;br /&gt;&lt;br /&gt;If I were to codify some requirements for my data structure by writing a test, it might look something like this:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;&lt;br /&gt;import position&lt;br /&gt;&lt;br /&gt;def position_test():&lt;br /&gt;    """ Dead simple test to see if I can store data in my position object&lt;br /&gt;        and get it back out&lt;br /&gt;    """&lt;br /&gt;    p = position.Position(symbol="AAPL", qty=1000, price=185.25,&lt;br /&gt;                          multiplier=1., fee=7.0, total_amt=185250.,&lt;br /&gt;                          trans_date=1053605468.54)&lt;br /&gt;                          &lt;br /&gt;    assert p.price==185.25&lt;br /&gt;    assert p.description==""&lt;br /&gt;    assert p.display_date=="05/22/2003"&lt;br /&gt;    assert p.display_time=="08:11:08"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;OK, so there are several things implicit in this test (don't worry, I'll break all those asserts into different tests before going forward).  One obvious point is that I seem to require some fairly sophisticated handling of datetimes and default values.  Other than that, we have a big pile of keyword arguments and a bunch of attributes in a fairly brain-dead object.  The conventional approach is to build a giant __init__ with a bunch of lines like this nonsense:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;    if not description is None:&lt;br /&gt;        self.description = description&lt;br /&gt;    else:&lt;br /&gt;        self.description = 0.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Frankly, we deserve better than to waste our lives on silly boilerplate code for initialization, validation and defaults.  This is where &lt;a href="http://code.enthought.com/projects/traits/"&gt;Traits&lt;/a&gt; comes to the rescue (there's a &lt;a href="http://code.enthought.com/projects/traits/docs/html/tutorials/traits_ui_scientific_app.html"&gt;nice tutorial here&lt;/a&gt;).  I'll not go into all the details here, but you'll see how how traits allows us to have much cleaner code as I progress.&lt;br /&gt;&lt;br /&gt;As a very simple first test, let's take our first assert and make a stand-alone test:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;def position_attribute_test():&lt;br /&gt;    """ Dead simple test to see if I can store data in my position object&lt;br /&gt;        and get it back out&lt;br /&gt;    """&lt;br /&gt;    p = position.Position(symbol="AAPL", qty=1000, price=185.25,&lt;br /&gt;                          multiplier=1., fee=7.0, total_amt=185250.,&lt;br /&gt;                          trans_date=1053605468.54)&lt;br /&gt;                          &lt;br /&gt;    assert p.price==185.25&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We can solve it with the following code, which includes all the fields (attributes/traits) I think I'll need:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;from enthought.traits.api import (HasTraits, Enum, Float, Int,&lt;br /&gt;                                  Str)&lt;br /&gt;&lt;br /&gt;class Position(HasTraits):&lt;br /&gt;    """ Simple object to act as a data structure for a position &lt;br /&gt;    &lt;br /&gt;        While all attributes (traits) are optional, classes that contain or&lt;br /&gt;        collect instances of the Position class will probably require the following:&lt;br /&gt;        symbol, trans_date, qty, price, total_amt&lt;br /&gt;    &lt;br /&gt;    """&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    side = Enum("BUYTOOPEN", ["SELLTOCLOS", "BUYTOOPEN", "SELLTOOPEN", "BUYTOCLOSE"])&lt;br /&gt;    symbol = Str&lt;br /&gt;    id = Int&lt;br /&gt;    description = Str&lt;br /&gt;    trans_date = Float&lt;br /&gt;    qty = Float&lt;br /&gt;    price = Float&lt;br /&gt;    multiplier = Float(1.0)&lt;br /&gt;    fee = Float&lt;br /&gt;    exchange_rate = Float(1.0)&lt;br /&gt;    currency = Str("USD")&lt;br /&gt;    total_amt = Float&lt;br /&gt;    filled = Str&lt;br /&gt;    exchange = Str&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I now can throw my test code into a directory called "test" adjacent to this module (which I named position.py) and I use the excellent &lt;a href="http://somethingaboutorange.com/mrl/projects/nose/0.11.1/"&gt;nosetest&lt;/a&gt; tool to harvest and run any tests I've got.  It passes! ... but that was actually not much of a challenge, so let's look at another test function:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;def position_initialization_test():&lt;br /&gt;    """ Test to see if I can handle fields for which I provide no data.&lt;br /&gt;    """&lt;br /&gt;    p = position.Position(symbol="AAPL", qty=1000, price=185.25,&lt;br /&gt;                          multiplier=1., fee=7.0, total_amt=185250.,&lt;br /&gt;                          trans_date=1053605468.54)&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    assert p.description==""&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;... this is similarly a soft pitch over the middle of the plate.  Traits has already solved this for us.  The test passes with no change to the code.&lt;br /&gt;&lt;br /&gt;How about something a little more challenging:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;def position_dates_test():&lt;br /&gt;    """ Test to see if I'm handling dates correctly&lt;br /&gt;    """&lt;br /&gt;    p = position.Position(symbol="AAPL", qty=1000, price=185.25,&lt;br /&gt;                          multiplier=1., fee=7.0, total_amt=185250.,&lt;br /&gt;                          trans_date=1053605468.54)&lt;br /&gt;    &lt;br /&gt;    assert p.date_display=="05/22/2003"&lt;br /&gt;    assert p.time_display=="08:11:08"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So now I have to actually write some code.  I won't go into all the painful issues with Python date handling, I'll just provide a link to the &lt;a href="http://github.com/tvaught/experimental/blob/master/trade_queue/date_util.py"&gt;date utility code&lt;/a&gt; that I wrote to help smooth things over.&lt;br /&gt;&lt;br /&gt;I do want to step back and write a test that defines a need for date handling that is currently a bit broken (at least in Python 2.5) -- converting a python datetime to a timestamp and a timestamp to a python datetime:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;from date_util import dt_from_timestamp, dt_to_timestamp&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def date_util_test():&lt;br /&gt;    """ Simple test of correctly transforming a timestamp to a python datetime,&lt;br /&gt;        and back to a timestamp&lt;br /&gt;    """&lt;br /&gt;    &lt;br /&gt;    # test a not-very-random sequence of times&lt;br /&gt;    for ts in range(0, 1250000000, 321321):&lt;br /&gt;        # simply see if we can round-trip the timestamp and get the same result&lt;br /&gt;        dt = dt_from_timestamp(ts)&lt;br /&gt;        ts2 = int(dt_to_timestamp(dt))&lt;br /&gt;        assert ts2 == ts&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Picking back up with my &lt;code&gt;position_dates_test&lt;/code&gt; ... the instructive part of what I need to do here is in using a &lt;a href="https://svn.enthought.com/enthought/wiki/PropertyDependsOn"&gt;Property Trait&lt;/a&gt; as a good human interface for my trans_date.  The trans_date value is actually a float value indicating seconds (and fractional seconds) since the epoch (which happens to fall in the same year that we put the first man on the moon, the summer of love, Woodstock and, notably, the year of my birth -- 1969!).  While the current tally of seconds since 1969 may be at the front of &lt;em&gt;my&lt;/em&gt; mind, it's not a particularly good way to represent dates for most people.  In fact, most GUIs that handle datetime entries separate the date and time.  This makes sense -- we're used to interacting with the two with separate pieces of hardware (a calendar and a clock).  The date and time properties are defined after the other traits in my class like so:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;    date_display = Property(Regex(value='11/17/1969',&lt;br /&gt;                                  regex='\d\d[/]\d\d[/]\d\d\d\d'),&lt;br /&gt;                                  depends_on='trans_date')&lt;br /&gt;    time_display = Property(Regex(value='12:01:01',&lt;br /&gt;                                  regex='\d\d[:]\d\d[:]\d\d'),&lt;br /&gt;                                  depends_on='trans_date')&lt;br /&gt;&lt;br /&gt;    ###################################&lt;br /&gt;    # Property methods&lt;br /&gt;    def _get_date_display(self):&lt;br /&gt;        return dt_from_timestamp(self.trans_date, tz=Eastern).strftime("%m/%d/%Y")&lt;br /&gt;        &lt;br /&gt;    def _set_date_display(self, val):&lt;br /&gt;        tm = self._get_time_display()&lt;br /&gt;        trans_date = datetime.strptime(val+tm, "%m/%d/%Y%H:%M:%S" )&lt;br /&gt;        trans_date = trans_date.replace(tzinfo=Eastern)&lt;br /&gt;        self.trans_date = dt_to_timestamp(trans_date)&lt;br /&gt;        return &lt;br /&gt;        &lt;br /&gt;    def _get_time_display(self):&lt;br /&gt;        t = dt_from_timestamp(self.trans_date, tz=Eastern).strftime("%H:%M:%S")&lt;br /&gt;        return t&lt;br /&gt;        &lt;br /&gt;    def _set_time_display(self, val):&lt;br /&gt;        trans_time = datetime.strptime(self._get_date_display()+val, "%m/%d/%Y%H:%M:%S")&lt;br /&gt;        trans_time = trans_time.replace(tzinfo=Eastern)&lt;br /&gt;        self.trans_date = dt_to_timestamp(trans_time)&lt;br /&gt;        return&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The getter and setter methods behave just as you would expect, and the whole thing provides a meaningful interface to the timestamp stored in my trans_date trait.  There are some imports that I don't show here for brevity, but it's fairly clean code considering what it does.  The best part of all -- my test passes!&lt;br /&gt;&lt;br /&gt;Finally, I want to try another test, because I know I'll need to sort my Position objects within a collection.  Here's a simple test function:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;def position_sort_test():&lt;br /&gt;    """ Test to see if I can collect and sort these properly.&lt;br /&gt;        The objective is to have the objects sort by the trans_date&lt;br /&gt;        trait.&lt;br /&gt;    """&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    p0 = position.Position(id=102, symbol="AAPL", qty=1000, price=185.25,&lt;br /&gt;                           multiplier=1., fee=7.0, total_amt=185250.,&lt;br /&gt;                           trans_date=1045623459.68)&lt;br /&gt;    &lt;br /&gt;    p1 = position.Position(id=103, symbol="AAPL", qty=-1000, price=186.25,&lt;br /&gt;                           multiplier=1., fee=7.0, total_amt=-186250.,&lt;br /&gt;                           trans_date=1053605468.54)&lt;br /&gt;    &lt;br /&gt;    p2 = position.Position(id=101, symbol="AAPL", qty=500, price=184.00,&lt;br /&gt;                           multiplier=1., fee=7.0, total_amt=62000.,&lt;br /&gt;                           trans_date=1021236990.02)&lt;br /&gt;    &lt;br /&gt;    plist = [p0, p1, p2]&lt;br /&gt;    &lt;br /&gt;    plist.sort()&lt;br /&gt;    &lt;br /&gt;    assert plist[0]==p2&lt;br /&gt;    assert plist[1]==p0&lt;br /&gt;    assert plist[2]==p1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This just collects a few positions into a list, calls the &lt;code&gt;.sort&lt;/code&gt; method on the list and checks to see if it accurately sorts by the trans_date.  The method I add to my class to accomplish this is actually quite simple.  I define a &lt;code&gt;__cmp__&lt;/code&gt; method which correctly compares objects of this class and I'm good to go:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;    # support reasonable sorting based on trans_date&lt;br /&gt;    def __cmp__(self, other):&lt;br /&gt;        if self.trans_date &lt; other.trans_date:&lt;br /&gt;            return -1&lt;br /&gt;        elif self.trans_date &gt; other.trans_date:&lt;br /&gt;            return 1&lt;br /&gt;        else: return 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This pretty much wraps up the functionality I want (so far) in my position class.  This is evidenced by my nosetests results:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_3SMSqEHAuEQ/SsJkfDnkElI/AAAAAAAAASI/_bJr6q-vAzo/s1600-h/Picture+2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 112px;" src="http://1.bp.blogspot.com/_3SMSqEHAuEQ/SsJkfDnkElI/AAAAAAAAASI/_bJr6q-vAzo/s320/Picture+2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5386978588998505042" /&gt;&lt;/a&gt;&lt;br /&gt;Oh, and one more thing -- thanks to the miracle of traits I can call the &lt;code&gt;edit_traits&lt;/code&gt; method on any position object and get a nice form with all the fields.  I've added a view definition to my position class to pare this down a bit:&lt;br /&gt;&lt;pre name="code" class="py"&gt;&lt;br /&gt;    traits_view = View(Item('symbol', label="Symb"),&lt;br /&gt;                       Item('date_display'),&lt;br /&gt;                       Item('time_display'),&lt;br /&gt;                       Item('qty'),&lt;br /&gt;                       buttons=['OK', 'Cancel'], resizable=True)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now if &lt;code&gt;p&lt;/code&gt; is a &lt;code&gt;Position&lt;/code&gt; object then calling &lt;code&gt;p.edit_traits()&lt;/code&gt; pops up the following dialog:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_3SMSqEHAuEQ/SsJYSHQl57I/AAAAAAAAASA/nhMsi0Npbq0/s1600-h/Picture+1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 226px; height: 183px;" src="http://2.bp.blogspot.com/_3SMSqEHAuEQ/SsJYSHQl57I/AAAAAAAAASA/nhMsi0Npbq0/s320/Picture+1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5386965172498065330" /&gt;&lt;/a&gt;&lt;br /&gt;Again, all the code for this is available &lt;a href="http://github.com/tvaught/experimental/tree/master/trade_queue"&gt;via github&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Given this simple exercise in TDD, my initial impression is that it's helpful for ensuring good test coverage for &lt;em&gt;features&lt;/em&gt;, but I really need to go back and write corner-case tests and tests which will pick up minor regressions in the state of my code better.  Also, I found that considering how to best &lt;em&gt;design the code&lt;/em&gt; was often a separate exercise from &lt;em&gt;designing tests&lt;/em&gt; -- so I could really sense I was switching mental contexts to try to drive the development with well thought-out tests.  I'm willing to concede that this is unique to this developer.  Overall, I think the jury is still out.  I get the feeling I don't have enough experience with TDD to do it well, but hope springs eternal.&lt;br /&gt;&lt;br /&gt;I'll handle proper queueing and adding/removing positions in a portfolio in the next post in this series.  Until then, please raise any questions or corrections in the comments below.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2076691526526730952-395846230879882291?l=travisvaught.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://travisvaught.blogspot.com/feeds/395846230879882291/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://travisvaught.blogspot.com/2009/09/p-statement-step-1-simple-position.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/395846230879882291'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/395846230879882291'/><link rel='alternate' type='text/html' href='http://travisvaught.blogspot.com/2009/09/p-statement-step-1-simple-position.html' title='Test Driven Python Development Experiment: P&amp;L Statement - Step 1'/><author><name>Travis</name><uri>http://www.blogger.com/profile/00720286668168584326</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://2.bp.blogspot.com/_3SMSqEHAuEQ/SnCHvdf6zII/AAAAAAAAAOc/FJxkHVZ0fvo/S220/IMG_8035.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_3SMSqEHAuEQ/SsJkfDnkElI/AAAAAAAAASI/_bJr6q-vAzo/s72-c/Picture+2.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2076691526526730952.post-3374693591960079642</id><published>2009-08-26T16:41:00.005-05:00</published><updated>2009-08-31T16:38:51.501-05:00</updated><title type='text'>Multidimensional Data Visualization in Python - Mixing Chaco and Mayavi</title><content type='html'>In a &lt;a href="http://travisvaught.blogspot.com/2009/08/infographic-in-python-using-chaco.html"&gt;previous post&lt;/a&gt;, I recreated an infographic using the &lt;a href="http://code.enthought.com/chaco/"&gt;Chaco plotting library&lt;/a&gt;.  Inspired by &lt;a href="http://www.archive.org/details/scipy09_day1_17-Lightning_talks_5-8"&gt;Peter Wang's lightning talk&lt;/a&gt; (scroll to about 5:15 in the video) at the recent &lt;a href="http://conference.scipy.org/"&gt;SciPy Conference&lt;/a&gt;, I've extended this idea a bit to show the exploration of a "4D" data set (three axes and the color/size of the points) and using a 5th dimension (the date) as an interactive filter.&lt;br /&gt;&lt;br /&gt;Since it's a whole lot easier to demonstrate than to describe, I made a short screencast of me playing with it:&lt;br /&gt;&lt;br /&gt;&lt;object width="320" height="266" class="BLOG_video_class" id="BLOG_video-789a5bc9041c5620" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"&gt;&lt;param name="movie" value="http://www.youtube.com/get_player"&gt;&lt;param name="bgcolor" value="#FFFFFF"&gt;&lt;param name="allowfullscreen" value="true"&gt;&lt;param name="flashvars" value="flvurl=http://v7.nonxt4.googlevideo.com/videoplayback?id%3D789a5bc9041c5620%26itag%3D5%26app%3Dblogger%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1329935856%26sparams%3Did,itag,ip,ipbits,expire%26signature%3D47AAEEE3E48E463F535EE397EF769BC5B0AF890.7AE63C718C03BAE2A9A669BC7D0E93E98D59866F%26key%3Dck1&amp;amp;iurl=http://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3D789a5bc9041c5620%26offsetms%3D5000%26itag%3Dw160%26sigh%3DkmQQsQgqY2vrU5DLYBp-MFWtIt4&amp;amp;autoplay=0&amp;amp;ps=blogger"&gt;&lt;embed src="http://www.youtube.com/get_player" type="application/x-shockwave-flash"width="320" height="266" bgcolor="#FFFFFF"flashvars="flvurl=http://v7.nonxt4.googlevideo.com/videoplayback?id%3D789a5bc9041c5620%26itag%3D5%26app%3Dblogger%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1329935856%26sparams%3Did,itag,ip,ipbits,expire%26signature%3D47AAEEE3E48E463F535EE397EF769BC5B0AF890.7AE63C718C03BAE2A9A669BC7D0E93E98D59866F%26key%3Dck1&amp;iurl=http://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3D789a5bc9041c5620%26offsetms%3D5000%26itag%3Dw160%26sigh%3DkmQQsQgqY2vrU5DLYBp-MFWtIt4&amp;autoplay=0&amp;ps=blogger"allowFullScreen="true" /&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;While a bit hackish, the code &lt;span style="font-style:italic;"&gt;is&lt;/span&gt; &lt;a href="http://github.com/tvaught/experimental/tree/70be059395a6a604381ef9ba9d7cf84bcd43d04c/commodities"&gt;available&lt;/a&gt; for anyone wishing to play with or improve it.&lt;br /&gt;&lt;br /&gt;I know I've said this before, but it bears repeating -- &lt;a href="http://code.enthought.com/projects/mayavi/"&gt;Mayavi&lt;/a&gt; is awesome.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2076691526526730952-3374693591960079642?l=travisvaught.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='enclosure' type='video/mp4' href='http://www.blogger.com/video-play.mp4?contentId=789a5bc9041c5620&amp;type=video%2Fmp4' length='0'/><link rel='replies' type='application/atom+xml' href='http://travisvaught.blogspot.com/feeds/3374693591960079642/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://travisvaught.blogspot.com/2009/08/multidimensional-data-visualization-in.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/3374693591960079642'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/3374693591960079642'/><link rel='alternate' type='text/html' href='http://travisvaught.blogspot.com/2009/08/multidimensional-data-visualization-in.html' title='Multidimensional Data Visualization in Python - Mixing Chaco and Mayavi'/><author><name>Travis</name><uri>http://www.blogger.com/profile/00720286668168584326</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://2.bp.blogspot.com/_3SMSqEHAuEQ/SnCHvdf6zII/AAAAAAAAAOc/FJxkHVZ0fvo/S220/IMG_8035.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2076691526526730952.post-5099520294291474356</id><published>2009-08-08T10:06:00.008-05:00</published><updated>2009-08-08T14:14:18.051-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='enthought'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='traits'/><title type='text'>A Very Simple GUI Using Traits</title><content type='html'>I ran across &lt;a href="http://www.mechanicalcat.net/richard/log/Python/Something_I_m_working_on.3"&gt;this post&lt;/a&gt; today about some magic with simple syntax for GUI building that Richard Jones is playing around with.  It occurred to me to give a simple example of &lt;a href="http://code.enthought.com/projects/traits/"&gt;traits&lt;/a&gt; usage to do the same thing.&lt;br /&gt;&lt;br /&gt;So, thanks to traits, our class definition looks quite clean, and we can provide some manifest typing as well.  I show a screen shot of my ipython session, which was launched with &lt;code&gt;ipython -wthread&lt;/code&gt;:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_3SMSqEHAuEQ/Sn3LiuuLxYI/AAAAAAAAAQI/jzl-E-j22k4/s1600-h/Picture+2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 349px;" src="http://1.bp.blogspot.com/_3SMSqEHAuEQ/Sn3LiuuLxYI/AAAAAAAAAQI/jzl-E-j22k4/s400/Picture+2.png" alt="" id="BLOGGER_PHOTO_ID_5367670128413230466" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So executing line [5] pops up the GUI shown.  One nice thing about traits is the built-in MVC architecture, which allows me to change the value in the &lt;code&gt;choices&lt;/code&gt; class attribute and it informs the listening &lt;code&gt;label&lt;/code&gt; , which is updated automatically.  Notice that the value &lt;code&gt;f.choices&lt;/code&gt; inspected at the command line is updated as well:&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_3SMSqEHAuEQ/Sn3Lpv2xctI/AAAAAAAAAQQ/BM8zpX6-moQ/s1600-h/Picture+4.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 356px;" src="http://3.bp.blogspot.com/_3SMSqEHAuEQ/Sn3Lpv2xctI/AAAAAAAAAQQ/BM8zpX6-moQ/s400/Picture+4.png" alt="" id="BLOGGER_PHOTO_ID_5367670248976773842" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The main drawback I see between this approach and Richard's is the size of the tool chain.  The TraitsGUI piece requires wxPython (or QT--it works with either), and &lt;a href="https://svn.enthought.com/enthought/wiki/Install"&gt;some dependencies&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2076691526526730952-5099520294291474356?l=travisvaught.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://travisvaught.blogspot.com/feeds/5099520294291474356/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://travisvaught.blogspot.com/2009/08/very-simple-gui.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/5099520294291474356'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/5099520294291474356'/><link rel='alternate' type='text/html' href='http://travisvaught.blogspot.com/2009/08/very-simple-gui.html' title='A Very Simple GUI Using Traits'/><author><name>Travis</name><uri>http://www.blogger.com/profile/00720286668168584326</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://2.bp.blogspot.com/_3SMSqEHAuEQ/SnCHvdf6zII/AAAAAAAAAOc/FJxkHVZ0fvo/S220/IMG_8035.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_3SMSqEHAuEQ/Sn3LiuuLxYI/AAAAAAAAAQI/jzl-E-j22k4/s72-c/Picture+2.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2076691526526730952.post-9056311813084534383</id><published>2009-08-01T10:56:00.008-05:00</published><updated>2009-08-01T20:45:51.872-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='data visualization'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='chaco'/><title type='text'>Infographic in Python using Chaco</title><content type='html'>This week I ran across a &lt;a href="http://flowingdata.com/2009/07/06/is-the-economy-getting-ready-to-turn-around/"&gt;blog post&lt;/a&gt; about &lt;a href="http://www.nytimes.com/interactive/2009/07/02/business/economy/20090705-cycles-graphic.html"&gt;this New York Times infographic&lt;/a&gt;, which explains one of the measures of the "business cycle" based on industrial production (the data comes from the &lt;a href="http://www.oecd.org/"&gt;OECD&lt;/a&gt; originally). [Update: I also saw the &lt;a href="http://www.juiceanalytics.com/writing/recreating-another-new-york-times-chart/"&gt;post&lt;/a&gt; over at Juice Analytics in which they implemented this in excel.]  Never one to pass up an opportunity to re-invent the wheel, I thought it would be a good exercise to implement this in &lt;a href="http://www.python.org/"&gt;Python&lt;/a&gt; using the excellent &lt;a href="http://code.enthought.com/projects/chaco/gallery.php"&gt;Chaco plotting toolkit&lt;/a&gt; which comes included in some python &lt;a href="http://enthought.com/products/epd.php"&gt;distributions&lt;/a&gt;.  So, here's the beginning of that effort, after a few hours digging around the docs and hacking together a GUI:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_3SMSqEHAuEQ/SnRnmuDtyfI/AAAAAAAAAPY/87y-j0SNrzQ/s1600-h/infographic_chaco.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_3SMSqEHAuEQ/SnRnmuDtyfI/AAAAAAAAAPY/87y-j0SNrzQ/s320/infographic_chaco.png" alt="" id="BLOGGER_PHOTO_ID_5365026971001407986" border="0" /&gt;&lt;/a&gt;This is a good start.  I've posted the &lt;a href="http://github.com/tvaught/experimental/tree/master"&gt;code for this&lt;/a&gt; at github.&lt;br /&gt;&lt;br /&gt;To really flesh it out, you'd need to add in the Composite Leading Indicator data and make some of the elements update based on the selected range.  It would also be cool to dynamically switch out the data for various countries, or view them concurrently.  Any takers?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Information Density&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I think what makes such a simple interface so compelling is that you are able to see the relationship between three pieces of data. Cross-plots are a great mechanism for visualizing relationships between two data sets, but they're made even more useful when you can highlight a range in a common index (e.g. time, in the case of time-series data, or depth, in the case of depth indexed data in the geophysics arena.)  Even with a lot of information presented, the display is very clean--even sparse.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;State and State-Transition&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This particular graphic also reveals the "state" of the business cycle by partitioning the graph into quadrants.  This data set has a very straightforward state inherent in it's construction, but one might imagine more sophisticated calculations of state decorating time series data such as this.  I'm beginning to investigate the application of this to stock price streams and some derived state that can be displayed in ways that can be "replayed" and analyzed.  Whether real "information" can be teased out of the data will remain to be seen, but I'll try to leverage the visual cortex to gain intuition about the data.&lt;br /&gt;&lt;br /&gt;Any comments/suggestions about the approach are welcome.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2076691526526730952-9056311813084534383?l=travisvaught.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://travisvaught.blogspot.com/feeds/9056311813084534383/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://travisvaught.blogspot.com/2009/08/infographic-in-python-using-chaco.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/9056311813084534383'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/9056311813084534383'/><link rel='alternate' type='text/html' href='http://travisvaught.blogspot.com/2009/08/infographic-in-python-using-chaco.html' title='Infographic in Python using Chaco'/><author><name>Travis</name><uri>http://www.blogger.com/profile/00720286668168584326</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://2.bp.blogspot.com/_3SMSqEHAuEQ/SnCHvdf6zII/AAAAAAAAAOc/FJxkHVZ0fvo/S220/IMG_8035.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_3SMSqEHAuEQ/SnRnmuDtyfI/AAAAAAAAAPY/87y-j0SNrzQ/s72-c/infographic_chaco.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2076691526526730952.post-8829946978938708785</id><published>2009-07-29T09:40:00.001-05:00</published><updated>2009-07-30T08:16:35.271-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='enthought'/><category scheme='http://www.blogger.com/atom/ns#' term='gis'/><category scheme='http://www.blogger.com/atom/ns#' term='mapnik'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Hello World (Map)</title><content type='html'>I'm resurrecting my &lt;a href="http://blog.enthought.com/?author=6"&gt;blogging&lt;/a&gt; effort outside of &lt;a href="http://www.enthought.com/"&gt;my former company&lt;/a&gt;--in the hopes of sharing useful tips I discover in my new software pursuits.&lt;br /&gt;&lt;br /&gt;One particular area of distraction for me has been in GIS and mapping.  I'm using the excellent &lt;a href="http://mapnik.org/"&gt;mapnik&lt;/a&gt; toolkit to produce mapping visualizations.  Mapnik sits on a fairly heavy stack of dependencies including:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://gdal.org/"&gt;GDAL&lt;/a&gt;/&lt;a href="http://gdal.org/ogr/"&gt;OGR&lt;/a&gt; - libraries and utilities for dealing with a multitude of raster and shape (vector) files.  These have very good &lt;a href="http://trac.osgeo.org/gdal/wiki/GdalOgrInPython"&gt;python bindings&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.kyngchaos.com/software:postgres"&gt;PostgreSQL/PostGIS&lt;/a&gt; - nice, but not required&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://trac.osgeo.org/proj/"&gt;PROJ4&lt;/a&gt; - Cartographic Projections library&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.boost.org/"&gt;BOOST&lt;/a&gt; - The forever-to-compile C++ libraries that mapnik uses for wrapping C++ codes.&lt;/li&gt;&lt;/ul&gt;Getting all of this running on OSX is a huge pain in the neck--especially if I want to use &lt;a href="http://enthought.com/products/getepd.php"&gt;my preferred Python Distribution&lt;/a&gt;.  Much of this pain is alleviated with Framework installs of many of the dependencies from the excellent &lt;a href="http://www.kyngchaos.com/macosx:build:index"&gt;kyngchaos site&lt;/a&gt; (many thanks!).  I'm still not convinced I have all my libraries linked properly (who needs ICU_LIBS anyway?!).&lt;br /&gt;&lt;br /&gt;My original mapping goal was fairly simple:  Develop a composite map of roads, terrain and some custom features for a nice large-format printout for my son's room.  Technically, this meant a few things:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;converting &lt;a href="http://www2.jpl.nasa.gov/srtm/"&gt;srtm elevation data&lt;/a&gt; [1] to nice contour shape files (&lt;a href="http://gdal.org/gdal_contour.html"&gt;gdal_contour&lt;/a&gt;).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;generating a &lt;a href="http://mike.teczno.com/notes/hillshading.html"&gt;hillshade&lt;/a&gt; raster image from srtm data&lt;/li&gt;&lt;li&gt;generating a &lt;a href="http://www.perrygeo.net/wordpress/?p=7"&gt;color relief&lt;/a&gt; from srtm data&lt;/li&gt;&lt;li&gt;properly composing the custom features (considering &lt;a href="http://www.macgis.com/"&gt;Cartographica&lt;/a&gt; for this, but it's a bit expensive for a hobby project and the product seems at bit...nascent.  Suggestions?).&lt;/li&gt;&lt;li&gt;beautifully rendering everything using a nice &lt;a href="http://www.antigrain.com/doc/scanlines/scanlines.agdoc.html"&gt;scanline&lt;/a&gt; approach.&lt;/li&gt;&lt;/ul&gt;It's important to note that the data and the tools I've used (with the exception of Cartographica) are all freely available (some tools do have copy-left provisions, and the data are not to be 'sold' in many cases).  There is a &lt;a href="http://www.tnris.org/datadownload/download.jsp"&gt;massive amount of data for my region of interest&lt;/a&gt;, and a half day of effort gave me something like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_3SMSqEHAuEQ/SnB4RGCoEtI/AAAAAAAAAOM/m0cHpceoHxE/s1600-h/hackberry_first_cut.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 245px;" src="http://3.bp.blogspot.com/_3SMSqEHAuEQ/SnB4RGCoEtI/AAAAAAAAAOM/m0cHpceoHxE/s320/hackberry_first_cut.png" alt="" id="BLOGGER_PHOTO_ID_5363919391272997586" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Well...there was still much work to be done.  The above image used the downloadable contour sets from the two Texas counties that this image spans, and the renderer I was using (cartographica) doesn't do very good text-layout "collision detection," nor does it have very fine-grained control over things like max angles and spacing for labels.  So, putting together a simple mapnik script, using my own generated contours, applying hill shading, and tweaking the color relief to make it more wife-friendly yields something like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_3SMSqEHAuEQ/SnB8iyFrK9I/AAAAAAAAAOU/cO0k7nKvUVM/s1600-h/hackberry_second_try.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 210px;" src="http://1.bp.blogspot.com/_3SMSqEHAuEQ/SnB8iyFrK9I/AAAAAAAAAOU/cO0k7nKvUVM/s320/hackberry_second_try.png" alt="" id="BLOGGER_PHOTO_ID_5363924093201230802" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I'm getting close.  I need to re-append the roads data and add all my custom features (labels, points of interest, etc.)  I'm pleased with the progress so far.&lt;br /&gt;&lt;br /&gt;This exercise has led me to think about potentially different approaches.  While mapnik is extremely nice, they do use boost which, unfortunately, leads to a more lengthy and difficult build/install.  (I'll try to document the OS X steps that I used when I set it up on a new machine--I had to &lt;a href="http://trac.mapnik.org/ticket/380"&gt;deviate&lt;/a&gt; &lt;a href="http://www.nabble.com/Compiling-latest-Mapnik-and-OSM-plugin-on-Mac-OS-X-10.5---experiences-td20999537.html"&gt;from&lt;/a&gt; the "official" installation instructions in a couple of places.)  Why didn't they use SWIG?  Is the mapnik C++ layer heavily templated? Also,  I'd like to see the rendering layer use my favorite stack of 2D rendering tools -- &lt;a href="https://svn.enthought.com/enthought/wiki/Kiva"&gt;Kiva&lt;/a&gt; and &lt;a href="http://code.enthought.com/projects/enable/"&gt;Enable&lt;/a&gt;.  This would allow an intermediate layer which would provide more general vector output routines.  I'll try to get a handle on the level of effort for this in the coming weeks, as time allows.&lt;br /&gt;&lt;br /&gt;----&lt;br /&gt;[1] The srtm data set is one of the cooler products I've come across--a shuttle mission to map the world and provide free access to global elevation data at 10m or 30 resolution (gladly sponsored by this American taxpayer!).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2076691526526730952-8829946978938708785?l=travisvaught.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://travisvaught.blogspot.com/feeds/8829946978938708785/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://travisvaught.blogspot.com/2009/07/hello-world.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/8829946978938708785'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2076691526526730952/posts/default/8829946978938708785'/><link rel='alternate' type='text/html' href='http://travisvaught.blogspot.com/2009/07/hello-world.html' title='Hello World (Map)'/><author><name>Travis</name><uri>http://www.blogger.com/profile/00720286668168584326</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='21' src='http://2.bp.blogspot.com/_3SMSqEHAuEQ/SnCHvdf6zII/AAAAAAAAAOc/FJxkHVZ0fvo/S220/IMG_8035.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_3SMSqEHAuEQ/SnB4RGCoEtI/AAAAAAAAAOM/m0cHpceoHxE/s72-c/hackberry_first_cut.png' height='72' width='72'/><thr:total>2</thr:total></entry></feed>
