{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Types
(DayCount(..),DateType(..)
,DatePattern(..)
,BondName,BondNames,FeeName,FeeNames,AccName,AccNames,AccountName
,Ts(..),TsPoint(..),PoolSource(..)
,PerPoint(..),PerCurve(..),getValFromPerCurve
,Period(..), Threshold(..)
,RangeType(..),CutoffType(..),DealStatus(..)
,Balance,Index(..)
,Cmp(..),TimeHorizion(..)
,Date,Dates,TimeSeries(..),IRate,Amount,Rate,StartDate,EndDate,Lag
,Spread,Floor,Cap,Interest,Principal,Cash,Default,Loss,Rental,PrepaymentPenalty
,SplitType(..),BookItem(..),BookItems,BalanceSheetReport(..),CashflowReport(..)
,Floater,CeName,RateAssumption(..)
,PrepaymentRate,DefaultRate,RecoveryRate,RemainTerms,Recovery,Prepayment
,Table(..),lookupTable,Direction(..),epocDate,BorrowerNum
,Txn(..),TxnComment(..)
,RoundingBy(..),DateDirection(..)
,BookDirection(..),IRR(..),DealCycle(..),Limit(..),Pre(..)
,Liable(..),CumPrepay,CumDefault,CumDelinq,CumPrincipal,CumLoss,CumRecovery,PoolId(..)
,DealName,lookupIntervalTable,CutoffFields(..),PriceResult(..)
,DueInt,DuePremium, DueIoI,DateVector,DealStats(..)
,PricingMethod(..),CustomDataType(..),ResultComponent(..),DealStatType(..)
,ActionWhen(..),DealStatFields(..)
,getDealStatType,getPriceValue,preHasTrigger
,MyRatio,HowToPay(..),BondPricingMethod(..),InvestorAction(..)
,_BondTxn ,_InspectBal, _IrrResult
)
where
import qualified Data.Text as Text
import qualified Data.Text as T
import qualified Data.Vector as V
import qualified Data.Time as Time
import qualified Data.Time.Format as TF
import qualified Data.Map as Map
import qualified Data.List.Split
import Text.Regex.Base
import Text.Regex.PCRE
import GHC.Generics
import Language.Haskell.TH
import Control.Lens hiding (element,Index,Empty)
import Control.Lens.TH
import Text.Read (readMaybe, get)
import Data.Aeson (ToJSON, toJSON, Value(String))
import Data.Ratio (Ratio, numerator, denominator)
import Data.Text (pack)
import Control.DeepSeq (NFData,rnf)
import Data.Scientific (fromRationalRepetend,formatScientific, Scientific,FPFormat(Fixed))
import Data.Aeson hiding (json)
import Data.Aeson.TH
import Data.Aeson.Types
import Data.Fixed hiding (Ratio)
import Data.Decimal
import Data.Ix
import Data.List (intercalate, findIndex, find)
-- import Cashflow (CashFlowFrame)
-- import Web.Hyperbole hiding (All,Fixed)
import Debug.Trace
-- import qualified Cashflow as CF
debug = flip trace
type BondName = String
type BondNames = [String]
type FeeName = String
type FeeNames = [String]
type AccName = String
type AccountName = String
type AccNames = [String]
type CeName = String
type Comment = String
type Date = Time.Day
type Dates = [Time.Day]
type StartDate = Date
type EndDate = Date
type LastIntPayDate = Date
type Balance = Centi
-- type Balance = Decimal
type Amount = Balance
type Principal = Balance
type Valuation = Balance
type Interest = Balance
type Default = Balance
type Loss = Balance
type Cash = Balance
type Recovery = Balance
type Prepayment = Balance
type Rental = Balance
type PrepaymentPenalty = Balance
type CumPrepay = Balance
type CumPrincipal = Balance
type CumDefault = Balance
type CumDelinq = Balance
type CumLoss = Balance
type CumRecovery = Balance
type AccruedInterest = Balance
type PerFace = Micro
type WAL = Balance
type Duration = Micro
type Convexity = Micro
type Yield = Micro
type IRR = Micro
type Rate = Rational -- general Rate like pool factor
type PrepaymentRate = Rate
type DefaultRate = Rate
type RecoveryRate = Rate
type IRate = Micro -- Interest Rate Type
type Spread = Micro
type Floor = Micro
type Cap = Micro
type RemainTerms = Int
type BorrowerNum = Int
type Lag = Int
data Index = LPR5Y
| LPR1Y
| LIBOR1M
| LIBOR3M
| LIBOR6M
| LIBOR1Y
| USTSY1Y
| USTSY2Y
| USTSY3Y
| USTSY5Y
| USTSY7Y
| USTSY10Y
| USTSY20Y
| USTSY30Y
| USCMT1Y
| PRIME
| COFI
| SOFR1M
| SOFR3M
| SOFR6M
| SOFR1Y
| EURIBOR1M
| EURIBOR3M
| EURIBOR6M
| EURIBOR12M
| BBSW
| IRPH -- The IRPH (Índice de Referencia de Préstamos Hipotecarios) is a reference index used in Spain to fix the interest rate of mortgage loans
| SONIA
-- deriving (Show,Eq,Generic,Ord,Read, Bounded, Enum, Finite, Named, ProtoEnum)
deriving (Show,Eq,Generic,Ord,Read)
type Floater = (Index,Spread)
epocDate = Time.fromGregorian 1970 1 1
-- http://www.deltaquants.com/day-count-conventions
data DayCount = DC_30E_360 -- ^ ISMA European 30S/360 Special German Eurobond Basis
| DC_30Ep_360 -- ^ 30E+/360
| DC_ACT_360 -- ^ Actual/360 , French
| DC_ACT_365
| DC_ACT_365A -- ^ Actual/365 Actual
| DC_ACT_365L -- ^ Actual/365 Leap Year
| DC_NL_365 -- ^ Actual/365 No leap year
| DC_ACT_365F -- ^ Actual /365 Fixed, English
| DC_ACT_ACT -- ^ Actual/Actual ISDA
| DC_30_360_ISDA -- ^ IDSA
| DC_30_360_German -- ^ Gernman
| DC_30_360_US -- ^ 30/360 US Municipal , Bond basis
deriving (Show,Eq,Generic,Ord,Read)
data DateType = ClosingDate -- ^ deal closing day
| CutoffDate -- ^ after which, the pool cashflow was aggregated to SPV
| FirstPayDate -- ^ first payment day for bond/waterfall to run with
| NextPayDate
| NextCollectDate
| FirstCollectDate -- ^ first collection day for pool
| LastCollectDate -- ^ last collection day for pool
| LastPayDate -- ^ last payment day for bond/waterfall
| StatedMaturityDate -- ^ sated maturity date, all cashflow projection/deal action stops by
| DistributionDates -- ^ distribution date for waterfall
| CollectionDates -- ^ collection date for pool
| CustomExeDates String -- ^ custom execution date
deriving (Show,Ord,Eq,Generic,Read)
data DatePattern = MonthEnd
| QuarterEnd
| YearEnd
| MonthFirst
| QuarterFirst
| MidYear
| YearFirst
| MonthDayOfYear Int Int -- T.MonthOfYear T.DayOfMonth
| DayOfMonth Int -- T.DayOfMonth
| SemiAnnual (Int, Int) (Int, Int)
| CustomDate [Date]
| SingletonDate Date
| DaysInYear [(Int, Int)] -- MM/DD
| EveryNMonth Date Int
| Weekday Int
| AllDatePattern [DatePattern]
| StartsExclusive Date DatePattern -- TODO depricated
| StartsAt CutoffType Date DatePattern
| EndsAt CutoffType Date DatePattern
| Exclude DatePattern [DatePattern]
| OffsetBy DatePattern Int
-- | DayOfWeek Int -- T.DayOfWeek
deriving (Show, Eq, Generic, Ord, Read)
data Period = Daily
| Weekly
| BiWeekly
| Monthly
| Quarterly
| SemiAnnually
| Annually
deriving (Show,Eq,Generic,Ord)
type DateVector = (Date, DatePattern)
data RoundingBy a = RoundCeil a
| RoundFloor a
deriving (Show, Generic, Eq, Ord, Read)
type DealName = String
data PoolId = PoolName String -- ^ pool name
| PoolConsol -- ^ consolidate pool ( the only pool )
| DealBondFlow DealName String Date Rate -- ^ bond flow from deal
deriving (Eq,Ord,Generic)
instance Show PoolId where
show (PoolName n) = n
show PoolConsol = "PoolConsol"
show (DealBondFlow dn bn sd r) = "BondFlow:"++dn++":"++bn++":"++show sd++":"++show r
instance (Read PoolId) where
readsPrec d "PoolConsol" = [(PoolConsol,"")]
readsPrec d rStr =
let
pn = Data.List.Split.splitOn ":" rStr
in
case pn of
[dn,bn,sd,r] ->
let
sd' = TF.parseTimeOrError True TF.defaultTimeLocale "%Y-%m-%d" sd
r' = read r::Rate
in
[(DealBondFlow dn bn sd' r',"")]
["PoolName",pn] -> [(PoolName pn,"")]
_ -> error $ "Invalid PoolId: "++ show pn
data Cmp = G -- ^ Greater than
| GE -- ^ Greater Equal than
| L -- ^ Less than
| LE -- ^ Less Equal than
| E -- ^ Equals to
deriving (Generic,Eq,Ord,Read)
instance Show Cmp where
show :: Cmp -> String
show G = ">"
show GE = ">="
show L = "<"
show LE = "<="
show E = "=="
data PoolSource = CollectedInterest -- ^ interest
| CollectedPrincipal -- ^ schdule principal
| CollectedRecoveries -- ^ recoveries
| CollectedPrepayment -- ^ prepayment
| CollectedPrepaymentPenalty -- ^ prepayment pentalty
| CollectedRental -- ^ rental from pool
| CollectedFeePaid -- ^ fee from pool
| CollectedCash -- ^ cash from pool
| NewDefaults -- ^ new defaults in balance
| NewLosses -- ^ new losses in balance
| NewDelinquencies -- ^ new delinquencies in balance
| CurBalance -- ^ performing balance
| CurBegBalance -- ^ performing balance at the beginning of the period
deriving (Show,Ord,Read,Eq, Generic)
data TsPoint a = TsPoint Date a
deriving (Show,Eq,Read,Generic)
instance Ord a => Ord (TsPoint a) where
compare (TsPoint d1 tv1) (TsPoint d2 tv2) = compare d1 d2
data PerPoint a = PerPoint Int a
deriving (Show,Eq,Read,Generic)
data PerCurve a = CurrentVal [PerPoint a]
| WithTrailVal [PerPoint a]
deriving (Show,Eq,Read,Generic,Ord)
getValFromPerCurve :: PerCurve a -> DateDirection -> CutoffType -> Int -> Maybe a
getValFromPerCurve (WithTrailVal []) _ _ _ = Nothing
getValFromPerCurve (CurrentVal []) _ _ _ = Nothing
getValFromPerCurve (CurrentVal (v:vs)) Future p i
= let
cmp = case p of
Inc -> (>=)
Exc -> (>)
in
if cmp (getIdxFromPerPoint v) i then
Just $ getValFromPerPoint v
else
getValFromPerCurve (CurrentVal vs) Future p i
getValFromPerCurve (CurrentVal vs) Past p i
= let
cmp = case p of
Inc -> (<=)
Exc -> (<)
ps = reverse vs
in
case find (\x -> cmp (getIdxFromPerPoint x) i) ps of
Just rs -> Just $ getValFromPerPoint rs
Nothing -> Nothing
getValFromPerCurve (WithTrailVal _ps) dr p i
= let
ps = case dr of
Future -> _ps
Past -> reverse _ps
cmp = case p of
Inc -> (>=)
Exc -> (>)
in
case find (\x -> cmp (getIdxFromPerPoint x) i) ps of
Nothing -> Just $ getValFromPerPoint (last ps)
Just rs -> Just $ getValFromPerPoint rs
getIdxFromPerPoint :: PerPoint a -> Int
getIdxFromPerPoint (PerPoint i _) = i
getValFromPerPoint :: PerPoint a -> a
getValFromPerPoint (PerPoint _ v) = v
instance Ord a => Ord (PerPoint a) where
compare (PerPoint i _) (PerPoint j _) = compare i j
data RangeType = II -- ^ include both start and end date
| IE -- ^ include start date ,but not end date
| EI -- ^ exclude start date but include end date
| EE -- ^ exclude either start date and end date
| NO_IE -- ^ no handling on start date and end date
deriving (Show,Eq,Read,Generic,Ord)
data CutoffType = Inc
| Exc
deriving (Show,Ord,Read,Generic,Eq)
data DateDirection = Future
| Past
deriving (Show,Read,Generic)
data InvestorAction = Buy
| Sell
deriving (Show,Ord,Read,Generic,Eq)
class TimeSeries ts where
cmp :: ts -> ts -> Ordering
cmp t1 t2 = compare (getDate t1) (getDate t2)
sameDate :: ts -> ts -> Bool
sameDate t1 t2 = getDate t1 == getDate t2
getDate :: ts -> Date
getDates :: [ts] -> [Date]
getDates ts = [ getDate t | t <- ts ]
filterByDate :: [ts] -> Date -> [ts]
filterByDate ts d = filter (\x -> getDate x == d ) ts
sliceBy :: RangeType -> StartDate -> EndDate -> [ts] -> [ts]
sliceBy rt sd ed ts
= case rt of
II -> filter (\x -> getDate x >= sd && getDate x <= ed ) ts
IE -> filter (\x -> getDate x >= sd && getDate x < ed ) ts
EI -> filter (\x -> getDate x > sd && getDate x <= ed) ts
EE -> filter (\x -> getDate x > sd && getDate x < ed ) ts
_ -> error "Not support NO_IE for sliceBy in TimeSeries"
cutBy :: CutoffType -> DateDirection -> Date -> [ts] -> [ts]
cutBy ct dd d ts
= case (ct,dd) of
(Inc, Future) -> filter (\x -> getDate x >= d) ts
(Inc, Past) -> filter (\x -> getDate x <= d) ts
(Exc, Future) -> filter (\x -> getDate x > d) ts
(Exc, Past) -> filter (\x -> getDate x < d) ts
cmpWith :: ts -> Date -> Ordering
cmpWith t d = compare (getDate t) d
isAfter :: ts -> Date -> Bool
isAfter t d = getDate t > d
isOnAfter :: ts -> Date -> Bool
isOnAfter t d = getDate t >= d
isBefore :: ts -> Date -> Bool
isBefore t d = getDate t < d
isOnBefore :: ts -> Date -> Bool
isOnBefore t d = getDate t <= d
splitBy :: Date -> CutoffType -> [ts] -> ([ts],[ts])
splitBy d ct tss =
let
ffunR x = case ct of
Inc -> getDate x > d -- include ts in the Left
Exc -> getDate x >= d --
ffunL x = case ct of
Inc -> getDate x <= d -- include ts in the Left
Exc-> getDate x < d --
in
(filter ffunL tss, filter ffunR tss)
getByDate :: Date -> [ts] -> Maybe ts
getByDate d ts = case filterByDate ts d of
[] -> Nothing
(x:_) -> Just x
-- ^ different types of curves, which determine how to interpolate between two points
data Ts = FloatCurve [TsPoint Rational]
| BoolCurve [TsPoint Bool]
| BalanceCurve [TsPoint Balance]
| LeftBalanceCurve [TsPoint Balance]
| RatioCurve [TsPoint Rational]
| ThresholdCurve [TsPoint Rational]
| IRateCurve [TsPoint IRate]
| FactorCurveClosed [TsPoint Rational] Date
| PricingCurve [TsPoint Rational]
| PeriodCurve [TsPoint Int]
| IntCurve [TsPoint Int]
deriving (Show,Eq,Ord,Read,Generic)
data Direction = Up
| Down
deriving (Show,Read,Generic,Eq,Ord)
-- ^ direction of the transaction, in terms of the book keeping
data BookDirection = Credit
| Debit
deriving (Show,Ord, Eq,Read, Generic)
type DueInt = Balance
type DuePremium = Balance
type DueIoI = Balance
data DealCycle = EndCollection -- ^ | collection period <HERE> collection action , waterfall action
| EndCollectionWF -- ^ | collection period collection action <HERE>, waterfall action
| BeginDistributionWF -- ^ | collection period collection action , <HERE>waterfall action
| EndDistributionWF -- ^ | collection period collection action , waterfall action<HERE>
| InWF -- ^ | collection period collection action , waterfall <HERE> action
deriving (Show, Ord, Eq, Read, Generic)
-- ^ different status of the deal
data DealStatus = DealAccelerated (Maybe Date) -- ^ Deal is accelerated status with optinal accerlerated date
| DealDefaulted (Maybe Date) -- ^ Deal is defaulted status with optinal default date
| Amortizing -- ^ Deal is amortizing
| Revolving -- ^ Deal is revolving
| PreClosing DealStatus -- ^ Deal is not closed, but has a closing date
| Warehousing (Maybe DealStatus) -- ^ Deal is not closed, but closing date is not determined yet
| Called -- ^ Deal is called
| Ended Date -- ^ Deal is marked as closed
deriving (Show,Ord,Eq,Read, Generic)
-- ^ pricing methods for assets
data PricingMethod = BalanceFactor Rate Rate -- ^ [balance] to be multiply with rate1 and rate2 if status of asset is "performing" or "defaulted"
| BalanceFactor2 Rate Rate Rate -- ^ [balance] by performing/delinq/default factor
| DefaultedBalance Rate -- ^ [balance] only liquidate defaulted balance
| PV IRate Rate -- ^ discount factor, recovery pct on default
| PVCurve Ts -- ^ [CF] Pricing cashflow with a Curve
| PvRate IRate -- ^ [CF] Pricing cashflow with a constant rate
| PvWal Ts
| PvByRef DealStats -- ^ [CF] Pricing cashflow with a ref rate
| Custom Rate -- ^ custom amount
deriving (Show, Eq ,Generic, Read, Ord)
-- ^ pricing methods for bonds
data BondPricingMethod = BondBalanceFactor Rate
| PvBondByRate Rate
| PvBondByCurve Ts
deriving (Show, Eq ,Generic, Read, Ord)
-- ^ condition which can be evaluated to a boolean value
data Pre = IfZero DealStats
| If Cmp DealStats Balance
| IfRate Cmp DealStats Micro
| IfCurve Cmp DealStats Ts
| IfByPeriodCurve Cmp DealStats DealStats (PerCurve Balance)
| IfRateCurve Cmp DealStats Ts
| IfRateByPeriodCurve Cmp DealStats DealStats (PerCurve Rate)
| IfIntCurve Cmp DealStats Ts
-- Integer
| IfInt Cmp DealStats Int
| IfIntBetween DealStats RangeType Int Int
| IfIntIn DealStats [Int]
-- Dates
| IfDate Cmp Date
| IfDateBetween RangeType Date Date
| IfDateIn Dates
-- Bool
| IfBool DealStats Bool
-- compare deal status
| If2 Cmp DealStats DealStats
| IfRate2 Cmp DealStats DealStats
| IfInt2 Cmp DealStats DealStats
-- | IfRateCurve DealStats Cmp Ts
| IfDealStatus DealStatus
| Always Bool
| IfNot Pre
| Any [Pre]
| All [Pre] -- ^
deriving (Show,Generic,Eq,Ord,Read)
data Table a b = ThresholdTable [(a,b)]
deriving (Show,Eq,Ord,Read,Generic)
data ActionType = ActionResetRate -- ^ reset interest rate from curve
| ActionAccrue -- ^ accrue liablity
deriving (Show,Eq,Ord,Read,Generic)
-- ^ comment of the transaction in the accounts
data TxnComment = PayInt [BondName]
| PayYield BondName
| PayPrin [BondName]
| PayGroupPrin [BondName]
| PayGroupInt [BondName]
| WriteOff BondName Balance
| FundWith BondName Balance
| PayPrinResidual [BondName]
| PayFee FeeName
| SeqPayFee [FeeName]
| PayFeeYield FeeName
| Transfer AccName AccName
| TransferBy AccName AccName Limit
| BookLedgerBy BookDirection String
| PoolInflow (Maybe [PoolId]) PoolSource
| LiquidationProceeds [PoolId]
| LiquidationSupport String
| LiquidationDraw
| LiquidationRepay String
| LiquidationSupportInt Balance Balance
| BankInt
| SupportDraw
| Empty
| Tag String
| UsingDS DealStats
| SwapAccrue
| SwapInSettle String
| SwapOutSettle String
| PurchaseAsset String Balance
| IssuanceProceeds String
| TxnDirection BookDirection
| TxnComments [TxnComment]
deriving (Eq, Show, Ord ,Read, Generic)
-- ^ transaction record in each entity
data Txn = BondTxn Date Balance Interest Principal IRate Cash DueInt DueIoI (Maybe Float) TxnComment -- ^ bond transaction record for interest and principal
| AccTxn Date Balance Amount TxnComment -- ^ account transaction record
| ExpTxn Date Balance Amount Balance TxnComment -- ^ expense transaction record
| SupportTxn Date (Maybe Balance) Balance DueInt DuePremium Cash TxnComment -- ^ liquidity provider transaction record
| IrsTxn Date Balance Amount IRate IRate Balance TxnComment -- ^ interest swap transaction record
| EntryTxn Date Balance Amount TxnComment -- ^ ledger book entry
| TrgTxn Date Bool TxnComment
deriving (Show, Generic, Eq, Read)
data DealStatFields = PoolCollectedPeriod
| BondPaidPeriod
deriving (Generic, Eq, Ord, Show, Read)
-- ^ different types of deal stats
data DealStats = CurrentBondBalance
| CurrentPoolBalance (Maybe [PoolId])
| CurrentPoolBegBalance (Maybe [PoolId])
| CurrentPoolDefaultedBalance
| CumulativePoolDefaultedBalance (Maybe [PoolId]) -- ^ Depreciated, use PoolCumCollection
| CumulativePoolRecoveriesBalance (Maybe [PoolId]) -- ^ Depreciated, use PoolCumCollection
| CumulativeNetLoss (Maybe [PoolId])
| OriginalBondBalance
| OriginalBondBalanceOf [BondName]
| BondTotalFunding [BondName]
| OriginalPoolBalance (Maybe [PoolId])
| DealIssuanceBalance (Maybe [PoolId])
| UseCustomData String
| PoolCumCollection [PoolSource] (Maybe [PoolId])
| PoolCumCollectionTill Int [PoolSource] (Maybe [PoolId])
| PoolCurCollection [PoolSource] (Maybe [PoolId])
| PoolCollectionStats Int [PoolSource] (Maybe [PoolId])
| PoolWaSpread (Maybe [PoolId])
| AllAccBalance
| AccBalance [AccName]
| LedgerBalance [String]
| LedgerBalanceBy BookDirection [String]
| LedgerTxnAmt [String] (Maybe TxnComment)
| ReserveBalance [AccName]
| ReserveGap [AccName]
| ReserveExcess [AccName]
| ReserveGapAt Date [AccName]
| ReserveExcessAt Date [AccName]
| FutureCurrentPoolBalance (Maybe [PoolId])
| FutureCurrentSchedulePoolBalance (Maybe [PoolId])
| FutureCurrentSchedulePoolBegBalance (Maybe [PoolId])
| PoolScheduleCfPv PricingMethod (Maybe [PoolId])
| FuturePoolScheduleCfPv Date PricingMethod (Maybe [PoolId])
| FutureWaCurrentPoolBalance Date Date (Maybe [PoolId])
| FutureCurrentPoolBegBalance (Maybe [PoolId])
| FutureCurrentBondBalance Date
| CurrentBondBalanceOf [BondName]
| BondIntPaidAt Date BondName
| BondsIntPaidAt Date [BondName]
| BondPrinPaidAt Date BondName
| BondsPrinPaidAt Date [BondName]
| BondBalanceTarget [BondName]
| BondBalanceGap BondName
| BondBalanceGapAt Date BondName
| BondDuePrin [BondName]
| BondReturn BondName Balance [TsPoint Amount]
| FeePaidAmt [FeeName]
| FeeTxnAmt [FeeName] (Maybe TxnComment)
| BondTxnAmt [BondName] (Maybe TxnComment)
| AccTxnAmt [AccName] (Maybe TxnComment)
| FeeTxnAmtBy Date [FeeName] (Maybe TxnComment)
| BondTxnAmtBy Date [BondName] (Maybe TxnComment)
| AccTxnAmtBy Date [AccName] (Maybe TxnComment)
| FeesPaidAt Date [FeeName]
| CurrentDueBondInt [BondName]
| CurrentDueBondIntAt Int [BondName]
| CurrentDueBondIntOverInt [BondName]
| CurrentDueBondIntOverIntAt Int [BondName]
| CurrentDueBondIntTotal [BondName]
| CurrentDueBondIntTotalAt Int [BondName]
| CurrentDueFee [FeeName]
| LastBondIntPaid [BondName]
| LastBondPrinPaid [BondName]
| LastFeePaid [FeeName]
| LiqCredit [String]
| LiqBalance [String]
| RateCapNet String
| RateSwapNet String
| BondBalanceHistory Date Date
| PoolCollectionHistory PoolSource Date Date (Maybe [PoolId])
| UnderlyingBondBalance (Maybe [BondName])
| WeightedAvgCurrentPoolBalance Date Date (Maybe [PoolId])
| WeightedAvgCurrentBondBalance Date Date [BondName]
| WeightedAvgOriginalPoolBalance Date Date (Maybe [PoolId])
| WeightedAvgOriginalBondBalance Date Date [BondName]
| CustomData String Date
| DealStatBalance DealStatFields
-- analytical query
| AmountRequiredForTargetIRR Double BondName
-- integer type
| CurrentPoolBorrowerNum (Maybe [PoolId])
| FutureCurrentPoolBorrowerNum Date (Maybe [PoolId])
| ProjCollectPeriodNum
| MonthsTillMaturity BondName
| DealStatInt DealStatFields
-- boolean type
| TestRate DealStats Cmp Micro
| TestAny Bool [DealStats]
| TestAll Bool [DealStats]
| TestNot DealStats
| IsDealStatus DealStatus
| IsMostSenior BondName [BondName]
| IsPaidOff [BondName]
| IsFeePaidOff [String]
| IsLiqSupportPaidOff [String]
| IsRateSwapPaidOff [String]
| IsOutstanding [BondName]
| HasPassedMaturity [BondName]
| TriggersStatus DealCycle String
| DealStatBool DealStatFields
-- rate type
| PoolWaRate (Maybe PoolId)
| BondRate BondName
| CumulativeNetLossRatio (Maybe [PoolId])
| FutureCurrentBondFactor Date
| FutureCurrentPoolFactor Date (Maybe [PoolId])
| BondFactor
| BondFactorOf BondName
| CumulativePoolDefaultedRate (Maybe [PoolId])
| CumulativePoolDefaultedRateTill Int (Maybe [PoolId])
| PoolFactor (Maybe [PoolId])
| BondWaRate [BondName]
| DealStatRate DealStatFields
-- Compond type
| Factor DealStats Rational
| Multiply [DealStats]
| Max [DealStats]
| Min [DealStats]
| Sum [DealStats]
| Substract [DealStats]
| Subtract [DealStats]
| Excess [DealStats]
| Avg [DealStats]
| AvgRatio [DealStats]
| Divide DealStats DealStats
| DivideRatio DealStats DealStats
| Constant Rational
| FloorAndCap DealStats DealStats DealStats
| FloorWith DealStats DealStats
| FloorWithZero DealStats
| CapWith DealStats DealStats
| Abs DealStats
| Round DealStats (RoundingBy Rational)
deriving (Show,Eq,Ord,Read,Generic)
preHasTrigger :: Pre -> [(DealCycle,String)]
preHasTrigger (IfBool (TriggersStatus dc tName) _) = [(dc,tName)]
preHasTrigger (Any ps) = concat $ preHasTrigger <$> ps
preHasTrigger (All ps) = concat $ preHasTrigger <$> ps
preHasTrigger _ = []
data Limit = DuePct Rate -- ^ up to % of total amount due
| DueCapAmt Balance -- ^ up to $ amount
| KeepBalAmt DealStats -- ^ pay till a certain amount remains in an account
| DS DealStats -- ^ transfer with limit described by a `DealStats`
-- | ClearLedger BookDirection String -- ^ when transfer, clear the ledger by transfer amount
-- | ClearLedgerBySeq BookDirection [String] -- ^ clear a direction to a sequence of ledgers
-- | BookLedger String -- ^ when transfer, book the ledger by the transfer amount
| RemainBalPct Rate -- ^ pay till remain balance equals to a percentage of `stats`
| TillTarget -- ^ transfer amount which make target account up reach reserve balanace
| TillSource -- ^ transfer amount out till source account down back to reserve balance
| Multiple Limit Float -- ^ factor of a limit
deriving (Show,Ord,Eq,Read, Generic)
data HowToPay = ByProRata
| BySequential
deriving (Show,Ord,Eq,Read, Generic)
type BookItems = [BookItem]
data BookItem = Item String Balance
| ParentItem String BookItems
deriving (Show,Read,Generic,Eq)
data BalanceSheetReport = BalanceSheetReport {
asset :: BookItem
,liability :: BookItem
,equity :: BookItem
,reportDate :: Date} -- ^ snapshot date of the balance sheet
deriving (Show,Read,Generic,Eq)
data CashflowReport = CashflowReport {
inflow :: BookItem
,outflow :: BookItem
,net :: BookItem
,startDate :: Date
,endDate :: Date }
deriving (Show,Read,Generic,Eq)
data Threshold = Below
| EqBelow
| Above
| EqAbove
deriving (Show,Eq,Ord,Read,Generic)
data SplitType = EqToLeft -- if equal, the element belongs to left
| EqToRight -- if equal, the element belongs to right
| EqToLeftKeepOne
| EqToLeftKeepOnes
deriving (Show, Eq, Generic)
-- ^ deal level cumulative statistics
data CutoffFields = IssuanceBalance -- ^ pool issuance balance
| HistoryRecoveries -- ^ cumulative recoveries
| HistoryInterest -- ^ cumulative interest collected
| HistoryPrepayment -- ^ cumulative prepayment collected
| HistoryPrepaymentPentalty -- ^ cumulative prepayment collected
| HistoryPrincipal -- ^ cumulative principal collected
| HistoryRental -- ^ cumulative rental collected
| HistoryDefaults -- ^ cumulative default balance
| HistoryDelinquency -- ^ cumulative delinquency balance
| HistoryLoss -- ^ cumulative loss/write-off balance
| HistoryCash -- ^ cumulative cash
| HistoryFeePaid
| AccruedInterest -- ^ accrued interest at closing
| RuntimeCurrentPoolBalance -- ^ current pool balance
deriving (Show,Ord,Eq,Read,Generic,NFData)
data PriceResult = PriceResult Valuation PerFace WAL Duration Convexity AccruedInterest [Txn]
| AssetPrice Valuation WAL Duration Convexity AccruedInterest
| OASResult PriceResult [Valuation] Spread
| ZSpread Spread
| IrrResult IRR [Txn]
deriving (Show, Eq, Generic)
makePrisms ''PriceResult
getPriceValue :: PriceResult -> Balance
getPriceValue (AssetPrice v _ _ _ _ ) = v
getPriceValue (PriceResult v _ _ _ _ _ _) = v
getPriceValue x = error $ "failed to match with type when geting price value" ++ show x
getValuation :: PriceResult -> PerFace
getValuation (PriceResult _ val _ _ _ _ _) = val
getValuation (OASResult pr _ _) = getValuation pr
getValuation pr = error $ "not support for pricing result"++ show pr
class Liable lb where
-- must implement
isPaidOff :: lb -> Bool
getCurBalance :: lb -> Balance
getCurRate :: lb -> IRate
getOriginBalance :: lb -> Balance
getOriginDate :: lb -> Date
getAccrueBegDate :: lb -> Date
getDueInt :: lb -> Balance
getDueIntAt :: lb -> Int -> Balance
getDueIntOverInt :: lb -> Balance
getDueIntOverIntAt :: lb -> Int -> Balance
getTotalDueInt :: lb -> Balance
getTotalDueIntAt :: lb -> Int -> Balance
getOutstandingAmount :: lb -> Balance
-- optional implement
-- getTotalDue :: [lb] -> Balance
-- getTotalDue lbs = sum $ getDue <$> lbs
class Accruable ac where
accrue :: Date -> ac -> ac
calcAccrual :: Date -> ac -> Balance
-- buildAccrualAction :: ac -> Date -> Date -> [ActionOnDate]
-- class Resettable rs where
-- reset :: Date -> rs -> rs
-- buildResetAction :: rs -> Date -> Date -> [Txn]
lookupTable :: Ord a => Table a b -> Direction -> (a -> Bool) -> Maybe b
lookupTable (ThresholdTable rows) direction lkUpFunc
= case findIndex lkUpFunc rs of
Nothing -> Nothing
Just i -> Just $ vs!!i
where
rs = case direction of
Up -> reverse $ map fst rows
Down -> map fst rows
vs = case direction of
Up -> reverse $ map snd rows
Down -> map snd rows
lookupIntervalTable :: Ord a => Table a b -> Direction -> (a -> Bool) -> Maybe ((a,b),(a,b))
lookupIntervalTable (ThresholdTable rows) direction lkUpFunc
= case findIndex lkUpFunc rs of
Nothing -> Nothing
Just i -> if succ i == length rows then
Nothing
else
Just $ (rows!!i, rows!!(i+1)) -- `debug` ("Find index"++ show i)
where
rs = case direction of
Up -> reverse $ map fst rows
Down -> map fst rows
data RateAssumption = RateCurve Index Ts -- ^ a rate curve ,which value of rates depends on time
| RateFlat Index IRate -- ^ a rate constant
deriving (Show, Generic)
data TimeHorizion = ByMonth
| ByYear
| ByQuarter
instance TimeSeries (TsPoint a) where
getDate (TsPoint d a) = d
$(deriveJSON defaultOptions ''DecimalRaw)
$(deriveJSON defaultOptions ''TsPoint)
$(deriveJSON defaultOptions ''PerPoint)
$(deriveJSON defaultOptions ''Ts)
$(deriveJSON defaultOptions ''Cmp)
$(deriveJSON defaultOptions ''PoolSource)
$(deriveJSON defaultOptions ''RoundingBy)
$(deriveJSON defaultOptions ''PoolId)
instance ToJSONKey PoolId where
toJSONKey :: ToJSONKeyFunction PoolId
toJSONKey = toJSONKeyText (T.pack . show)
instance FromJSONKey PoolId where
fromJSONKey = FromJSONKeyTextParser $ \t -> case readMaybe (T.unpack t) of
Just k -> pure k
Nothing -> fail ("Invalid key: " ++ show t++">>"++ show (T.unpack t))
-- ^ different types of waterfall execution
data ActionWhen = EndOfPoolCollection -- ^ waterfall executed at the end of pool collection
| DistributionDay DealStatus -- ^ waterfall executed depends on deal status
| CleanUp -- ^ waterfall exectued upon a clean up call
| OnClosingDay -- ^ waterfall executed on deal closing day
| DefaultDistribution -- ^ default waterfall executed
| RampUp -- ^ ramp up
| WithinTrigger String -- ^ waterfall executed within a trigger
| CustomWaterfall String -- ^ custom waterfall
deriving (Show,Ord,Eq,Generic,Read)
data ResultComponent = CallAt Date -- ^ the date when deal called
| DealStatusChangeTo Date DealStatus DealStatus String -- ^ record when & why status changed
| BondOutstanding String Balance Balance -- ^ when deal ends,calculate oustanding principal balance
| BondOutstandingInt String Balance Balance -- ^ when deal ends,calculate oustanding interest due
| InspectBal Date DealStats Balance -- ^ A bal value from inspection
| InspectInt Date DealStats Int -- ^ A int value from inspection
| InspectRate Date DealStats Micro -- ^ A rate value from inspection
| InspectBool Date DealStats Bool -- ^ A bool value from inspection
| RunningWaterfall Date ActionWhen -- ^ running waterfall at a date
| FinancialReport StartDate EndDate BalanceSheetReport CashflowReport
| InspectWaterfall Date (Maybe String) [DealStats] [String]
| ErrorMsg String
| WarningMsg String
| EndRun (Maybe Date) String -- ^ end of run with a message
-- | SnapshotCashflow Date String CashFlowFrame
deriving (Show, Generic,Eq)
makePrisms ''ResultComponent
listToStrWithComma :: [String] -> String
listToStrWithComma = intercalate ","
instance ToJSON TxnComment where
toJSON (PayInt bns ) = String $ T.pack $ "<PayInt:"++ listToStrWithComma bns ++ ">"
toJSON (PayYield bn ) = String $ T.pack $ "<PayYield:"++ bn ++">"
toJSON (PayPrin bns ) = String $ T.pack $ "<PayPrin:"++ listToStrWithComma bns ++ ">"
toJSON (WriteOff bn amt ) = String $ T.pack $ "<WriteOff:"++ bn ++","++ show amt ++ ">"
toJSON (FundWith b bal) = String $ T.pack $ "<FundWith:"++b++","++show bal++">"
toJSON (PayPrinResidual bns ) = String $ T.pack $ "<PayPrinResidual:"++ listToStrWithComma bns ++ ">"
toJSON (PayFee fn ) = String $ T.pack $ "<PayFee:" ++ fn ++ ">"
toJSON (SeqPayFee fns) = String $ T.pack $ "<SeqPayFee:"++ listToStrWithComma fns++">"
toJSON (PayFeeYield fn) = String $ T.pack $ "<PayFeeYield:"++ fn++">"
toJSON (Transfer an1 an2) = String $ T.pack $ "<Transfer:"++ an1 ++","++ an2++">"
toJSON (TransferBy an1 an2 limit) = String $ T.pack $ "<TransferBy:"++ an1 ++","++ an2++","++show limit++">"
toJSON (PoolInflow mPids ps) = String $ T.pack $ "<Pool"++ maybe "" (intercalate "|" . (show <$>)) mPids ++":"++ show ps++">"
toJSON (LiquidationProceeds pids) = String $ T.pack $ "<Liquidation:"++ listToStrWithComma (show <$> pids) ++">"
toJSON (UsingDS ds) = String $ T.pack $ "<DS:"++ show ds++">"
toJSON BankInt = String $ T.pack $ "<BankInterest:>"
toJSON Empty = String $ T.pack $ ""
toJSON (LiquidationSupport source) = String $ T.pack $ "<Support:"++source++">"
toJSON (LiquidationSupportInt b1 b2) = String $ T.pack $ "<SupportExp:(Int:"++ show b1 ++ ",Fee:" ++ show b2 ++")>"
toJSON LiquidationDraw = String $ T.pack $ "<Draw:>"
toJSON (LiquidationRepay s) = String $ T.pack $ "<Repay:"++ s ++">"
toJSON SwapAccrue = String $ T.pack $ "<Accure:>"
toJSON (SwapInSettle s)= String $ T.pack $ "<SettleIn:"++ s ++">"
toJSON (SwapOutSettle s) = String $ T.pack $ "<SettleOut:"++ s ++">"
toJSON (PurchaseAsset rPoolName bal) = String $ T.pack $ "<PurchaseAsset:"<> rPoolName <>","++show bal++">"
toJSON (TxnDirection dr) = String $ T.pack $ "<TxnDirection:"++show dr++">"
toJSON SupportDraw = String $ T.pack $ "<SupportDraw:>"
toJSON (IssuanceProceeds nb) = String $ T.pack $ "<IssuanceProceeds:"++nb++">"
toJSON (Tag cmt) = String $ T.pack $ "<Tag:"++cmt++">"
toJSON (TxnComments tcms) = Array $ V.fromList $ map toJSON tcms
toJSON (PayGroupInt bns) = String $ T.pack $ "<PayGroupInt:"++ listToStrWithComma bns ++ ">"
toJSON (PayGroupPrin bns) = String $ T.pack $ "<PayGroupPrin:"++ listToStrWithComma bns ++ ">"
toJSON (BookLedgerBy dr lName) = String $ T.pack $ "<BookLedger:"++ lName ++ ">"
toJSON x = error $ "Not support for toJSON for "++show x
instance FromJSON TxnComment where
parseJSON = withText "Empty" parseTxn
parseTxn :: T.Text -> Parser TxnComment
parseTxn "" = return Empty
parseTxn "<BankInt>" = return BankInt
parseTxn t = case tagName of
"Transfer" -> let
sv = T.splitOn (T.pack ",") $ T.pack contents
in
return $ Transfer (T.unpack (head sv)) (T.unpack (sv!!1))
"Support" -> return $ LiquidationSupport contents
"PayInt" -> return $ PayInt [contents]
"PayYield" -> return $ PayYield contents
"PayPrin" -> return $ PayPrin [contents]
"WriteOff" -> let
sv = T.splitOn (T.pack ",") $ T.pack contents
in
return $ WriteOff (T.unpack (head sv)) (read (T.unpack (sv!!1))::Balance)
"PayPrinResidual" -> return $ PayPrinResidual [contents]
"PayFee" -> return $ PayFee contents
"SeqPayFee" -> return $ SeqPayFee [contents]
"PayFeeYield" -> return $ PayFeeYield contents
"TransferBy" -> let
sv = T.splitOn (T.pack ",") $ T.pack contents
in
return $ TransferBy (T.unpack (head sv)) (T.unpack (sv!!1)) (read (T.unpack (sv!!2))::Limit)
"Pool" -> let
sr = T.splitOn (T.pack ":") $ T.pack contents
mPids = if head sr == "Nothing" then
Nothing
else
Just (read <$> T.unpack <$> sr)::(Maybe [PoolId])
in
return $ PoolInflow mPids (read (T.unpack (sr!!1))::PoolSource)
"Liquidation" -> let
sv = T.splitOn (T.pack ",") $ T.pack contents
pids::[PoolId] = read <$> T.unpack <$> sv
in
return $ LiquidationProceeds pids
"DS" -> return $ UsingDS (read (contents)::DealStats)
"LiquidationSupportExp" -> let
sv = T.splitOn (T.pack ",") $ T.pack contents
in
return $ LiquidationSupportInt (read (T.unpack (head sv))::Balance) (read (T.unpack (sv!!1))::Balance)
"SupportDraw" -> return SupportDraw
"Draw" -> return LiquidationDraw
"Repay" -> return $ LiquidationRepay contents
"Accure" -> return SwapAccrue
"SettleIn" -> return $ SwapInSettle contents
"SettleOut" -> return $ SwapOutSettle contents
"PurchaseAsset" -> let
sv = T.splitOn (T.pack ",") $ T.pack contents
in
return $ PurchaseAsset (read (T.unpack (sv!!0))::String) (read (T.unpack (sv!!1))::Balance)
"TxnDirection" -> return $ TxnDirection (read contents::BookDirection)
"FundWith" -> let
sv = T.splitOn (T.pack ",") $ T.pack contents
in
return $ FundWith (T.unpack (head sv)) (read (T.unpack (sv!!1))::Balance)
-- toJSON (IssuanceProceeds nb) = String $ T.pack $ "<IssuanceProceeds:"++nb++">"
"IssuanceProceeds" -> return $ IssuanceProceeds contents
"Tag" -> return $ Tag contents
where
pat = "<(\\S+):(\\S+)>"::String
sr = (T.unpack t =~ pat)::[[String]]
tagName = head sr!!1::String
contents = head sr!!2::String
data DealStatType = RtnBalance
| RtnRate
| RtnBool
| RtnInt
deriving (Show,Eq,Ord,Read,Generic)
getDealStatType :: DealStats -> DealStatType
getDealStatType (CumulativePoolDefaultedRateTill _ _) = RtnRate
getDealStatType (CumulativePoolDefaultedRate _) = RtnRate
getDealStatType (CumulativeNetLossRatio _) = RtnRate
getDealStatType BondFactor = RtnRate
getDealStatType (BondFactorOf _) = RtnRate
getDealStatType (PoolFactor _) = RtnRate
getDealStatType (FutureCurrentBondFactor _) = RtnRate
getDealStatType (FutureCurrentPoolFactor _ _) = RtnRate
getDealStatType (BondWaRate _) = RtnRate
getDealStatType (PoolWaRate _) = RtnRate
getDealStatType (BondRate _) = RtnRate
getDealStatType DivideRatio {} = RtnRate
getDealStatType AvgRatio {} = RtnRate
getDealStatType (DealStatRate _) = RtnRate
getDealStatType (Avg dss) = RtnRate
getDealStatType (Divide ds1 ds2) = RtnRate
getDealStatType (Multiply _) = RtnRate
getDealStatType (Factor _ _) = RtnRate
getDealStatType (PoolWaSpread _) = RtnRate
getDealStatType (CurrentPoolBorrowerNum _) = RtnInt
getDealStatType (MonthsTillMaturity _) = RtnInt
getDealStatType ProjCollectPeriodNum = RtnInt
getDealStatType (DealStatInt _) = RtnInt
getDealStatType (IsMostSenior _ _) = RtnBool
getDealStatType IsPaidOff {} = RtnBool
getDealStatType IsOutstanding {} = RtnBool
getDealStatType HasPassedMaturity {} = RtnBool
getDealStatType (TriggersStatus _ _)= RtnBool
getDealStatType (IsDealStatus _)= RtnBool
getDealStatType TestRate {} = RtnBool
getDealStatType (TestAny _ _) = RtnBool
getDealStatType (TestAll _ _) = RtnBool
getDealStatType (DealStatBool _) = RtnBool
getDealStatType (Max dss) = getDealStatType (head dss)
getDealStatType (Min dss) = getDealStatType (head dss)
getDealStatType _ = RtnBalance
dealStatType _ = RtnBalance
data CustomDataType = CustomConstant Rational
| CustomCurve Ts
| CustomDS DealStats
deriving (Show,Ord,Eq,Read,Generic)
opts :: JSONKeyOptions
opts = defaultJSONKeyOptions -- { keyModifier = toLower }
$(deriveJSON defaultOptions ''BondPricingMethod)
$(deriveJSON defaultOptions ''DealStatus)
$(deriveJSON defaultOptions ''CutoffType)
$(deriveJSON defaultOptions ''DealStatFields)
$(concat <$> traverse (deriveJSON defaultOptions) [''BookDirection, ''DealStats, ''PricingMethod, ''DealCycle, ''DateType, ''Period,
''DatePattern, ''Table, ''BalanceSheetReport, ''BookItem, ''CashflowReport, ''Txn] )
instance ToJSONKey DateType where
toJSONKey = genericToJSONKey opts
instance FromJSONKey DateType where
fromJSONKey = FromJSONKeyTextParser $ \t ->
case T.splitOn " " t of
["CustomExeDates", rest] -> pure $ CustomExeDates (T.unpack rest)
_ -> case readMaybe (T.unpack t) of
Just k -> pure k
Nothing -> fail ("Invalid key (DateType): " ++ show t++">>"++ show (T.unpack t))
$(deriveJSON defaultOptions ''RangeType)
$(deriveJSON defaultOptions ''PerCurve)
$(deriveJSON defaultOptions ''Pre)
$(deriveJSON defaultOptions ''CustomDataType)
$(deriveJSON defaultOptions ''ActionWhen)
instance ToJSONKey ActionWhen where
toJSONKey = toJSONKeyText (T.pack . show)
instance FromJSONKey ActionWhen where
fromJSONKey = FromJSONKeyTextParser $ \t ->
case T.splitOn " " t of
["CustomWaterfall", rest] -> pure $ CustomWaterfall (T.unpack rest)
_ -> case readMaybe (T.unpack t) of
Just k -> pure k
Nothing -> fail ("Invalid key (Action When): " ++ show t++">>"++ show (T.unpack t))
$(deriveJSON defaultOptions ''ResultComponent)
$(deriveJSON defaultOptions ''PriceResult)
$(deriveJSON defaultOptions ''CutoffFields)
$(deriveJSON defaultOptions ''HowToPay)
instance ToJSONKey DealCycle where
toJSONKey = toJSONKeyText (T.pack . show)
instance FromJSONKey DealCycle where
fromJSONKey = FromJSONKeyTextParser $ \t -> case readMaybe (T.unpack t) of
Just k -> pure k
Nothing -> fail ("Invalid key: " ++ show t)
instance ToJSONKey CutoffFields where
toJSONKey = toJSONKeyText (Text.pack . show)
instance FromJSONKey CutoffFields where
fromJSONKey = FromJSONKeyTextParser $ \t -> case readMaybe (Text.unpack t) of
Just k -> pure k
Nothing -> fail ("Invalid key: " ++ show t)
newtype MyRatio = MyRatio (Ratio Integer)
instance ToJSON MyRatio where
toJSON (MyRatio r) = case fromRationalRepetend Nothing r of
Left (sci, _) -> toJSON $ formatScientific Fixed (Just 8) sci
Right (sci, rep) -> toJSON $ formatScientific Fixed (Just 8) sci
instance Show MyRatio where
show (MyRatio r) = case fromRationalRepetend Nothing r of
Left (sci, _) -> show $ formatScientific Fixed (Just 8) sci
Right (sci, rep) -> show $ formatScientific Fixed (Just 8) sci
$(deriveJSON defaultOptions ''Index)
$(deriveJSON defaultOptions ''DayCount)
$(deriveJSON defaultOptions ''Threshold)
instance ToJSONKey Threshold where
toJSONKey = genericToJSONKey opts
instance FromJSONKey Threshold where
fromJSONKey = genericFromJSONKey opts
$(deriveJSON defaultOptions ''RateAssumption)
$(deriveJSON defaultOptions ''Direction)
makePrisms ''Txn
$(concat <$> traverse (deriveJSON defaultOptions) [''Limit] )