DFA Model

The model maintains and updates a portfolio of bonds as it moves through time.

objects2001.JPG (141588 bytes)

 When the simulation starts, the portfolio has 10 bond invocations maturing on 2001 to 2010. After a 20 years simulation, where maturing bonds are destroyed and new bonds are purchased if the cash position allows, the portfolio is left with 3 bonds purchased in 2018, 2019 and 2020.

SWR_MatchCash1@ Begin

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Generate rates and factors - DoRandomize@ to randomize

dummy = RANDOM(Seed,DoRandomize@)

! Get rates. DoRates@ is used to trigger

Rates% = SCRIPT('GetRates', ListLink({Alpha, Beta, Sigma, Rt, Tmax, DoRates@}))

! Get A(t,T)s and B(t,Ts)

AtTs% = SCRIPT('GetAtT', Listlink({Alpha, Beta, Sigma, Lambda, Tmax, DoAtTs@}))
BtTs% = SCRIPT('GetBtT', ListLink({Alpha, Sigma, Lambda, Tmax, DoBtTs@}))

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Bond continous valuation
! Bond purchase price

BondPrice = 1000

! Cash position - use list

CashList% = {Cash}

! Year of evaluation start date

SYear := DateTimeNum(StartDate,'YYYY')

! Record portfolio value

PortValue%=MAKELIST('',{1..Tmax},'NUMERIC',{PortValuse%,0})

! Write out PortfolifoTotal

Written@ eqv JOB(LOGSTATE(PortfolioTotal),'nofile write PortfolioTotal')

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Main loop - setting i triggers a yearly cycle

ForEveryYear@ EQV FORNEST({{i,1,20}},Test@,Move@,Startup@,Initialise@)

! Yearly sequence - i triggers the first, which triggers the second, etc. FORNEST waits for Test@ before it continues

UpdatedCash@ EQV SEQUENCE(UpdateCash@,LOGSTATE(i),True,False)
BondsValuated@ EQV SEQUENCE(ValuateBonds@,UpdatedCash@,True,False)
HowMany@ EQV SEQUENCE(UseSpareCash@,BondsValuated@,True,False)
BondsBought@ EQV SEQUENCE(BuyBonds@,HowMany@,True,False)
Test@ EQV SEQUENCE(UpdateMVList@,BondsBought@,True,False)

!!!!!!!!!! UpdateCash@ - put FinishCash (cash position at the end of last yearly cycle) in cash

StartingCash = SCRIPT('setvargap',LISTLINK({{Cash},{FinishCash},UpdateCash@}))

!!!!!!!!!!!!! ValuateBonds@ - valuate the portfolio - valuate bonds, add coupon payments to cash position, mature and destroy matured bonds
!ValuateBonds@ EQV j = i

ClaimThisYear = Claims%[i]
PaidClaims@ EQV GETPUT(CashAfterClaims,CashBeforeClaims,ValuateBonds@,CashList%)
CashAfterClaims = CashBeforeClaims -ClaimThisYear
PaidClaims@ EQV j = i
ValuationDate = StartDate + j * 366

! Updating the valuation dates of all the bonds triggers invocation of the subnet, where they are revaluated, etc.

EQUAL({ValuationDate}+VDs%)

!!!!!!!!!!!!!!! UseSpareCash@ - if enough cash, buy new bonds

UseSpareCash@ EQV SpareCash := Cash

! Simple buying decision logic

if SPareCash < BondPrice then CalcNumber = 0 else CalcNumber = TRUNC(SpareCash/BondPrice)
BondCost = CalcNumber * BondPrice
NewCash := Cash - BondCost
PortfolioTotal = SUM(MVList%) + NewCash
SavedCash = SCRIPT('setvargap',LISTLINK({{FinishCash},{NewCash},PortfolioTotal}))
KeepRecord = SCRIPT('setvargapInd',LISTLINK({PortValue%,{i},PortfolioTotal}))

!!!!!!!!!!!!!!!!!! BuyBonds@ - create a new bond

Created@ EQV CREATENET( {BondA@},InvocName$,ParmsList%,CreateTrigger@)
IF ( BuyBonds@ AND CalcNumber > 0 ) THEN CreateTrigger@
InvocName$ = DATETIMESTR(ValuationDate,'yyyy')
CreatedMaturity = ValuationDate + 1000
ParmsList% = {{'ValuationDate',{ValuationDate}},{'MaturityDate',CreatedMaturity},{'MarketValue',CreatedMarket},{'notionalAmount',BondPrice},{'PurchasePrice',BondPrice},{'Coupon',0.05},{'NumberUnits',CalcNumber}}

!!!!!!!!!!!!!!!!! UpdateMVList@ - add the new bond's mv to the MVList

MVList% = SCRIPT('InvocVars',LISTLINK({{BondA@},'MarketValue',UpdateMVList@}))

!!!!!!!!!!!!!!!! Claims structure

Claims% = {3000,3000,3000,2000,2000,2000,2000,1000,1000,1000,1000,1000,1000,10000,1000,1000,1000,1000,1000,1000,1000,1000,1000,1000}

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Cashflow matching

! Describe risks, payment patterns and treaty bases

ClaimTypes% = {'Auto', 'Fire', 'EarthQuake'}
ClaimPatterns% = {{0.8,0.2}, {0.3,0.3,0.15,0.1,0.05,0.03,0.02,0.02,0.02,0.01}, {0.5,0.3,0.2}}
ClaimTreaties% = { {10000,5000}, {10000,2000}, {5000,5000}}
ClaimsInfo% = {ClaimTypes%, ClaimPatterns%, ClaimTreaties%}

! Average claim sizes - assign values to the variables and trigger DoProfile@ - produces ClaimsProfile%

AvgClaimSizes% = {ClaimType1Size, ClaimType2Size, ClaimType3Size}
ClaimsProfile% = SCRIPT('genClaimsProfile',listlink({ClaimsInfo%, AvgClaimSizes%, DoClaimsProfile@}))
MDList% = SCRIPT('InvocVars',LISTLINK({{BondA@},'MaturityDate',UpdateMDList@}))
NAList% = SCRIPT('InvocVars',LISTLINK({{BondA@},'notionalAmount',UpdateNAList@}))
NUList% = SCRIPT('InvocVars',LISTLINK({{BondA@},'NumberUnits',UpdateNUList@}))

! generate expected bond maturation profile for the same period covered by the generated claims profile

MaturityProfile% = SCRIPT('genMatProfile',ListLink({VDate,Size(ClaimsProfile%), MDList%, NAList%, NUList%,DoMatProfile@}))

BondA@ subnet

! Revaluate the invoked bond

MarketValue = NumberUnits * SCRIPT( 'getBondValue' , ListLink({ VYear , MYear, @.SYear, notionalAmount, Coupon, @.Rates%, @.AtTs%, @.BtTs% } ) )
VYear := DateTimeNum(ValuationDate,'YYYY')
MYear := DateTimeNum(MaturityDate,'YYYY')
PYear := DateTimeNum(PurchaseDate,'YYYY')

! Amortized cost principal

If MYear > VYear then BookValue := PurchasePrice + (VYear - PYear)/(MYear - PYear) * (notionalAmount - PurchasePrice) else BookValue := NotionalAmount

! Update lowest and highet bond value

NewLowestMarket := min( OldLowestMarket, MarketValue/ NumberUnits)
LowestDone@ EQV GETPUT(NewLowestMarket, OldLowestMarket, MarketValue, 'LowestMarket',PurchasePrice)
NewHighestMarket := max( OldHighestMarket, MarketValue/ NumberUnits)
HighestDone@ EQV GETPUT(NewHighestMarket, OldHighestMarket, MarketValue, 'HighestMarket', PurchasePrice)

! Cash coupon

if VYear <= MYear then CashCoupon@
CashedCoupon@ EQV GETPUT(NewCouponCash,OldCouponCash,CashCoupon@,@.CashList%)
NewCouponCash = OldCouponCash + NumberUnits*PurchasePrice*Coupon

! Cash matured bond

if CashedCoupon@ and VYear = MYear then MatureBond@
CashedBond@ EQV GETPUT(NewMaturedCash,OldMaturedCash,MatureBond@,@.CashList%)
NewMaturedCash = OldMaturedCash + NumberUnits*notionalAmount

! Destroy matured bond

if CashedBond@ then DestroyBond@
Finished@ EQV SCRIPT('DestroyBond',DestroyBond@)

End !BondA@

End !SWR_MatchCash1@