[cello-devel] New Cells Use Case, comments welcome

Kenny Tilton ktilton at nyc.rr.com
Wed May 18 18:35:17 UTC 2005


Sorry for spaming all the groups, but the occasion (Cells doc) is rare 
enough I thought everyone should be included. From here on is a 
standalone example (with Cells loaded) that reads data from itself.

It models tracking of an index of stocks. I plan a few more notes, but 
thought I would invite comments/requests for clarification now. Here goes:

(in-package :cells)

#|

The deal is this: explanations of chunks of code appear /below/ them.

Now here are Ron's functional requirements: process a stream of messages 
from an
imagined source of financial data. Actually, Ron has an intermediate process
reading a real source and producing a somewhat-digested stream in 
Lisp-friendly
format. Sample:

(:date 5123 :weekday 3)
(:index ((AA 29.30 7.3894672) (AIG 53.30 7.3894672)(AXP 53.00 7.3894672)
(BA 59.87 7.3894672) (C 46.80 7.3894672) (CAT 87.58 7.3894672) (DD 47.74 
7.3894672)
(DIS 26.25 7.3894672) (GE 36.10 7.3894672) (GM 27.77 7.3894672) (HD 
36.75 7.3894672)
(HON 35.30 7.3894672) (HPQ 21.00 7.3894672) (IBM 76.47 7.3894672)
(INTC 23.75 7.3894672) (JNJ 68.73 7.3894672) (JPM 35.50 7.3894672) (KO 
43.76 7.3894672)
(MCD 29.80 7.3894672) (MMM 76.76 7.3894672) (MO 65.99 7.3894672) (MRK 
34.42 7.3894672)
(MSFT 25.36 7.3894672) (PFE 27.5 7.3894672) (PG 54.90 7.3894672) (SBC 
23.8 7.3894672)
(UTX 100.96 7.3894672) (VZ 36.75 7.3894672) (WMT 48.40 7.3894672) (XOM 
56.50 7.3894672)))
(:trade INTC    0.001932 :last  23.75)
(:trade MSFT    0.001932 :last  25.36)
(:trade INTC    0.011931 :last  23.75)
(:trade MSFT    0.011931 :last  25.36)
(:trade MSFT    0.041965 :last  25.32)
(:trade UTX     0.067027 :last 101.39)
...etc...

Date messages encode date as (+ (* (- year 2000) 1000) julian-days). 
Weekday is dicey,
so the tutorial deduces the Lisp weekday and stores that.

Index messages define which tickers are in the index and their weights.
Entries are: (ticker-symbol initial-price index-weight)

Trade messages are (ticker-symbol ticker-minute :LAST price)
Ticker-minute is time since open, in minutes. Negative indicates 
pre-open trading.

To get the ball rolling, we just want to print out each trade as 
received, with the
addition of an indicator as to which way the price moved: -1, 0, or 1 
for down, unchanged, or up.

For the index, we want to track the minute of the last trade affecting 
the index, the
weighted index value, and the last move of each index entry.

|#

(defparameter *trc-trades* t)

#+test
(run-trading-day)

(defun run-trading-day ()
  (cell-reset)
  (let ((*trc-trades* nil)
        (t-day (make-be 'trading-day)))
   
    ;; - always call CELLS-RESET when starting a test run
    ;; - (make-be ...) -> (to-be (make-instance ...))
    ;; - TO-BE jumpstarts a Cells instance into the flow. (FN to-be)
   
    #+not (with-open-file (t-data (make-pathname
                                   :directory '(:absolute "0dev" "cells" 
"Use Cases" "dow-jones")
                                   :name "stock-exchange" :type "lisp"))
            (with-metrics (nil t "run-trading-day")
              (loop for message = (read t-data nil :eof)
                  until (eq message :eof)
                  do (setf (message t-day) message)))
            )
   
    (with-open-file (t-data (make-pathname
                             :directory '(:absolute "0dev" "cells" "Use 
Cases" "dow-jones")
                             :name "stock-exchange" :type "lisp"))
      (with-metrics (t t "run-trading-day")
        (loop with in-data = nil
            do (if (not in-data)
                   (setf in-data (msg-start (read-line t-data nil :eof)))
                 (let ((message (read t-data nil :eof)))
                   (count-it :dow-message)
                   (if (eql (car message) :close)
                       (loop-finish)
                     (setf (message t-day) message)))))))
    (trc "index value = " (value (car (indexes t-day))))))

;; --- trading day ---------------------------------
;;

(defmodel trading-day ()
  ((message :initarg :message :accessor message
     :initform (c-in nil) ;; c-in -> c-input, how data enters a model 
(see FN c-input)
     :cell :ephemeral) ;; handling transient phenomena in a steady-state 
paradigm (FN ephemeral)
  
   (date :initarg :date :accessor date
     :initform (c? (or .cache ;; advanced trick using prior value (see 
FN date/.cache)
                     (when (eql :date (car (^message)))
                       (destructuring-bind (&key date weekday)
                           (^message)
                         (declare (ignore weekday)) ;; derive from date
                         (encode-julian-date (+ 2000 (floor date 1000)) 
(mod date 1000)))))))
  
   (weekday :initarg :weekday :accessor weekday
     :initform (c? (when (^date)
                     (multiple-value-bind (second minute hour date month 
year day daylight-p zone)
                         (decode-universal-time (^date))
                       (declare (ignorable second minute hour date month 
year daylight-p zone))
                       day))))

   ;; not much new here, but astute readers will wonder if this cell 
gets optimized away
   ;; when (^date) on its second evaluation uses its .cache and gets 
optimized away.
   ;;
   ;; yes. Just checked to be sure.
  
   (trade :cell :ephemeral :initarg :trade :accessor trade
     :initform (c? (when (eql :trade (car (^message)))
                     (message-to-trade (^message)))))
   ;;
   ;; nothing new here, but note that again we use the :ephemeral option
   ;;
   (indexes :initarg :indexes :accessor indexes
     :initform (c? (with-c-cache ('cons)
                     (when (eql :index (car (^message)))
                       (make-be 'index
                               :trading-day self
                               :index-def (second (^message)))))))
   (tickers :cell nil :reader tickers :initform (make-hash-table 
:rehash-size 50))
   ))


(def-c-output trade ((self trading-day) trade) ;; FN def-c-output
  (when trade ;; FN trade setf optimization
    (push trade (trades (ensure-ticker self (trade-ticker-sym trade))))))

(defun trading-day-ticker (day sym)
  (gethash sym (tickers day)))

(defun (setf trading-day-ticker) (ticker day sym)
  (setf (gethash sym (tickers day)) ticker))

(defun ensure-ticker (trading-day ticker-sym &optional price minute)
  (or (trading-day-ticker trading-day ticker-sym)
    (setf (trading-day-ticker trading-day ticker-sym)
      (make-be 'ticker :ticker-sym ticker-sym
        :trades (c-in (when price
                        (list (make-trade :ticker-sym ticker-sym
                                :minute minute :price price))))))))

(defmodel ticker (model)
  ((ticker-sym :cell nil :initarg :ticker-sym :reader ticker-sym)
   (trades :initarg :trades :accessor trades :initform (c-in nil))
   (last-move :reader last-move
     :initform (c? (bwhen (penult-trade (second (^trades)))
                     (let ((last (trade-price (first (^trades))))
                           (penult (trade-price penult-trade)))
                       (cond
                        ((< last penult) -1)
                        ((= last penult) 0)
                        (t 1)))))
     :documentation "Price up, down, or unchanged as 1, -1, or zero")))
;;
;; Small point: there is no problem with accessing a dependency twice, 
as above.
;;

(defun ticker-price (ticker)
  (bwhen (trade (car (trades ticker)))
    (trade-price trade)))

(defun ticker-trade-minute (ticker)
  (bwhen (trade (car (trades ticker)))
    (trade-minute trade)))

(def-c-output trades ((self ticker)) ;; FN trades def-c-output
    (when *trc-trades*
      (loop for trade in (set-difference new-value old-value)
            do (format t "~&at ~a min, ~a at ~a, change ~a"
                 (trade-minute trade) (ticker-sym self) (trade-price 
trade) (or (^last-move) "")))))

;; --- index ---------------------------------------------------

(defmodel index ()
  ((index-def :cell nil :initarg :index-def :initform nil :accessor 
index-def)
   (trading-day :cell nil :initarg :trading-day :initform nil :accessor 
trading-day)
   (ticker-weights :initarg :ticker-weights :accessor ticker-weights
     :initform (c? (loop for (ticker-sym price weight) in (index-def self)
                       collecting (cons (ensure-ticker (trading-day 
self) ticker-sym price -60)
                                    ;; whoa, a mid-rule to-be! (FN 
ticker-weights rule)
                                    weight))))

   (state :reader state
     :initform (let ((moves (make-hash-table :size 50)))
                 (c-formula (:lazy t) ;; do not re-compute on every 
trade (see FN lazy)
                   (count-it :index-state-calc)
                   (clrhash moves) ;; Re-use OK since fresh cons 
triggers dataflow (FN state rule)
                   (let ((minutes (loop for (ticker . nil) in 
(ticker-weights self)
                                      maximizing (ticker-trade-minute 
ticker))))
                     (without-c-dependency ;; dependency on trade minute 
suffices (see FN without-c-dependency)
                      (loop for (ticker . weight) in (ticker-weights self)
                          summing (* weight (ticker-price ticker)) into 
value
                          do (setf (gethash (ticker-sym ticker) moves) 
(last-move ticker))
                          finally (return (list minutes value moves))))))))

   (value :reader value :initform (c? (second (^state))))
   ;;
   ;; allows dependency on just value, which will not change on 
unchanged trades (FN value cell)
   ))

(defun index-minutes (index) (first (state index)))
(defun index-moves (index) (third (state index)))
(defun index-ticker-sym-move (index ticker-sym) (gethash ticker-sym 
(index-moves index)))
(defun index-ticker-move (index ticker) (index-ticker-sym-move index 
(ticker-sym ticker)))

(def-c-output value ((self index))
  (when *trc-trades*
    (trc "index time:" (index-minutes self) :value new-value :was 
old-value)))

;;; --- trade 
---------------------------------------------------------------------

(defstruct trade minute ticker-sym price)

(defun message-to-trade (message)
  (destructuring-bind (ticker-sym ticker-min &key last) (rest message)
    (make-trade
     :ticker-sym ticker-sym
     :minute ticker-min
     :price last)))

;;; --- utilities ---------------------------------------------------------

(defun encode-julian-date (year julian)
  (+ (encode-universal-time 0 0 0 1 1 year )
    (* (1- julian) 86400))) ;; seconds in a day

;; I am sorry, that is all there is to tell. So we have a mindless main 
loop and a few declarations
;; and somehow we get all the functionality desired. [OK, granted, this 
is a pretty simple
;; batch process which would not be too complicated in non-Cells form. 
In that regard, it
;; is a good tutorial use case but does not show off Cells very much.] 
Anyway...
;;
;; It occurs to me that the above notes do not convey how the damn thing 
works. So let us walk
;; thru a hand-execution of the above sample data.
;;
;; (make-be 'trading-day) -> (to-be (make-instance 'trading-day))
;;
;; Each ruled Cell gets evaluated. Each Cell slot -- constant, input, or 
ruled -- is output.
;; So with trading-day:
;;
;;    message is input, and has no associated output function
;;
;;    date is evaluated:
;;;    (or .cache
;;;     (when (eql :date (car (^message)))
;;;       (destructuring-bind (&key date weekday)
;;;           (^message)
;;;         (declare (ignore weekday)) ;; derive from date
;;;         (encode-julian-date (+ 2000 (floor date 1000)) (mod date 
1000)))))
;;   
;;      .cache is nil, but so is (message self). NIL is returned, there 
is no output.
;;      date now has a dependency on message.
;;
;;   weekday is evaluated
;;;               (c? (when (^date)
;;;                     (multiple-value-bind (second minute hour date 
month year day daylight-p zone)
;;;                         (decode-universal-time (^date))
;;;                       (declare (ignorable second minute hour date 
month year daylight-p zone))
;;;                       day))))
;;      date is nil, so weekday is NIL but has a dependency on date. No 
output is defined.
;;
;;   trade is evaluated
;;;              (c? (when (eql :trade (car (^message)))
;;;                     (message-to-trade (^message)))))
;;      message is NIL, so NIL is returned. trade now has a dependency 
on message. The output
;;      method on trade is invoked, but has no interest in NIL new values.
;;
;;   indexes is evaluated:
;;;              (with-c-cache ('cons)
;;;                     (when (eql :index (car (^message)))
;;;                       (make-be 'index
;;;                               :trading-day self
;;;                               :index-def (second (^message)))))))
;;      message is NIL, so NIL is returned, a dependency on message 
created. No output defined.
;;
;; (setf (message t-day) <the :date message>)
;;
;;  Many rules are dispatched: date, trade, and indexes. Only date 
processes :date messages.
;;  it returns a converted date, and still has a dependency on message. 
Weekday has a dependency
;;  on date, so that rule gets dispatched. It returns a weekday 
calculated off the date, and
;;  keeps the dependency on that. Other rules return
;;  NIL, which is the same value they had before. Nothing else is done 
(and in this case, that
;;  would only have been to call the output method on trade.
;;
;; (setf (message t-day) <the :index message>)
;;
;;  The date rule runs and returns its .cache without accessing any 
cell. The Cell internals
;;  optimize away the fact that date ever had a rule or any kind of 
cell. It sees weekday
;;  was a dependent on date and nothing else, so it optimizes that away, 
too. Slots end up
;;  with the last values calculated, and now look to other rules as if 
they were constant
;;  all along.
;;
;;  The trade rule runs and comes up empty again. The indexes rule runs 
and adds a new
;;  index list to its current contents, which happens to be NIL.
;;
;;;;  make-be is called on the index instance. Each slot gets processed 
in turn in a
;;;;  fashion similar to that for trading-day. When the ticker-weights 
rule runs, ticker
;;;;  instances for each ticker in the index are created and passed to 
TO-BE, in the
;;;;  function ensure-ticker. No dependencies are created since 
index-def is not a Cell,
;;;;  so the ticker-weights cell gets optimized away.
;;;;
;;;;  as each ticker is created and processed by TO-BE:
;;;;;;;
;;;;  the state rule is evaluated and computes an initial index state 
off the data
;;;;  provided in the index-def. state ends up with dependencies on each 
ticker in the
;;;;  index.
;;  [rest under construction]
;;

;;; 
=============================================================================
;;; Footnotes
;;; 
=============================================================================
;
;; --- FN to-be --------------------------------------
;;   TO-BE jumpstarts a Cells instance into the flow. Literally, as in
;;   the dataflow. It evaluates ruled slots to establish dependencies (those
;;   get established during evaluation) and in turn arrange for state 
change
;;   within the model to propagate to the instance's ruled Cells. It also
;;   DEF-C-OUTPUTs all cell slots so the outside world is consistent
;;   with the model state. More on def-c-output below.
;
;; --- FN c-input ------------------------------------
;;
;; c-in is short for c-input, which simply means imperative application code
;; can SETF this slot. (Note that this is just the initform for this slot,
;; which can be overridden by subclasses or at make-instance time, and if
;; the override is not another C-IN or C-INPUT, then all bets are off. 
ie, The
;; SETF ability depends on the type of Cell (if any) associated at run-time
;; with the slot of an instance. It
;; is not an attribute of the slot as with the :cell slot option 
discussed just below.
;;
;; Anyway, C-IN lets us make a lot of points about Cells.
;;
;; First, no model is
;; an island; the dataflow has to start somewhere. Just as a VisiCalc 
spreadsheet
;; has cells where you can type, say, different interest rates to see 
how that
;; effects the rest of a financial model, a Cell-based application model 
needs
;; some way to interface with the outside world, if only the mouse and 
keyboard
;; of a GUI application.
;;
;; The way we do that is by having conventional application code feed 
(SETF) data into
;; the dataflow model at what we call cell inputs. In a typical GUI app, 
this means
;; having callbacks registered with the window manager. The callbacks 
then take their
;; arguments (window events such as mouse-downs and key-presses) and 
setf that
;; info to slots of a window or system instance modelling the window or 
operating
;; system, slots mediated by c-input Cells.
;;
;; In this simple use case we have just one stream of external inputs 
(messages
;; from some financial data service) being SETFed into one slot, the message
;; slot of an instance of the trading-day class.
;;
;; Second, the Cells design enforces discipline. So in case you are
;; wondering, no, if you do not bind a C-INPUT to a slot of an instance, 
you cannot
;; SETF that slot from imperative code. (Aside: (SETF SLOT-VALUE) /is/ a 
back door
;; allowing you to wreak havoc on your dataflow model if you so choose 
(but it will
;; wreak havoc).)
;;
;; Third, you might wonder why slots meant as inputs cannot just have no 
Cell at all
;; associated with them at run-time, and then have the Cell internals 
accept that
;; as a SETF-able state. Well, it is a long story, but it turns out that 
a lot of
;; Cells overhead can be avoided if we distinguish a slot whose value 
will never
;; change from an input slot which will be SETF'ed. A simple example of 
a constant
;; slot would be the bounding rectangle of a push button. Those values 
have to be
;; Cells because in other graphical elements sharing the same 
superclass, the bounding
;; rectangle changes. A good example is the win32-style scroll bar 
thumb, which changes
;; size to reflect how much of the total file is visible. Anyway, it 
turns out that
;; a significant performance boost comes from having Cells which happen 
to access
;; a constant value not record a dependency on that value and, where a 
rule evaluation
;; turns out not to access any non-constant other Cell slot, likewise 
convert the ruled
;; slot into a constant slot. Sorry you asked?
;;
;; --- FN ephemeral 
-----------------------------------------------------------
;;
;; Whoa, here is an advanced topic. Ephemeral means "fleeting". Before 
getting into
;; that, the other options for the :cell option are T and NIL. T is the 
default.
;; NIL means you get a normal slot having nothing to do with Cells. Now 
about
;; that :ephemeral option: Messages are
;; like events: they happen, then they are no more. This is a problem for
;; Cells, which like a VisiCalc spreadsheet model (say, your household 
budget)
;; is all about steady-state occasionally perturbed by inputs. That is 
vague.
;; Here is a concrete example: suppose you have some game where the user has
;; to press a key when two randomly moving shapes overlap. You will have 
a hit rule
;; that says (abbreviated somewhat):
;;
;;     (and (eql (event *sys*) :keypress) (shapes-overlap-p *sys*))
;;
;; OK, the key is pressed but the shapes do not overlap. No cigar. Now a few
;; seconds later the shapes do overlap. The key is not being pressed, 
but the
;; EVENT slot of the *sys* instance (modelling the computer system) still
;; says :keypress. bad news. Obviously we need to process an event and then
;; clear the value before processing any other model input. Now perhaps 
we could
;; simply have imperative code which says:
;;
;;    (setf (event *sys*) :keypress)
;;    (setf (event *sys*) nil)
;;
;; But that is different. That suggests an application semantic in which the
;; EVENT slot changes from :keypress to NIL. It will trigger all the usual
;; dataflow, to see if the model should react. But in fact what we /really/
;; need is /not/ to clear the EVENT slot. What we really need is
;; ephemeral SETF behavior from a mechanism designed for steady-state.
;; We need the EVENT slot to take on a value just long enough to perturb our
;; model and then cease to be without fanfare.
;;
;; So we extend the Cells model with the :ephemeral option on a slot, 
and have
;; Cell internals watch out for that and silently clear the slot once a 
value
;; has been propagated to other Cells and output (again, outputs
;; are discussed below.)
;;
;; A final newbie note: watch the bouncing options. Ephemerality is a 
slot option,
;; not something one tailors to the instance. Think about it. Think 
about the
;; slot names. "message", "event". We want to get ephemeral behavior for 
these
;; slots no matter what cell (input or ruled) we choose to associate 
with them.
;; So it is more convenient and reliable to endow the slot itself with 
ephemerality.
;; in other cases we see different instances enjoying different Cell-ish 
qualities
;; for the same slot, sometimes constant, sometimes computed, sometimes 
being
;; SETFed by imperative code outside the dataflow model. These 
variations are
;; then found in the type of runtime Cell associated with the Cell slot.
;;
;; --- FN date/.cache --------------------------------------------------
;;
;;
;; There is a lot going on here, too, including some premature optimization.
;;
;; First of all, .cache is just a local variable, bound by the expansion
;; of the C? macro to the latest value calculated for this rule. It 
starts out as NIL, so
;; the rule next reads the message slot of the same trading-day 
instance. How so?
;;
;; ^message is a macro written by the defmodel macro. It expands simply to:
;;
;;     (message self)
;;
;; It used to expand to more, including vital Cell plumbing. Now I keep 
it around just
;; because I love that self-documenting quality. And yes, I have adopted 
the
;; Smalltalk "self" convention over the C++ "this" convention. There is 
no need
;; to use these (^macros), just code (<slot-name> self) and you will 
establish a
;; dependency on the message slot. What does dependency mean?
;;
;; Simply that the next time the message slot changes (the default test 
between old and
;; new values is EQL, but can be overridden), the Cells engine will 
immediately kick
;; the DATE rule to see if it wants to compute a different value.
;;
;; A very important point is that dependencies are established 
automatically simply
;; by invoking the reader or accessor associated with a slot, and that 
this happens
;; dynamically at run-time, not by inspection of code. A second point is 
that the
;; dependency is established even if the read takes place in a called 
function.
;;
;; There is a backdoor. No dependencies are established in code wrapped by
;; the macro WITHOUT-C-DEPENDENCY.
;;
;; Another important point is that dependencies are re-decided 
completely each time
;; a rule is invoked. So this particular rule is an oddball: it will 
produce only one value, when a :date
;; message is received
;; and teh first non-NIL value is returned. On the next message (of any 
kind) .cache will be
;; non-NIL and the rule will simply return that value.
;; During this last evaluation the cell will not access, hence no longer
;; depend on, the message slot or any other slot and it will get 
optimized away. This
;; improves performance, since the message slot no longer bothers 
propagating to
;; the date slot and Cell internals no longer have to invoke the rule. 
Otherwise, every
;; new message for the entire day (none of which would be :date 
messages) would kick
;; off this rule.
;;
;; --- FN with-c-cache ------------------------------------
;;
;; I am actually doing something new here. The idea is that again we deviate
;; slightly from the spreadsheet paradigm and want to accumulate data
;; from a stream of ephemeral values. Normally we calculate a slot value in
;; its entirety from data at hand, even if only ephemerally. Here we want
;; to add a newly computed result to a list of prior such results.
;;
;; with-c-cache will accept any two-argument function, and when the enclosed
;; form returns a non-nil value, pass that and the .cache to the specified
;; function.
;;
;; --- FN def-c-output --------------------------------------------
;;
;; Above is another optimization, and the long-awaited discussion of Cell
;; output.
;;
;; Output reinforces the "no model is an island" theme. We create
;; models to obtain interesting outputs from inputs, where the model
;; provides the interest. For a RoboCup player simulation, the inputs are
;; sensory information about the game, provided in a stream from a server
;; application managing multiple client players and coaches. The outputs are
;; messages to the server indicating player choices about turning, running,
;; and kicking. In between, the game play model is supposed to compute
;; actions producing more or less capable soccer play.
;;
;; --- FN trade setf optimization ---------------------------------------
;
;; But this is strange "output". It actually changes internal model state.
;; It is no output at all, just feeding dataflow back into a different
;; model input. Whassup?
;;
;; Like I said, it is an optimization. A ticker instance could have a
;; rule which watched the message stream looking for trades on that ticker,
;; but then every ticker would be watching the message stream.
;;
;; Instead, we simply leverage an "output" method to procedurally decide 
which
;; ticker has been traded and directly add the trade to that ticker's list
;; of trades.
;;
;; --- FN trades def-c-output --------------------------------------
;;
;; Now the above is a proper output. Merely a print trace to standard 
output, but
;; that happens to be all the output we want just now. In a real trading 
application,
;; there probably would not be an output on this slot. Some gui widget 
might "output"
;; by telling the OS to redraw it, or some trader instance might decide 
to output
;; a buy order to an exchange, but that is about it.
;;
;; --- FN ticker-weights rule --------------------------------------
;;
;; A curiosity here is that ensure-ticker will often be making and 
to-be-ing new model
;; instances while this rule is running. No problem, though it would be 
possible to
;; get into trouble if such destructive (well, constructive) operations 
triggered
;; dataflow back to this same rule. Here we are safe; it does not. In 
fact...
;;
;; This rule runs once and then gets optimized away, because in this 
simple case
;; index-def is a constant, not even a cell. Should we someday want to 
handle
;; changes to an index during a trading-day, this would have to change.
;;
;; --- FN lazy ------------------------------------------------------
;;
;;     Lazy ruled cells do not get calculated until someone asks their 
value,
;;     and once they are evaluated and dependencies have been established,
;;     they merely will be flagged "obsolete" should any of those 
dependencies
;;     change in value.
;;
;; --- FN state rule ------------------------------------------------
;;
;; c? ends up wrapping its body in a lambda form which becomes the rule 
for this
;; slot, and here that lambda form will close over the MOVES hash-table. 
Neat, eh?
;; What is going on is that we do not anticipate in the application 
design that
;; any cell will depend in isolation on the move of one ticker in the 
index. So
;; we can allocate just one hashtable at make-instance time and reuse 
that each
;; time the rule gets evaluated. Cells depending on the state Cell will know
;; when that aggregate value gets recomputed because the finally clause 
conses
;; up a new list each time.
;;
;; --- FN without-c-dependency -------------------------------------
;;
;;   [to be written]
;;
;; --- FN value Cell --------------------------------------------------
;;
;; Weird, right? Well, we noticed that many trades came thru at the same 
price
;; sequentially. The rule above for STATE gets kicked off on each trade, 
and the
;; index gets recomputed. Because it is an aggregate, we get a new list 
for state
;; even if the trade was at an unchanged priced and the index value does 
not change.
;;
;; Now suppose there was some BUY! rule which cared only about the index 
value, and not
;; the latest minute traded of that value, which /would/ change if a new 
trade at
;; an unchanged price were received. Because a new list gets consed up 
(never mind the
;; new trade minute), The BUY! rule would get kicked off because of the 
new list in the
;; the STATE slot. Not even overriding the default EQL test with EQUAL 
would work,
;; because the trade minute would have changed.
;;
;; What to do? The above. Let VALUE get recalculated unnecessarily and 
return unchanged,
;; then code the BUY! rule to use VALUE. VALUE will get kicked off, but 
not BUY!, which
;; would likely be computationally intense.
;;

#| TRADEDATA
(:date 5123 :weekday 3)
(:index ((AA 29.30 7.3894672) (AIG 53.30 7.3894672)(AXP 53.00 7.3894672)
(BA 59.87 7.3894672) (C 46.80 7.3894672) (CAT 87.58 7.3894672) (DD 47.74 
7.3894672)
(DIS 26.25 7.3894672) (GE 36.10 7.3894672) (GM 27.77 7.3894672) (HD 
36.75 7.3894672)
(HON 35.30 7.3894672) (HPQ 21.00 7.3894672) (IBM 76.47 7.3894672)
(INTC 23.75 7.3894672) (JNJ 68.73 7.3894672) (JPM 35.50 7.3894672) (KO 
43.76 7.3894672)
(MCD 29.80 7.3894672) (MMM 76.76 7.3894672) (MO 65.99 7.3894672) (MRK 
34.42 7.3894672)
(MSFT 25.36 7.3894672) (PFE 27.5 7.3894672) (PG 54.90 7.3894672) (SBC 
23.8 7.3894672)
(UTX 100.96 7.3894672) (VZ 36.75 7.3894672) (WMT 48.40 7.3894672) (XOM 
56.50 7.3894672)))
(:trade INTC    0.001932 :last  23.75)
(:trade MSFT    0.001932 :last  25.36)
(:trade INTC    0.011931 :last  23.75)
(:trade MSFT    0.011931 :last  25.36)
(:trade MSFT    0.041965 :last  25.32)
(:trade UTX     0.067027 :last 101.39)
(:trade INTC    0.067062 :last  23.82)
(:trade MSFT    0.070397 :last  25.37)
(:trade INTC    0.070397 :last  23.82)
(:trade MSFT    0.074167 :last  25.32)
(:trade INTC    0.081800 :last  23.83)
(:trade MSFT    0.097178 :last  25.33)
(:trade MSFT    0.106488 :last  25.32)
(:trade INTC    0.110410 :last  23.82)
(:trade INTC    0.124263 :last  23.83)
(:trade MSFT    0.130411 :last  25.33)
(:trade INTC    0.143792 :last  23.81)
(:trade MSFT    0.143792 :last  25.33)
(:trade DIS     0.150441 :last  26.25)
(:trade INTC    0.160480 :last  23.82)
(:trade MSFT    0.160480 :last  25.33)
(:trade HPQ     0.166767 :last  21.00)
(:trade INTC    0.178832 :last  23.82)
(:trade MSFT    0.183710 :last  25.33)
(:trade DIS     0.187167 :last  26.25)
(:trade AIG     0.193117 :last  53.60)
(:trade INTC    0.196399 :last  23.81)
(:trade PFE     0.200523 :last  27.51)
(:trade MSFT    0.200523 :last  25.33)
(:trade GE      0.202185 :last  36.11)
(:trade MSFT    0.207199 :last  25.37)
(:trade BA      0.209810 :last  59.75)
(:trade INTC    0.210524 :last  23.83)
(:trade MSFT    0.230556 :last  25.37)
(:trade INTC    0.230556 :last  23.83)
(:trade BA      0.234812 :last  59.76)
(:trade MSFT    0.240580 :last  25.37)
(:trade INTC    0.247233 :last  23.83)
(:trade MSFT    0.256892 :last  25.37)
(:trade UTX     0.257729 :last 101.33)
(:trade GE      0.261942 :last  36.11)
(:trade AIG     0.267072 :last  53.60)
(:trade MSFT    0.272956 :last  25.36)
(:trade INTC    0.275617 :last  23.83)
(:trade WMT     0.280660 :last  48.40)
(:trade SBC     0.284975 :last  23.78)
(:trade GE      0.289229 :last  36.10)
(:trade MSFT    0.292285 :last  25.35)
(:trade DIS     0.295646 :last  26.30)
(:trade HPQ     0.303630 :last  21.04)
(:trade IBM     0.305629 :last  76.60)
(:trade INTC    0.307321 :last  23.81)
(:trade INTC    0.310671 :last  23.81)
(:trade SBC     0.316331 :last  23.76)
(:trade AIG     0.322292 :last  53.60)
(:trade MSFT    0.324057 :last  25.36)
(:trade MCD     0.324057 :last  29.79)
(:trade UTX     0.325694 :last 101.15)
(:trade INTC    0.327348 :last  23.81)
(:trade IBM     0.336878 :last  76.60)
(:trade MSFT    0.342414 :last  25.37)
(:trade MSFT    0.345710 :last  25.37)
(:trade HD      0.346983 :last  36.82)
(:trade BA      0.347295 :last  59.80)
(:trade MCD     0.360765 :last  29.80)
(:trade HPQ     0.364067 :last  21.03)
(:trade MSFT    0.364067 :last  25.37)
(:trade SBC     0.367409 :last  23.79)
(:trade MSFT    0.392928 :last  25.36)
(:trade AIG     0.407453 :last  53.55)
(:trade HPQ     0.407533 :last  21.03)
(:trade SBC     0.407533 :last  23.79)
(:trade MSFT    0.407533 :last  25.36)
(:trade INTC    0.407533 :last  23.82)
(:trade HPQ     0.407533 :last  21.03)
(:trade HD      0.407545 :last  36.84)
(:trade BA      0.413185 :last  59.80)
(:trade INTC    0.414117 :last  23.81)
(:trade PFE     0.420796 :last  27.51)
(:trade DIS     0.424120 :last  26.30)
(:trade AIG     0.424654 :last  53.58)
(:trade INTC    0.427471 :last  23.81)
(:trade XOM     0.429865 :last  56.85)
(:trade IBM     0.431927 :last  76.65)
(:trade HPQ     0.432407 :last  21.04)
(:trade HD      0.432507 :last  36.84)
(:trade MCD     0.439207 :last  29.80)
(:trade MSFT    0.442518 :last  25.36)
(:trade DIS     0.442518 :last  26.30)
(:trade MSFT    0.453747 :last  25.36)
(:trade PFE     0.458821 :last  27.52)
(:trade IBM     0.459026 :last  76.66)
(:trade HON     0.467342 :last  35.36)
(:trade XOM     0.469083 :last  56.88)
(:trade INTC    0.470871 :last  23.80)
(:trade SBC     0.476712 :last  23.79)
(:trade BA      0.476730 :last  59.80)
(:trade MCD     0.479248 :last  29.80)
(:trade HPQ     0.479248 :last  21.03)
(:trade AIG     0.480883 :last  53.57)
(:trade MSFT    0.482567 :last  25.36)
(:trade INTC    0.482567 :last  23.80)
(:trade IBM     0.484223 :last  76.73)
(:trade MSFT    0.494243 :last  25.36)
(:trade AIG     0.497551 :last  53.57)
(:trade PFE     0.497569 :last  27.53)
(:trade INTC    0.504245 :last  23.80)
(:trade HD      0.504660 :last  36.84)
(:trade IBM     0.504849 :last  76.73)
(:trade GM      0.507621 :last  30.53)
(:trade SBC     0.511484 :last  23.79)
(:trade HPQ     0.514265 :last  21.04)
(:trade HD      0.514798 :last  36.85)
(:trade MSFT    0.517601 :last  25.32)
(:trade WMT     0.524286 :last  48.46)
(:trade IBM     0.524286 :last  76.74)
(:trade INTC    0.529220 :last  23.80)
(:trade HPQ     0.536813 :last  21.04)
(:trade PG      0.537627 :last  54.91)
(:trade PFE     0.540979 :last  27.54)
(:trade INTC    0.544290 :last  23.80)
(:trade PG      0.547549 :last  54.91)
(:trade XOM     0.547624 :last  56.85)
(:trade HON     0.547687 :last  35.40)
(:trade UTX     0.550986 :last 101.33)
(:trade HD      0.555694 :last  36.85)
(:trade MSFT    0.560792 :last  25.35)
(:trade INTC    0.564337 :last  23.80)
(:trade XOM     0.566779 :last  56.85)
(:trade BA      0.567359 :last  59.81)
(:trade HON     0.581023 :last  35.41)
(:trade INTC    0.589796 :last  23.80)
(:trade BA      0.596050 :last  59.80)
(:trade CAT     0.612134 :last  87.83)
(:trade WMT     0.618386 :last  48.44)
(:trade INTC    0.620474 :last  23.80)
(:trade MCD     0.624417 :last  29.80)
(:trade MSFT    0.627748 :last  25.35)
(:trade BA      0.630881 :last  59.83)
(:trade AIG     0.634410 :last  53.56)
(:trade MCD     0.637785 :last  29.79)
(:trade HON     0.637785 :last  35.40)
(:trade INTC    0.649577 :last  23.79)
(:trade BA      0.655889 :last  59.85)
(:trade HD      0.662287 :last  36.83)
(:trade AIG     0.669431 :last  53.53)
(:trade HON     0.671133 :last  35.44)
(:trade MCD     0.674457 :last  29.79)
(:trade MO      0.683443 :last  66.20)
(:trade INTC    0.687668 :last  23.79)
(:trade MSFT    0.691181 :last  25.35)
(:trade PFE     0.694477 :last  27.54)
(:trade MSFT    0.720936 :last  25.35)
(:trade GM      0.726237 :last  30.50)
(:trade WMT     0.730056 :last  48.40)
(:trade IBM     0.740544 :last  76.74)
(:trade PG      0.744569 :last  54.91)
(:trade HON     0.752103 :last  35.46)
(:trade CAT     0.753014 :last  87.85)
(:trade MO      0.763918 :last  66.20)
(:trade MSFT    0.764592 :last  25.35)
(:trade HON     0.771289 :last  35.46)
(:trade BA      0.772935 :last  59.75)
(:trade JPM     0.773229 :last  35.51)
(:trade MSFT    0.774612 :last  25.35)
(:trade PG      0.776267 :last  54.91)
(:trade AIG     0.781168 :last  53.54)
(:trade HD      0.782946 :last  36.87)
(:trade CAT     0.784614 :last  87.85)
(:trade XOM     0.786285 :last  56.88)
(:trade MSFT    0.792950 :last  25.36)
(:trade UTX     0.794689 :last 101.40)
(:trade INTC    0.797969 :last  23.78)
(:trade IBM     0.801301 :last  76.74)
(:trade HD      0.809652 :last  36.87)
(:trade JPM     0.809652 :last  35.51)
(:trade MSFT    0.811489 :last  25.37)
(:trade MO      0.812994 :last  66.20)
(:trade IBM     0.816563 :last  76.75)
(:trade MCD     0.828046 :last  29.77)
(:trade UTX     0.829055 :last 101.37)
(:trade MSFT    0.833420 :last  25.36)
(:trade GM      0.837650 :last  30.50)
(:trade IBM     0.838004 :last  76.75)
(:trade HON     0.838531 :last  35.47)
(:trade XOM     0.841372 :last  56.88)
(:trade MCD     0.841894 :last  29.78)
(:trade KO      0.853202 :last  43.98)
(:trade UTX     0.858235 :last 101.38)
(:trade INTC    0.864331 :last  23.82)
(:trade PFE     0.869104 :last  27.55)
(:trade HON     0.873063 :last  35.48)
(:trade IBM     0.873095 :last  76.77)
(:trade HD      0.873132 :last  36.87)
(:trade XOM     0.884796 :last  56.86)
(:trade UTX     0.884820 :last 101.38)
(:trade HON     0.888886 :last  35.48)
(:trade INTC    0.891420 :last  23.81)
(:trade CAT     0.895715 :last  87.86)
(:trade MO      0.898111 :last  66.19)
(:trade XOM     0.898111 :last  56.87)
(:trade IBM     0.899775 :last  76.78)
(:trade BA      0.899775 :last  59.83)
(:trade MSFT    0.901469 :last  25.38)
(:trade HD      0.906673 :last  36.86)
(:trade HPQ     0.908113 :last  21.03)
(:trade CAT     0.916467 :last  87.85)
(:trade BA      0.916467 :last  59.83)
(:trade MSFT    0.918773 :last  25.38)
(:trade PFE     0.926271 :last  27.57)
(:trade MO      0.926288 :last  66.18)
(:trade WMT     0.929791 :last  48.40)
(:trade KO      0.932333 :last  43.98)
(:trade JNJ     0.933224 :last  68.15)
(:trade PG      0.936516 :last  54.91)
(:trade INTC    0.938989 :last  23.81)
(:trade IBM     0.942596 :last  76.78)
(:trade XOM     0.944052 :last  56.89)
(:trade INTC    0.944885 :last  23.81)
(:trade BA      0.946486 :last  59.85)
(:trade IBM     0.958178 :last  76.78)
(:trade INTC    0.959853 :last  23.81)
(:trade JPM     0.959897 :last  35.50)
(:trade WMT     0.961498 :last  48.40)
(:trade MCD     0.963195 :last  29.77)
(:trade HPQ     0.966525 :last  21.03)
(:trade AIG     0.968663 :last  53.54)
(:trade XOM     0.978210 :last  56.89)
(:trade AIG     0.979896 :last  53.55)
(:trade CAT     0.979896 :last  87.85)
(:trade MCD     0.984732 :last  29.77)
(:trade PG      0.985307 :last  54.90)
(:trade WMT     0.995716 :last  48.41)
(:trade MSFT    1.005256 :last  25.38)
(:trade PFE     1.005256 :last  27.55)
(:trade JPM     1.008448 :last  35.48)
(:trade CAT     1.011343 :last  87.86)
(:trade XOM     1.011825 :last  56.88)
(:trade INTC    1.012667 :last  23.79)
(:trade JNJ     1.018655 :last  68.15)
(:trade KO      1.021589 :last  43.99)
(:trade INTC    1.026597 :last  23.78)
(:trade HD      1.029577 :last  36.85)
(:trade MSFT    1.029936 :last  25.39)
(:trade JPM     1.033267 :last  35.49)
(:trade C       1.064996 :last  46.80)
(:trade CAT     1.065946 :last  87.85)
(:trade MCD     1.066687 :last  29.75)
(:trade MRK     1.066687 :last  34.33)
(:trade PFE     1.066687 :last  27.55)
(:trade INTC    1.066687 :last  23.79)
(:trade INTC    1.066687 :last  23.79)
(:trade XOM     1.068360 :last  56.88)
(:trade JPM     1.068360 :last  35.49)
(:trade XOM     1.068360 :last  56.89)
(:trade KO      1.068360 :last  43.99)
(:trade MRK     1.070274 :last  34.34)
(:trade HON     1.073312 :last  35.49)
(:trade PFE     1.080025 :last  27.55)
(:trade MCD     1.080025 :last  29.75)
(:trade INTC    1.080025 :last  23.79)
(:trade AIG     1.083337 :last  53.55)
(:trade GM      1.083420 :last  30.55)
(:trade XOM     1.086739 :last  56.89)
(:trade HON     1.093425 :last  35.49)
(:trade HPQ     1.093425 :last  21.03)
(:trade INTC    1.093425 :last  23.79)
(:trade MSFT    1.093425 :last  25.37)
(:trade JPM     1.098339 :last  35.49)
(:trade IBM     1.099113 :last  76.86)
(:trade XOM     1.104257 :last  56.89)
(:trade MCD     1.104268 :last  29.74)
(:trade GE      1.108379 :last  36.14)
(:trade MSFT    1.108408 :last  25.40)
(:trade XOM     1.115052 :last  56.89)
(:trade JPM     1.118397 :last  35.50)
(:trade GM      1.118397 :last  30.55)
(:trade C       1.125426 :last  46.78)
(:trade MCD     1.132390 :last  29.74)
(:trade WMT     1.133494 :last  48.40)
(:trade MRK     1.135099 :last  34.33)
(:trade MSFT    1.135099 :last  25.39)
(:trade INTC    1.135099 :last  23.78)
(:trade INTC    1.146096 :last  23.79)
(:trade KO      1.146108 :last  43.99)
(:trade WMT     1.155346 :last  48.41)
(:trade PG      1.158447 :last  54.90)
(:trade WMT     1.162645 :last  48.41)
(:trade HON     1.162660 :last  35.52)
(:trade KO      1.162672 :last  43.98)
(:trade JNJ     1.166783 :last  68.20)
(:trade DIS     1.166815 :last  26.34)
(:trade HD      1.166856 :last  36.90)
(:trade MCD     1.171129 :last  29.74)
(:trade INTC    1.175130 :last  23.79)
(:trade JPM     1.178485 :last  35.50)
(:trade KO      1.178485 :last  43.98)
(:trade MSFT    1.184447 :last  25.39)
(:trade AIG     1.191811 :last  53.56)
(:trade WMT     1.195138 :last  48.41)
(:trade MSFT    1.199050 :last  25.39)
(:trade MO      1.201440 :last  66.18)
(:trade INTC    1.201841 :last  23.80)
(:trade DIS     1.201841 :last  26.34)
(:trade JNJ     1.202292 :last  68.20)
(:trade C       1.205172 :last  46.79)
(:trade KO      1.205172 :last  43.98)
(:trade WMT     1.209557 :last  48.40)
(:trade INTC    1.209927 :last  23.79)
(:trade VZ      1.209962 :last  34.75)
(:trade MSFT    1.213558 :last  25.37)
(:trade C       1.220169 :last  46.79)
(:trade DIS     1.220225 :last  26.34)
(:trade PFE     1.220225 :last  27.55)
(:trade JNJ     1.220921 :last  68.20)
(:trade MMM     1.223614 :last  76.70)
(:trade INTC    1.226875 :last  23.79)
(:trade DIS     1.230230 :last  26.34)
(:trade HPQ     1.230230 :last  21.03)
(:trade HON     1.230230 :last  35.52)
(:trade PFE     1.230230 :last  27.56)
(:trade SBC     1.230230 :last  23.78)
(:trade C       1.236915 :last  46.79)
(:trade MSFT    1.240577 :last  25.40)
(:trade DIS     1.243960 :last  26.34)
(:trade SBC     1.250258 :last  23.78)
(:trade MCD     1.250258 :last  29.74)
(:trade MSFT    1.250258 :last  25.40)
(:trade INTC    1.253588 :last  23.79)
(:trade HON     1.253588 :last  35.53)
(:trade MCD     1.257704 :last  29.74)
(:trade MSFT    1.262803 :last  25.37)
(:trade KO      1.271926 :last  43.99)
(:trade JPM     1.271926 :last  35.51)
(:trade VZ      1.276339 :last  34.75)
(:trade MSFT    1.280283 :last  25.40)
(:trade HPQ     1.280283 :last  21.03)
(:trade DIS     1.288624 :last  26.34)
(:trade GE      1.288664 :last  36.14)
(:trade JPM     1.288664 :last  35.51)
(:trade AIG     1.290300 :last  53.59)
(:trade CAT     1.290300 :last  87.86)
(:trade IBM     1.290300 :last  76.85)
(:trade SBC     1.291940 :last  23.77)
(:trade XOM     1.301948 :last  56.88)
(:trade DIS     1.303625 :last  26.34)
(:trade AIG     1.304047 :last  53.60)
(:trade KO      1.305316 :last  43.99)
(:trade JPM     1.305316 :last  35.51)
(:trade C       1.305316 :last  46.79)
(:trade KO      1.314761 :last  43.99)
(:trade DIS     1.316972 :last  26.35)
(:trade HON     1.316972 :last  35.54)
(:trade CAT     1.317022 :last  87.86)
(:trade IBM     1.317022 :last  76.85)
(:trade GE      1.318640 :last  36.15)
(:trade WMT     1.320354 :last  48.41)
(:trade HPQ     1.322354 :last  21.04)
(:trade AIG     1.331152 :last  53.59)
(:close)
|#

(defun msg-start (m)
  (search "TRADEDATA" m))





More information about the cello-devel mailing list