A BUY/SELL/HOLD Monoidal Strategy

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
module Analytics.Trading.Data.Advice where

-- http://lpaste.net/109712

import Control.Arrow

import Data.Monoid
import Data.Time

import Data.Monetary.USD                   -- http://lpaste.net/109597

-- buy/sell/hold monoiodal advice

infixr 2 |-

data Advice = BUY | SELL | HOLD
   deriving (Eq, Ord, Show, Read)

instance Monoid Advice where
   mempty = HOLD
   mappend HOLD x = x
   mappend x    _ = x

-- monoidal implication; like monadic guard, but instead of returning ()
-- as the continuation, the monoidal value is preserved.

(|-) :: Monoid m => m -> Bool -> m
_ |- False = mempty
ans |- _   = ans

invert :: Advice -> Advice
invert BUY = SELL
invert SELL = BUY
invert HOLD = HOLD

-- here're our strategies in practice ... we'll fine-tune them as we go ...

buy :: Double -> [(USD, USD)] -> Advice
buy perc run@(today:rest) =
   BUY |- all (uncurry (>)) run && inSpan id perc today

sell :: Double -> [(USD, USD)] -> Advice
sell perc ran@(today:_) = -- invert (buy perc $ map (negate *** negate) ran)
   SELL |- all (uncurry (<)) ran && inSpan negate perc today

inSpan :: (USD -> USD) -> Double -> (USD, USD) -> Bool
inSpan fn percent today = fn (uncurry (-) today) > USD percent * fst today

buyOrSell :: Double -> [(USD, USD)] -> Advice
buyOrSell perc = buy perc &&& sell perc >>> uncurry mappend