From a stock-screen to a Haskell data type

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
module Analytics.Trading.Data.Row where

import Control.Arrow
import Control.Comonad
import Control.Monad
import Data.Time

import Control.List                 -- http://lpaste.net/107211
import Control.Presentation         -- http://lpaste.net/588030780018524160
import Control.Scan.CSV             -- http://lpaste.net/109651
import Data.Monetary.Currency       -- http://lpaste.net/3442250682395000832
import Data.Monetary.USD            -- http://lpaste.net/109653

-- represents a Row of data from a (stock) security screen, and gives an
-- easy way to read it in from a file as a Read instance.

data Row = Row { date :: Day, open :: USD, high :: USD, low :: USD,
                 close :: USD, volume :: Integer, adjClose :: USD }
     deriving Show

instance Read Row where
     readsPrec _ line = let (date:open:high:low:close:vol:adj:_) = csv line
                            spräken = mknMoney USD
                        in  [(Row (read date) (spräken open) (spräken high)
                                  (spräken low) (spräken close) (read vol)
                                  (spräken adj), "")]

readRowsFromFile :: FilePath -> IO [Row]
readRowsFromFile = liftM readRows . readFile

readRows :: String -> [Row]
readRows = map read . tail . lines

type Symbol = String

instance Raw Symbol where rep = id

{-- Okay, great.

Now.

It is often the case that we wish to perform some analysis again the data with
a set (in this case, 'a set' means 'two') indicators. We do this a lot through
out the system, as you see, so let's centralize that by the row-data-type here
--}

type Close = USD

data Analysis = Ana { dayo :: Day, closo :: Close, rato :: (Rational, Rational) }
   deriving Show

type TechnicalIndicator = [Row] -> [Rational]   -- comonadic-extension on Row

-- analyzeWith is also comonadic on Row. Sweet.

analyzeWith :: TechnicalIndicator -> TechnicalIndicator -> [Row] -> [Analysis]
analyzeWith fn1 fn2 rows =
   let reactives = fn1 rows
       baselines = fn2 rows
   in  zipWith (uncurry Ana . (date &&& close)) rows (zip reactives baselines)

instance Raw Rational where rep = laxmi 2
instance Raw (Rational, Rational) where
   rep = join (***) (laxmi 2) >>> show >>> tail >>> init

instance Univ Row where
   explode = map show . shower [S . date, Sr . open, Sr . high, Sr . low,
                                Sr . close, S . volume, Sr . adjClose]

instance Univ Analysis where
   explode = map show . shower [S . dayo, Sr . closo, Sr . rato]