PageRenderTime 64ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 0ms

/ql/time/daycounters/actualactual.cpp

https://gitlab.com/arthurpham/QuantLib
C++ | 210 lines | 138 code | 28 blank | 44 comment | 37 complexity | 6261f9437f807c9e615feb9d31e1d93f MD5 | raw file
  1. /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /*
  3. Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
  4. This file is part of QuantLib, a free-software/open-source library
  5. for financial quantitative analysts and developers - http://quantlib.org/
  6. QuantLib is free software: you can redistribute it and/or modify it
  7. under the terms of the QuantLib license. You should have received a
  8. copy of the license along with this program; if not, please email
  9. <quantlib-dev@lists.sf.net>. The license is also available online at
  10. <http://quantlib.org/license.shtml>.
  11. This program is distributed in the hope that it will be useful, but WITHOUT
  12. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  13. FOR A PARTICULAR PURPOSE. See the license for more details.
  14. */
  15. #include <ql/time/daycounters/actualactual.hpp>
  16. namespace QuantLib {
  17. boost::shared_ptr<DayCounter::Impl>
  18. ActualActual::implementation(ActualActual::Convention c,
  19. const Schedule& schedule) {
  20. switch (c) {
  21. case ISMA:
  22. case Bond:
  23. return boost::shared_ptr<DayCounter::Impl>(new ISMA_Impl(schedule));
  24. case ISDA:
  25. case Historical:
  26. case Actual365:
  27. return boost::shared_ptr<DayCounter::Impl>(new ISDA_Impl);
  28. case AFB:
  29. case Euro:
  30. return boost::shared_ptr<DayCounter::Impl>(new AFB_Impl);
  31. default:
  32. QL_FAIL("unknown act/act convention");
  33. }
  34. }
  35. Time ActualActual::ISMA_Impl::yearFraction(const Date& d1,
  36. const Date& d2,
  37. const Date& d3,
  38. const Date& d4) const {
  39. if (d1 == d2)
  40. return 0.0;
  41. if (d1 > d2)
  42. return -yearFraction(d2,d1,d3,d4);
  43. // when the reference period is not specified, try taking
  44. // it equal to (d1,d2)
  45. Date refPeriodStart = (d3 != Date() ? d3 : d1);
  46. Date refPeriodEnd = (d4 != Date() ? d4 : d2);
  47. QL_REQUIRE(refPeriodEnd > refPeriodStart && refPeriodEnd > d1,
  48. "invalid reference period: "
  49. << "date 1: " << d1
  50. << ", date 2: " << d2
  51. << ", reference period start: " << refPeriodStart
  52. << ", reference period end: " << refPeriodEnd);
  53. // estimate roughly the length in months of a period
  54. Integer months =
  55. Integer(0.5+12*Real(refPeriodEnd-refPeriodStart)/365);
  56. // for short periods...
  57. if (months == 0) {
  58. // ...take the reference period as 1 year from d1
  59. refPeriodStart = d1;
  60. refPeriodEnd = d1 + 1*Years;
  61. months = 12;
  62. }
  63. Time period = Real(months)/12.0;
  64. if (d2 <= refPeriodEnd) {
  65. // here refPeriodEnd is a future (notional?) payment date
  66. if (d1 >= refPeriodStart) {
  67. // here refPeriodStart is the last (maybe notional)
  68. // payment date.
  69. // refPeriodStart <= d1 <= d2 <= refPeriodEnd
  70. // [maybe the equality should be enforced, since
  71. // refPeriodStart < d1 <= d2 < refPeriodEnd
  72. // could give wrong results] ???
  73. return period*Real(daysBetween(d1,d2)) /
  74. daysBetween(refPeriodStart,refPeriodEnd);
  75. } else {
  76. // here refPeriodStart is the next (maybe notional)
  77. // payment date and refPeriodEnd is the second next
  78. // (maybe notional) payment date.
  79. // d1 < refPeriodStart < refPeriodEnd
  80. // AND d2 <= refPeriodEnd
  81. // this case is long first coupon
  82. // the last notional payment date
  83. Date previousRef;
  84. if (schedule_.empty()) {
  85. previousRef = refPeriodStart - months*Months;
  86. } else {
  87. previousRef = schedule_.calendar().advance(refPeriodStart,
  88. -schedule_.tenor(),
  89. schedule_.businessDayConvention(),
  90. schedule_.endOfMonth());
  91. }
  92. if (d2 > refPeriodStart)
  93. return yearFraction(d1, refPeriodStart, previousRef,
  94. refPeriodStart) +
  95. yearFraction(refPeriodStart, d2, refPeriodStart,
  96. refPeriodEnd);
  97. else
  98. return yearFraction(d1,d2,previousRef,refPeriodStart);
  99. }
  100. } else {
  101. // here refPeriodEnd is the last (notional?) payment date
  102. // d1 < refPeriodEnd < d2 AND refPeriodStart < refPeriodEnd
  103. QL_REQUIRE(refPeriodStart<=d1,
  104. "invalid dates: "
  105. "d1 < refPeriodStart < refPeriodEnd < d2");
  106. // now it is: refPeriodStart <= d1 < refPeriodEnd < d2
  107. // the part from d1 to refPeriodEnd
  108. Time sum = yearFraction(d1, refPeriodEnd,
  109. refPeriodStart, refPeriodEnd);
  110. // the part from refPeriodEnd to d2
  111. // count how many regular periods are in [refPeriodEnd, d2],
  112. // then add the remaining time
  113. Integer i=0;
  114. Date newRefStart, newRefEnd;
  115. for (;;) {
  116. newRefStart = refPeriodEnd + (months*i)*Months;
  117. newRefEnd = refPeriodEnd + (months*(i+1))*Months;
  118. if (d2 < newRefEnd) {
  119. break;
  120. } else {
  121. sum += period;
  122. i++;
  123. }
  124. }
  125. sum += yearFraction(newRefStart,d2,newRefStart,newRefEnd);
  126. return sum;
  127. }
  128. }
  129. Time ActualActual::ISDA_Impl::yearFraction(const Date& d1,
  130. const Date& d2,
  131. const Date&,
  132. const Date&) const {
  133. if (d1 == d2)
  134. return 0.0;
  135. if (d1 > d2)
  136. return -yearFraction(d2,d1,Date(),Date());
  137. Integer y1 = d1.year(), y2 = d2.year();
  138. Real dib1 = (Date::isLeap(y1) ? 366.0 : 365.0),
  139. dib2 = (Date::isLeap(y2) ? 366.0 : 365.0);
  140. Time sum = y2 - y1 - 1;
  141. // FLOATING_POINT_EXCEPTION
  142. sum += daysBetween(d1, Date(1,January,y1+1))/dib1;
  143. sum += daysBetween(Date(1,January,y2),d2)/dib2;
  144. return sum;
  145. }
  146. Time ActualActual::AFB_Impl::yearFraction(const Date& d1,
  147. const Date& d2,
  148. const Date&,
  149. const Date&) const {
  150. if (d1 == d2)
  151. return 0.0;
  152. if (d1 > d2)
  153. return -yearFraction(d2,d1,Date(),Date());
  154. Date newD2=d2, temp=d2;
  155. Time sum = 0.0;
  156. while (temp > d1) {
  157. temp = newD2 - 1*Years;
  158. if (temp.dayOfMonth()==28 && temp.month()==2
  159. && Date::isLeap(temp.year())) {
  160. temp += 1;
  161. }
  162. if (temp>=d1) {
  163. sum += 1.0;
  164. newD2 = temp;
  165. }
  166. }
  167. Real den = 365.0;
  168. if (Date::isLeap(newD2.year())) {
  169. temp = Date(29, February, newD2.year());
  170. if (newD2>temp && d1<=temp)
  171. den += 1.0;
  172. } else if (Date::isLeap(d1.year())) {
  173. temp = Date(29, February, d1.year());
  174. if (newD2>temp && d1<=temp)
  175. den += 1.0;
  176. }
  177. return sum+daysBetween(d1, newD2)/den;
  178. }
  179. }