Difference between revisions of "Programming performance/TimN Haskell"

From HaskellWiki
Jump to navigation Jump to search
(initial import)
(No difference)

Revision as of 03:41, 7 March 2007

  • Language: Haskell
  • Skill: Intermediate. I'm self taught in Haskell and use it as a hobbyist but haven't used it much for serious work.
  • Time: Aprox 45 - 90 minutes. I did not keep track of time well and this is just an estimate.
  • Notes: I had previously written similar code in python. I feel overall this was a bit harder for me to write than the python code and it took me a bit longer. My resulting haskell code is definitely on the imperative side and is longer and more complex than my python code. The dprint function can be altered to give more or less output.

Code

#!/usr/bin/env runhaskell
module Main where
import Control.Monad
import Control.Monad.State
import Data.List
import Text.Printf

io = lift

dprint :: String -> StateT Owned IO ()
dprint = io . putStrLn
--dprint x = io $ return ()

type Data = [(String,Float)]        -- list of date, closing price

-- | Parse data from raw file bytes.  Columns as:
-- Date Open High Low Close Volume Adj Close
parseData :: String -> Data
parseData = map (mkData.words).filter ((/= '#').head).lines
    where mkData [d,o,h,l,c,a,c2] = (d, read c2)
          mkData xs = error (show xs)

readData :: IO Data
readData = return.parseData =<< readFile "gspc"

data Owned = Owned {
        cash :: Float,
        stocks :: Float,
        queue :: [(Float, Float)]     -- quant and price
    } deriving(Show)
newOwnings = Owned 10000 0 []

type DeltaData = [(String,Float,Float)]  -- list of date, close, delta

delta c1 c2 = (c2-c1)/c1

mkDelta :: Data -> DeltaData
mkDelta dat = zipWith f dat (drop 1 dat) where
    f (d1,c1) (d2,c2) = (d1, c2, delta c1 c2)

buy :: String -> Float -> Float -> StateT Owned IO ()
buy date p n = do
    if n > 0 
        then dprint $ printf "%s buy %.2f at $%.2f" date n p
        else dprint $ printf "%s sell %.2f at $%.2f" date (negate n) p
    c <- gets cash
    s <- gets stocks
    modify $ \o -> o {cash = c-n*p, stocks = s+n}

remember p n = do
    q <- gets queue
    modify $ \s -> s {queue = (p, n) : q}

pop :: StateT Owned IO (Float,Float)
pop = do
    q <- gets queue
    modify $ \s -> s {queue = tail q}
    return $ head q

buyAndRemember date c p = do
    let n = c / p
    buy date p n
    remember p n

processQueue :: String -> Float -> StateT Owned IO ()
processQueue date price = processQueue' where
    processQueue' = do
        q <- gets queue
        when ((not.null) q) (do
            let (p,n) = head q
            when (delta p price > 0.06) (do
                buy date price (negate n)
                modify $ \s -> s {queue = tail q}
                processQueue'
             )
         )

buyTrigger = -0.03
sellTrigger = 0.06

strategy :: Data -> StateT Owned IO ()
strategy dat = do
    forM_ (mkDelta dat) (\(date,price,delta) -> do
        c <- gets ((* 0.10).cash)
        when (delta < buyTrigger) (buyAndRemember date c price)
        processQueue date price
     )
    when ((not.null) dat) (do
        let (date,price) = last dat
        n <- gets stocks
        buy date price (negate n)
     )

runStrategy dat = do
    owned <- execStateT (strategy dat) newOwnings
    return $ cash owned

main = do
    dat <- liftM reverse readData
    res <- runStrategy dat
    print res