/* Richard A. DeVenezia * www.devenezia.com * */ /* Original version posted Dec. 18, 2003 on SAS-L in * response to a post made by Dorian Noel titled: * Time Interval Aggregation to an Event. */ /* Problem Essence: * At time T, a computation based on prior observations * occurring within prior interval I is to be made. * At each T there are a variant number of observations * not exceeeding NMOST in the prior INTERVAL */ /* Solution Essence: * Ring arrays are used to store values from prior observations needed * in the computation. The size of the ring should equal or * exceed (recommended) the most number of observations that * could occur in an interval. */ /* In this specific application, the computation * involves summing transactions of type buy and sell */ %* %* generate some random trading data occurring between 8am and 4pm %* - each time tick is at most 50 seconds after the previous time tick %* - each time tick has a 50% chance of both buy and sell %* - each buy or sell involves a random volume %* - all random numbers are selected from a poisson distribution %*; data transactions; retain symbol 'foo'; retain seed 11063; time = '08:00:00't; do until (time > '16:00:00't); time + max(1,int(ranpoi(seed,50)/100)); if ranuni(0) < 0.5 then do; action = 'sell'; volume = ranpoi(0,20); output; end; if ranuni(0) < 0.5 then do; action = 'buy'; volume = ranpoi(0,10); output; end; end; keep symbol time action volume; run; %* generously large: most number of transactions that can occurr in interval I; %let NMOST = 1000; %let INTERVAL = 180; data rollingSums ; array _sellTimeRing [&NMOST] _temporary_; array _buy_TimeRing [&NMOST] _temporary_; array _sellVolumeRing [&NMOST] _temporary_; array _buy_VolumeRing [&NMOST] _temporary_; retain _sellIndex 1; retain _buy_Index 1; set transactions; * compute sum of buy and sell volumes in prior interval; * do not include current volumes of current transaction; * buy; _ix = _buy_Index-1; if _ix < 1 then _ix = &NMOST; backtime = _buy_TimeRing[_ix]; rollsum = 0; do while (backtime >= time - &INTERVAL); rollsum + _buy_VolumeRing[_ix]; _ix = _ix-1; if _ix < 1 then _ix = &NMOST; if _ix = _buy_Index then do; put "ERROR: buy ring exceeded its capacity (&NMOST) " time=; stop; end; backtime = _buy_TimeRing[_ix]; end; bvol = rollsum; * sell; _ix = _sellIndex-1; if _ix < 1 then _ix = &NMOST; backtime = _sellTimeRing[_ix]; rollsum = 0; do while (backtime >= time - &INTERVAL); rollsum + _sellVolumeRing[_ix]; _ix = _ix-1; if _ix < 1 then _ix = &NMOST; if _ix = _sellIndex then do; put "ERROR: sell ring exceeded its capacity (&NMOST) " time=; stop; end; backtime = _sellTimeRing[_ix]; end; svol = -rollsum; net_vol = bvol + svol; * update ring arrays; if action = 'sell' then do; _sellTimeRing[_sellIndex] = time; _sellVolumeRing [_sellIndex] = volume; _sellIndex+1; if _sellIndex > &NMOST then _sellIndex = 1; end; else do; _buy_TimeRing[_buy_Index] = time; _buy_VolumeRing [_buy_Index] = volume; _buy_Index+1; if _buy_Index > &NMOST then _buy_Index = 1; end; keep time action volume bvol svol net_vol; run; goptions reset=all; goptions target=pdf hsize=8in vsize=8in ftext='Arial'; symbol1 v=point; *filename gsfname "\\extreme\samples\rollingvol.png"; *goptions device=png target=png hsize=1in vsize=1in gsfname=gsfname htext=2pct; proc gplot data=rollingSums; title h=10pct "Net Volumes"; title2 h=5pct "Prior interval &INTERVAL seconds"; plot net_vol * time; format time time8.; run; quit; goptions device=win gsfname=;