Merge branch 'master' into fawce_alpha750_bugs

This commit is contained in:
fawce
2012-08-15 23:39:59 -04:00
3 changed files with 152 additions and 3 deletions
+49 -1
View File
@@ -1,4 +1,5 @@
import pytz
import numpy
from datetime import timedelta, datetime
from collections import defaultdict
@@ -15,6 +16,7 @@ from zipline.gens.tradegens import SpecificEquityTrades
from zipline.gens.transform import StatefulTransform, EventWindow
from zipline.gens.vwap import VWAP
from zipline.gens.mavg import MovingAverage
from zipline.gens.stddev import MovingStandardDev
from zipline.gens.returns import Returns
import zipline.utils.factory as factory
@@ -70,6 +72,7 @@ class EventWindowTestCase(TestCase):
delta = timedelta(minutes = 5),
days = None
)
now = utcnow()
# 15 dates, increasing in 1 minute increments.
@@ -99,6 +102,7 @@ class EventWindowTestCase(TestCase):
delta = None,
days = 1
)
dates = ([self.pre_open]*3)
dates += ([self.mid_day]*3)
dates += ([self.post_close]*3)
@@ -239,11 +243,12 @@ class FinanceTransformsTestCase(TestCase):
fields = ['price', 'volume'],
delta = timedelta(days = 2),
)
transformed = list(mavg.transform(self.source))
# Output values.
tnfm_prices = [message.tnfm_value.price for message in transformed]
tnfm_volumes = [message.tnfm_value.volume for message in transformed]
# "Hand-calculated" values
expected_prices = [
((10.0) / 1.0),
@@ -264,3 +269,46 @@ class FinanceTransformsTestCase(TestCase):
assert tnfm_prices == expected_prices
assert tnfm_volumes == expected_volumes
def test_moving_stddev(self):
trade_history = factory.create_trade_history(
133,
[10.0, 15.0, 13.0, 12.0],
[100, 100, 100, 100],
timedelta(hours = 1),
self.trading_environment
)
stddev = StatefulTransform(
MovingStandardDev,
market_aware = False,
delta = timedelta(minutes = 150),
)
self.source = SpecificEquityTrades(event_list=trade_history)
transformed = list(stddev.transform(self.source))
vals = [message.tnfm_value for message in transformed]
expected = [
None,
numpy.std([10.0, 15.0], ddof = 1),
numpy.std([10.0, 15.0, 13.0], ddof = 1),
numpy.std([15.0, 13.0, 12.0], ddof = 1),
]
# numpy has odd rounding behavior, cf.
# http://docs.scipy.org/doc/numpy/reference/generated/numpy.std.html
for v1, v2 in zip(vals, expected):
if v1 == None:
assert v2 == None
continue
assert round(v1, 5) == round(v2, 5)
+100
View File
@@ -0,0 +1,100 @@
from numbers import Number
from datetime import datetime, timedelta
from collections import defaultdict
from math import sqrt
from zipline import ndict
from zipline.gens.transform import EventWindow
class MovingStandardDev(object):
"""
Class that maintains a dicitonary from sids to
MovingStandardDevWindows. For each sid, we maintain a the
standard deviation of all events falling within the specified
window.
"""
def __init__(self, market_aware, days = None, delta = None):
self.market_aware = market_aware
self.delta = delta
self.days = days
# Market-aware mode only works with full-day windows.
if self.market_aware:
assert self.days and not self.delta,\
"Market-aware mode only works with full-day windows."
# Non-market-aware mode requires a timedelta.
else:
assert self.delta and not self.days, \
"Non-market-aware mode requires a timedelta."
# No way to pass arguments to the defaultdict factory, so we
# need to define a method to generate the correct EventWindows.
self.sid_windows = defaultdict(self.create_window)
def create_window(self):
"""
Factory method for self.sid_windows.
"""
return MovingStandardDevWindow(
self.market_aware,
self.days,
self.delta
)
def update(self, event):
"""
Update the event window for this event's sid. Return an ndict
from tracked fields to moving averages.
"""
# This will create a new EventWindow if this is the first
# message for this sid.
window = self.sid_windows[event.sid]
window.update(event)
return window.get_stddev()
class MovingStandardDevWindow(EventWindow):
"""
Iteratively calculates standard deviation for a particular sid
over a given time window. The expected functionality of this
class is to be instantiated inside a MovingStandardDev.
"""
def __init__(self, market_aware, days, delta):
# Call the superclass constructor to set up base EventWindow
# infrastructure.
EventWindow.__init__(self, market_aware, days, delta)
self.sum = 0.0
self.sum_sqr = 0.0
def handle_add(self, event):
assert event.has_key('price')
assert isinstance(event.price, Number)
self.sum += event.price
self.sum_sqr += event.price ** 2
def handle_remove(self, event):
assert event.has_key('price')
assert isinstance(event.price, Number)
self.sum -= event.price
self.sum_sqr -= event.price ** 2
def get_stddev(self):
# Sample standard deviation is undefined for a single event or
# no events.
if len(self) <= 1:
return None
else:
average = self.sum /len(self)
s_squared = (self.sum_sqr - self.sum*average) / (len(self) - 1)
stddev = sqrt(s_squared)
return stddev
+3 -2
View File
@@ -159,8 +159,9 @@ class EventWindow:
from the window. Subclass these methods along with init(*args,
**kwargs) to calculate metrics over the window.
The market_aware flag is used to toggle whether the eventwindow
calculates
If the market_aware flag is True, the EventWindow drops old events
based on the number of elapsed trading days between newest and oldest.
Otherwise old events are dropped based on a raw timedelta.
See zipline/gens/mavg.py and zipline/gens/vwap.py for example
implementations of moving average and volume-weighted average