Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
collateralaccount.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
20#include <ql/errors.hpp>
21
22using namespace std;
23using namespace QuantLib;
24
25namespace {
26bool isMarginCallExpired(ore::analytics::CollateralAccount::MarginCall mc) { return !mc.openMarginRequest(); }
27
28bool isMarginPayDateLessThan(ore::analytics::CollateralAccount::MarginCall mc1,
30 return mc1.marginPayDate() < mc2.marginPayDate();
31}
32} // namespace
33
34namespace ore {
35using namespace data;
36namespace analytics {
37
38CollateralAccount::CollateralAccount(const QuantLib::ext::shared_ptr<NettingSetDefinition>& csaDef, const Date& date_t0)
39 : csaDef_(csaDef), balance_t0_(0.0) {
41 accountDates_.push_back(date_t0);
42}
43
44CollateralAccount::CollateralAccount(const QuantLib::ext::shared_ptr<NettingSetDefinition>& csaDef, const Real& balance_t0,
45 const Date& date_t0)
46 : csaDef_(csaDef), balance_t0_(balance_t0) {
48 accountDates_.push_back(date_t0);
49}
50
51void CollateralAccount::updateAccountBalance(const Date& simulationDate, const Real& annualisedZeroRate) {
52 for (unsigned i = 0; i < marginCalls_.size(); i++) {
53 QL_REQUIRE(marginCalls_[i].openMarginRequest(), "CollateralAccount error, expired margin call found"
54 << " (should have been purged after expiry)");
55 if (i != marginCalls_.size() - 1) {
56 QL_REQUIRE(marginCalls_[i].marginPayDate() <= marginCalls_[i + 1].marginPayDate(),
57 "CollateralAccount error; vector of margin calls not sorted correctly");
58 }
59
60 if (marginCalls_[i].marginPayDate() <= simulationDate) {
61 if (marginCalls_[i].marginPayDate() == accountDates_.back()) {
62 accountBalances_.back() += marginCalls_[i].marginAmount();
63 } else {
64 QL_REQUIRE(marginCalls_[i].marginPayDate() > accountDates_.back(),
65 "CollateralAccount error; balance update failed due to invalid dates");
66 int accrualDays = marginCalls_[i].marginPayDate() - accountDates_.back();
67 // apply "effective" accrual rate (i.e. adjust for spread specified in netting set definition)
68 Real accrualRate = (accountBalances_.back() >= 0.0) ? (annualisedZeroRate - csaDef_->csaDetails()->collatSpreadRcv())
69 : (annualisedZeroRate - csaDef_->csaDetails()->collatSpreadPay());
70 // bring collateral account up to margin payment date (note accrual rate is assumed to be compounded
71 // daily)
72 accountBalances_.push_back(accountBalances_.back() * std::pow(1.0 + accrualRate / 365.0, accrualDays) +
73 marginCalls_[i].marginAmount());
74 accountDates_.push_back(marginCalls_[i].marginPayDate());
75 }
76 marginCalls_[i] = MarginCall(0.0, Date(), Date(), false);
77 }
78 }
79 marginCalls_.erase(std::remove_if(marginCalls_.begin(), marginCalls_.end(), isMarginCallExpired),
80 marginCalls_.end());
81 if (simulationDate > accountDates_.back()) { // bring the collateral account up to simulation date
82 int accrualDays = simulationDate - accountDates_.back();
83 // apply "effective" accrual rate (i.e. adjust for spread specified in netting set definition)
84 Real accrualRate = (accountBalances_.back() >= 0.0) ? (annualisedZeroRate - csaDef_->csaDetails()->collatSpreadRcv())
85 : (annualisedZeroRate - csaDef_->csaDetails()->collatSpreadPay());
86 accountDates_.push_back(simulationDate);
87 accountBalances_.push_back(accountBalances_.back() * std::pow(1.0 + accrualRate / 365.0, accrualDays));
88 }
89}
90
92 QL_REQUIRE(newMarginCall.openMarginRequest() == true, "CollateralAccount error, "
93 << "attempting to load expired margin call");
94 if (marginCalls_.size() > 0) {
95 QL_REQUIRE(marginCalls_.back().marginRequestDate() < newMarginCall.marginRequestDate(),
96 "CollateralAccount error, attempting to issue an old margin call");
97 }
98 QL_REQUIRE(newMarginCall.marginRequestDate() >= accountDates_.back(),
99 "CollateralAccount error, old margin call being loaded");
100
101 marginCalls_.push_back(newMarginCall);
102 // sorts the vector of margin calls according to ascending pay-date
103 std::sort(marginCalls_.begin(), marginCalls_.end(), isMarginPayDateLessThan);
104}
105
106void CollateralAccount::updateMarginCall(const Real& marginFlowAmount, const Date& marginPayDate,
107 const Date& marginRequestDate) {
108 MarginCall newMarginCall(marginFlowAmount, marginPayDate, marginRequestDate);
109 QL_REQUIRE(newMarginCall.marginRequestDate() <= newMarginCall.marginPayDate(),
110 "CollateralAccount error, attempting to issue an old margin call");
111 updateMarginCall(newMarginCall);
112}
113
114Real CollateralAccount::accountBalance(const Date& date) const {
115 QL_REQUIRE(accountDates_.front() <= date, "CollateralAccount error, invalid date for balance request");
116 if (date >= accountDates_.back())
117 return accountBalances_.back(); // flat extrapolation at far end
118 for (unsigned i = 0; i < accountDates_.size(); i++) {
119 if (date == accountDates_[i])
120 return accountBalances_[i];
121 else if (date > accountDates_[i] && date < accountDates_[i + 1])
122 return accountBalances_[i];
123 else
124 continue;
125 }
126 QL_FAIL("CollateralAccount error, balance not found for date " << date);
127}
128
129Real CollateralAccount::outstandingMarginAmount(const Date& simulationDate) const {
130 Real outstandingMarginCallAmounts = 0.0;
131 for (unsigned i = 0; i < marginCalls_.size(); i++) {
132 QL_REQUIRE(marginCalls_[i].openMarginRequest(), "CollateralAccount error, expired margin call found"
133 << " (should have been purged after expiry)");
134 QL_REQUIRE(marginCalls_[i].marginPayDate() > simulationDate,
135 "CollateralAccount error, old margin call pay date,"
136 << " (should have been settled before now)");
137 outstandingMarginCallAmounts += marginCalls_[i].marginAmount();
138 }
139 return outstandingMarginCallAmounts;
140}
141
142void CollateralAccount::closeAccount(const Date& closeDate) {
143 QL_REQUIRE(closeDate > accountDates_.back(), "CollateralAccount error, invalid date "
144 << " for closure of Collateral Account");
145 marginCalls_.clear();
146 accountBalances_.push_back(0.0);
147 accountDates_.push_back(closeDate);
148}
149} // namespace analytics
150} // namespace ore
void updateMarginCall(const MarginCall &newMarginCall)
Real outstandingMarginAmount(const Date &simulationDate) const
void updateAccountBalance(const Date &simulationDate, const Real &annualisedZeroRate=0.0)
QuantLib::ext::shared_ptr< NettingSetDefinition > csaDef_
CollateralAccount()
Default constructor.
void closeAccount(const Date &closeDate)
Collateral Account Balance (stored in base currency)
data