module QuantLib.Quotes
        ( module QuantLib.Quotes
        ) where

import Data.Maybe
import QuantLib.Options
import QuantLib.PricingEngines.BlackFormula

-- | Base type class for market observables
class Quote a where
        qValue :: a->Maybe Double
        pureValue :: a->Double
        pureValue x = fromMaybe 0.0 (qValue x)

-- | Market element returning a stored value
data SimpleQuote = SimpleQuote (Maybe Double)
        deriving (Show, Eq)

instance Quote SimpleQuote where
        qValue (SimpleQuote x) = x

-- | Market element whose value depends on two other market elements
data CompositeQuote a = CompositeQuote {
        -- | First element
        cqQuote1        :: a,
        -- | Second element
        cqQuote2        :: a,
        -- | Composition function
        cqComposite     :: a->a->Maybe Double
        }

instance Quote (CompositeQuote a) where
        qValue x        = cqComposite x (cqQuote1 x) (cqQuote2 x)

-- | Market element whose value depends on another quote
data DerivedQuote a = DerivedQuote {
        dqQuote         :: a,
        dqDerivateFunc  :: a->Maybe Double
        }

instance Quote (DerivedQuote a) where
        qValue x        = dqDerivateFunc x (dqQuote x)

-- | Quote for the implied standard deviation of an underlying
data ImpliedStdDevQuote a = ImpliedStdDevQuote {
        isdqOptionType  :: OptionType,
        isdqForward     :: a,
        isdqPrice       :: a,
        isdqStrike      :: Double,
        isdqGuess       :: Maybe Double
        } deriving (Show)

instance Quote a => Quote (ImpliedStdDevQuote a) where
        qValue (ImpliedStdDevQuote opType fwd price strike guess) 
                = blackFormulaImpliedStdDev opType (pureValue fwd) (pureValue price) strike 1.0 0.0 guess 1.0e-6 100

-- | Quote for the Eurodollar-future implied standard deviation
data EurodollarFutureQuote a = EurodollarFutureQuote {
        efqForward      :: a,
        efqCallPrice    :: a,
        efqPutPrice     :: a,
        efqStrike       :: Double,
        efqGuess        :: Maybe Double
        } deriving (Show)

instance Quote a => Quote (EurodollarFutureQuote a) where
        qValue (EurodollarFutureQuote forward callPrice putPrice strike guess)
                | strike > forwardValue = blackFormulaImpliedStdDev Call strike forwardValue putValue 1.0 0.0 guess 1.0e-6 100
                | otherwise     = blackFormulaImpliedStdDev Put strike forwardValue callValue 1.0 0.0 guess 1.0e-6 100
                where
                        forwardValue = 100.0 - fromMaybe 0.0 (qValue forward)
                        putValue     = fromMaybe 0.0 (qValue putPrice)
                        callValue    = fromMaybe 0.0 (qValue callPrice)