FSM for Feed

This commit is contained in:
Stephen Diehl
2012-06-11 09:26:48 -04:00
parent 087de66e4c
commit fefca77fe2
6 changed files with 53 additions and 30 deletions
+24 -2
View File
@@ -3,13 +3,27 @@ from collections import Counter
from zipline.core.component import Component from zipline.core.component import Component
from zipline.components.aggregator import Aggregate from zipline.components.aggregator import Aggregate
from zipline.utils.protocol_utils import Enum
import zipline.protocol as zp import zipline.protocol as zp
from zipline.transitions import WorkflowMeta
from zipline.protocol import CONTROL_PROTOCOL, COMPONENT_TYPE, \ from zipline.protocol import CONTROL_PROTOCOL, COMPONENT_TYPE, \
CONTROL_FRAME, CONTROL_UNFRAME CONTROL_FRAME, CONTROL_UNFRAME
LOGGER = logging.getLogger('ZiplineLogger') LOGGER = logging.getLogger('ZiplineLogger')
# FSM
# ---
INIT, READY, DRAINING = FEED_STATES = \
Enum( 'INIT', 'READY', 'DRAINING')
state_transitions = dict(
do_start = (-1 , INIT) ,
do_run = (INIT , READY) ,
do_drain = (READY , DRAINING) ,
)
class Feed(Aggregate): class Feed(Aggregate):
""" """
Connects to N PULL sockets, publishing all messages received to a Connects to N PULL sockets, publishing all messages received to a
@@ -18,20 +32,26 @@ class Feed(Aggregate):
one execution context (thread, process, etc) and run in another. one execution context (thread, process, etc) and run in another.
""" """
__metaclass__ = WorkflowMeta
states = list(FEED_STATES)
transitions = state_transitions
initial_state = -1
def init(self): def init(self):
self.sent_count = 0 self.sent_count = 0
self.received_count = 0 self.received_count = 0
self.draining = False self.draining = False
self.ds_finished_counter = 0 self.ds_finished_counter = 0
# Depending on the size of this, might want to use a data
# structure with better asymptotics.
self.data_buffer = {} self.data_buffer = {}
# source_id -> integer count # source_id -> integer count
self.sent_counters = Counter() self.sent_counters = Counter()
self.recv_counters = Counter() self.recv_counters = Counter()
self.state = INIT
@property @property
def get_id(self): def get_id(self):
return "FEED" return "FEED"
@@ -71,6 +91,8 @@ class Feed(Aggregate):
""" """
Get the next message in chronological order. Get the next message in chronological order.
""" """
# is_full and draining defined in aggregator
if not(self.is_full() or self.draining): if not(self.is_full() or self.draining):
return return
+1 -1
View File
@@ -7,7 +7,7 @@ class PassthroughTransform(BaseTransform):
""" """
def init(self): def init(self):
self.state = { 'name': 'PASSTHROUGH' } self.props = { 'name': 'PASSTHROUGH' }
#TODO, could save some cycles by skipping the _UNFRAME call #TODO, could save some cycles by skipping the _UNFRAME call
# and just setting value to original msg string. # and just setting value to original msg string.
+20
View File
@@ -568,3 +568,23 @@ class Component(object):
pid = os.getpid() , pid = os.getpid() ,
pointer = hex(id(self)) , pointer = hex(id(self)) ,
) )
@property
def state(self):
if not hasattr(self, '_state'):
self._state = self.initial_state
else:
return self._state
@state.setter
def state(self, new):
if not hasattr(self, '_state'):
self._state = self.initial_state
old = self._state
if (old, new) in self.workflow:
self._state = new
else:
raise RuntimeError("Invalid State Transition : %s -> %s" %(old, new))
+5 -5
View File
@@ -7,20 +7,20 @@ from zipline.finance.movingaverage import EventWindow
class VWAPTransform(BaseTransform): class VWAPTransform(BaseTransform):
def init(self, name, daycount=3): def init(self, name, daycount=3):
self.state = {} self.props = {}
self.state['name'] = name self.props['name'] = name
self.daycount = daycount self.daycount = daycount
self.by_sid = defaultdict(self.create_vwap) self.by_sid = defaultdict(self.create_vwap)
@property @property
def get_id(self): def get_id(self):
return self.state['name'] return self.props['name']
def transform(self, event): def transform(self, event):
cur = self.by_sid[event.sid] cur = self.by_sid[event.sid]
cur.update(event) cur.update(event)
self.state['value'] = cur.vwap self.props['value'] = cur.vwap
return self.state return self.props
def create_vwap(self): def create_vwap(self):
return DailyVWAP(self.daycount) return DailyVWAP(self.daycount)
+3 -3
View File
@@ -25,7 +25,7 @@ class BaseTransform(Component):
@property @property
def get_id(self): def get_id(self):
return self.state['name'] return self.props['name']
@property @property
def get_type(self): def get_type(self):
@@ -116,9 +116,9 @@ class BaseTransform(Component):
Transforms run in parallel and results are merged into a Transforms run in parallel and results are merged into a
single map, so transform names must be unique. Best practice single map, so transform names must be unique. Best practice
is to use the self.state object initialized from the transform is to use the self.props object initialized from the transform
configuration, and only set the transformed value:: configuration, and only set the transformed value::
self.state['value'] = transformed_value self.props['value'] = transformed_value
""" """
raise NotImplementedError raise NotImplementedError
-19
View File
@@ -39,24 +39,6 @@ class WorkflowMeta(type):
""" """
Base metaclass component workflows. Base metaclass component workflows.
""" """
@property
def state(self):
if not hasattr(self, '_state'):
self._state = self.initial_state
else:
return self._state
@state.setter
def state(self, new):
if not hasattr(self, '_state'):
self._state = self.initial_state
old = self._state
if (old, new) in self.workflow:
self._state = new
else:
raise RuntimeError("Invalid State Transition : %s -> %s" %(old, new))
def __new__(cls, name, mro, attrs): def __new__(cls, name, mro, attrs):
base = 'Component' base = 'Component'
@@ -71,7 +53,6 @@ class WorkflowMeta(type):
raise RuntimeError('`workflow` is a reserved attribute.') raise RuntimeError('`workflow` is a reserved attribute.')
if not state: if not state:
import pdb; pdb.set_trace()
raise RuntimeError('Must specify states') raise RuntimeError('Must specify states')
if not transitions: if not transitions: