{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}

module Deal.DealMod (modDeal, ModifyType(..), AdjStrategy(..)
                     )                      
  where

import Data.Aeson
import Data.Aeson.Types
import Data.Aeson.TH
import Data.Aeson.Encode.Pretty (encodePretty)
import Servant.OpenApi
import Data.OpenApi hiding (Server,contentType,trace)

import qualified Accounts as A
import qualified Ledger as LD
import qualified Asset as Ast
import qualified Expense as F
import qualified Liability as L
import qualified CreditEnhancement as CE
import qualified Hedge as HE
import qualified Waterfall as W
import qualified Cashflow as CF
import qualified Assumptions as AP
import qualified AssetClass.AssetBase as ACM
import qualified Call as C
import qualified InterestRate as IR
import qualified Util as U
import qualified Deal.DealBase as DB
import Stmt
import Lib
import Util
import DateUtil
import Types
import Revolving
import Triggers

import qualified Data.Map as Map
import qualified Data.Time as T
import qualified Data.Set as S
import Data.List
import Data.Fixed
import Data.Maybe
import Data.Ratio
import Data.Aeson hiding (json)
-- import qualified Data.Aeson.Encode.Pretty as Pretty
import Language.Haskell.TH
import Data.Aeson.TH
import Data.Aeson.Types
import GHC.Generics
import Control.Lens hiding (element)
import Control.Lens.TH
import Data.IntMap (filterWithKey)
import qualified Data.Text as T
import Text.Read (readMaybe)
import qualified Pool as P
import qualified Types as CF

import Debug.Trace
import qualified Control.Lens as P
debug :: c -> String -> c
debug = (String -> c -> c) -> c -> String -> c
forall a b c. (a -> b -> c) -> b -> a -> c
flip String -> c -> c
forall a. String -> a -> a
trace


data AdjStrategy = ScaleBySpread
                 | ScaleByFactor
                 deriving (Int -> AdjStrategy -> ShowS
[AdjStrategy] -> ShowS
AdjStrategy -> String
(Int -> AdjStrategy -> ShowS)
-> (AdjStrategy -> String)
-> ([AdjStrategy] -> ShowS)
-> Show AdjStrategy
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> AdjStrategy -> ShowS
showsPrec :: Int -> AdjStrategy -> ShowS
$cshow :: AdjStrategy -> String
show :: AdjStrategy -> String
$cshowList :: [AdjStrategy] -> ShowS
showList :: [AdjStrategy] -> ShowS
Show,(forall x. AdjStrategy -> Rep AdjStrategy x)
-> (forall x. Rep AdjStrategy x -> AdjStrategy)
-> Generic AdjStrategy
forall x. Rep AdjStrategy x -> AdjStrategy
forall x. AdjStrategy -> Rep AdjStrategy x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. AdjStrategy -> Rep AdjStrategy x
from :: forall x. AdjStrategy -> Rep AdjStrategy x
$cto :: forall x. Rep AdjStrategy x -> AdjStrategy
to :: forall x. Rep AdjStrategy x -> AdjStrategy
Generic)

data ModifyType = AddSpreadToBonds BondName
                | SlideBalances BondName BondName
                deriving (Int -> ModifyType -> ShowS
[ModifyType] -> ShowS
ModifyType -> String
(Int -> ModifyType -> ShowS)
-> (ModifyType -> String)
-> ([ModifyType] -> ShowS)
-> Show ModifyType
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ModifyType -> ShowS
showsPrec :: Int -> ModifyType -> ShowS
$cshow :: ModifyType -> String
show :: ModifyType -> String
$cshowList :: [ModifyType] -> ShowS
showList :: [ModifyType] -> ShowS
Show,(forall x. ModifyType -> Rep ModifyType x)
-> (forall x. Rep ModifyType x -> ModifyType) -> Generic ModifyType
forall x. Rep ModifyType x -> ModifyType
forall x. ModifyType -> Rep ModifyType x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. ModifyType -> Rep ModifyType x
from :: forall x. ModifyType -> Rep ModifyType x
$cto :: forall x. Rep ModifyType x -> ModifyType
to :: forall x. Rep ModifyType x -> ModifyType
Generic)

-- ^ Modify a deal by various type of recipes
modDeal :: Ast.Asset a => ModifyType -> Double -> DB.TestDeal a -> DB.TestDeal a
modDeal :: forall a.
Asset a =>
ModifyType -> Double -> TestDeal a -> TestDeal a
modDeal (AddSpreadToBonds String
bnd) Double
sprd TestDeal a
d 
  = let 
      sprd' :: Spread
sprd' = (Rational -> Spread
forall a. Fractional a => Rational -> a
fromRational (Rational -> Spread) -> (Double -> Rational) -> Double -> Spread
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Double -> Rational
forall a. Real a => a -> Rational
toRational) Double
sprd
      bndMap :: Map String Bond
bndMap = TestDeal a -> Map String Bond
forall a. TestDeal a -> Map String Bond
DB.bonds TestDeal a
d
      bndMap' :: Map String Bond
bndMap' = (Bond -> Bond) -> [String] -> Map String Bond -> Map String Bond
forall k a. Ord k => (a -> a) -> [k] -> Map k a -> Map k a
U.mapWithinMap 
                  (\Bond
b -> Bond
b Bond -> (Bond -> Bond) -> Bond
forall a b. a -> (a -> b) -> b
& (InterestInfo -> Identity InterestInfo) -> Bond -> Identity Bond
Traversal' Bond InterestInfo
L.interestInfoTraversal ((InterestInfo -> Identity InterestInfo) -> Bond -> Identity Bond)
-> (InterestInfo -> InterestInfo) -> Bond -> Bond
forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ Spread -> InterestInfo -> InterestInfo
L.adjInterestInfoBySpread Spread
sprd'
                           Bond -> (Bond -> Bond) -> Bond
forall a b. a -> (a -> b) -> b
& (Spread -> Identity Spread) -> Bond -> Identity Bond
Traversal' Bond Spread
L.curRatesTraversal ((Spread -> Identity Spread) -> Bond -> Identity Bond)
-> (Spread -> Spread) -> Bond -> Bond
forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ (Spread -> Spread -> Spread
forall a. Num a => a -> a -> a
+ Spread
sprd')) 
                  [String
bnd]
                  Map String Bond
bndMap
    in 
      TestDeal a
d {DB.bonds = bndMap'}

modDeal (SlideBalances String
bn1 String
bn2) Double
r d :: TestDeal a
d@DB.TestDeal {bonds :: forall a. TestDeal a -> Map String Bond
DB.bonds = Map String Bond
bndMap}
  = let 
      totalBalance :: Balance
totalBalance = [Balance] -> Balance
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ([Balance] -> Balance) -> [Balance] -> Balance
forall a b. (a -> b) -> a -> b
$ OriginalInfo -> Balance
L.originBalance (OriginalInfo -> Balance)
-> (Bond -> OriginalInfo) -> Bond -> Balance
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bond -> OriginalInfo
L.bndOriginInfo (Bond -> Balance) -> [Bond] -> [Balance]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TestDeal a -> [String] -> [Bond]
forall a. Asset a => TestDeal a -> [String] -> [Bond]
DB.viewDealBondsByNames TestDeal a
d [String
bn1, String
bn2]
      leftBal :: Balance
leftBal = Balance -> Rational -> Balance
mulBR Balance
totalBalance (Double -> Rational
forall a. Real a => a -> Rational
toRational Double
r) -- `debug` ("split ratio" ++ show r)
      rightBal :: Balance
rightBal = Balance
totalBalance Balance -> Balance -> Balance
forall a. Num a => a -> a -> a
- Balance
leftBal 
      bndMap' :: Map String Bond
bndMap' = String -> (Bond -> Bond) -> Map String Bond -> Map String Bond
DB.updateBondInMap String
bn1 (Balance -> Bond -> Bond
L.adjustBalance Balance
leftBal) (Map String Bond -> Map String Bond)
-> Map String Bond -> Map String Bond
forall a b. (a -> b) -> a -> b
$
                 String -> (Bond -> Bond) -> Map String Bond -> Map String Bond
DB.updateBondInMap String
bn2 (Balance -> Bond -> Bond
L.adjustBalance Balance
rightBal) Map String Bond
bndMap -- `debug` ("leftBal: " ++ show leftBal ++ ", rightBal: " ++ show rightBal )
    in 
      TestDeal a
d {DB.bonds = bndMap'}

modDeal ModifyType
x Double
_ TestDeal a
_ = String -> TestDeal a
forall a. HasCallStack => String -> a
error (String -> TestDeal a) -> String -> TestDeal a
forall a b. (a -> b) -> a -> b
$ String
"modify deal: not implemented"String -> ShowS
forall a. [a] -> [a] -> [a]
++ ModifyType -> String
forall a. Show a => a -> String
show ModifyType
x


$(deriveJSON defaultOptions ''AdjStrategy)
instance ToSchema AdjStrategy

$(deriveJSON defaultOptions ''ModifyType)
instance ToSchema ModifyType