Coverage Report

Created: 2026-06-23 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/instruments/vanillaswingoption.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2010, 2011 Klaus Spanderen
5
6
 This file is part of QuantLib, a free-software/open-source library
7
 for financial quantitative analysts and developers - http://quantlib.org/
8
9
 QuantLib is free software: you can redistribute it and/or modify it
10
 under the terms of the QuantLib license.  You should have received a
11
 copy of the license along with this program; if not, please email
12
 <quantlib-dev@lists.sf.net>. The license is also available online at
13
 <https://www.quantlib.org/license.shtml>.
14
15
 This program is distributed in the hope that it will be useful, but WITHOUT
16
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
 FOR A PARTICULAR PURPOSE.  See the license for more details.
18
*/
19
20
/*! \file vanillaswingoption.cpp
21
    \brief vanilla swing option class
22
*/
23
24
25
#include <ql/event.hpp>
26
#include <ql/instruments/vanillaswingoption.hpp>
27
28
namespace QuantLib {
29
30
    namespace {
31
        const Size secPerDay = 24U * 3600U;
32
33
        std::pair<std::vector<Date>, std::vector<Size> >
34
0
            createDateTimes(const Date& from, const Date& to, Size stepSize) {
35
36
0
            std::vector<Size> secs;
37
0
            std::vector<Date> dates;
38
39
0
            Date iterDate = from;
40
0
            Size iterStepSize = 0U;
41
42
0
            while (iterDate <= to) {
43
0
                dates.push_back(iterDate);
44
0
                secs.push_back(iterStepSize);
45
46
0
                iterStepSize+=stepSize;
47
0
                if (iterStepSize >= secPerDay) {
48
0
                    iterDate+=1L;
49
0
                    iterStepSize%=secPerDay;
50
0
                }
51
0
            }
52
53
0
            return std::pair<std::vector<Date>,std::vector<Size> >(dates, secs);
54
0
        }
55
    }
56
57
    SwingExercise::SwingExercise(const std::vector<Date>& dates, const std::vector<Size>& seconds)
58
0
    : BermudanExercise(dates),
59
0
      seconds_(seconds.empty() ? std::vector<Size>(dates.size(), 0U) : seconds) {
60
0
        QL_REQUIRE(dates_.size() == seconds_.size(),
61
0
                   "dates and seconds must have the same size");
62
0
        for (Size i=0; i < dates_.size(); ++i) {
63
0
            QL_REQUIRE(seconds_[i] < secPerDay,
64
0
                       "a date can not have more than 24*3600 seconds");
65
0
            if (i > 0) {
66
0
                QL_REQUIRE(dates_[i-1] < dates_[i]
67
0
                           || (dates_[i-1] == dates_[i]
68
0
                               && seconds_[i-1] < seconds_[i]),
69
0
                           "date times must be sorted");
70
0
            }
71
0
        }
72
0
    }
73
74
75
    SwingExercise::SwingExercise(const Date& from,
76
                                 const Date& to, Size stepSizeSecs)
77
0
    : BermudanExercise(createDateTimes(from, to, stepSizeSecs).first),
78
0
      seconds_(createDateTimes(from, to, stepSizeSecs).second) {
79
0
    }
80
81
0
    const std::vector<Size>& SwingExercise::seconds() const { return seconds_; }
82
83
    std::vector<Time> SwingExercise::exerciseTimes(const DayCounter& dc,
84
0
                                                   const Date& refDate) const {
85
0
        std::vector<Time> exerciseTimes;
86
0
        exerciseTimes.reserve(dates().size());
87
0
        for (Size i=0; i<dates().size(); ++i) {
88
0
            Time t = dc.yearFraction(refDate, dates()[i]);
89
90
0
            const Time dt = dc.yearFraction(refDate, dates()[i] + Period(1U, Days)) - t;
91
92
0
            t += dt*seconds()[i]/(24*3600.);
93
94
0
            QL_REQUIRE(t >= 0, "exercise dates must not contain past date");
95
0
            exerciseTimes.push_back(t);
96
0
        }
97
98
0
        return exerciseTimes;
99
0
    }
100
101
0
    Real VanillaForwardPayoff::operator()(Real price) const {
102
0
        switch (type_) {
103
0
          case Option::Call:
104
0
            return price-strike_;
105
0
          case Option::Put:
106
0
            return strike_-price;
107
0
          default:
108
0
            QL_FAIL("unknown/illegal option type");
109
0
        }
110
0
    }
111
112
0
    void VanillaForwardPayoff::accept(AcyclicVisitor& v) {
113
0
        auto* v1 = dynamic_cast<Visitor<VanillaForwardPayoff>*>(&v);
114
0
        if (v1 != nullptr)
115
0
            v1->visit(*this);
116
0
        else
117
0
            StrikedTypePayoff::accept(v);
118
0
    }
119
120
121
0
    void VanillaSwingOption::arguments::validate() const {
122
0
        QL_REQUIRE(payoff, "no payoff given");
123
0
        QL_REQUIRE(exercise, "no exercise given");
124
125
0
        QL_REQUIRE(minExerciseRights <= maxExerciseRights,
126
0
                   "minExerciseRights <= maxExerciseRights");
127
0
        QL_REQUIRE(exercise->dates().size() >= maxExerciseRights,
128
0
                   "number of exercise rights exceeds "
129
0
                   "number of exercise dates");
130
0
    }
131
132
    void VanillaSwingOption::setupArguments(
133
0
                            PricingEngine::arguments* args) const {
134
0
        auto* arguments = dynamic_cast<VanillaSwingOption::arguments*>(args);
135
0
        QL_REQUIRE(arguments != nullptr, "wrong argument type");
136
137
0
        arguments->payoff
138
0
            = ext::dynamic_pointer_cast<StrikedTypePayoff>(payoff_);
139
0
        arguments->exercise
140
0
            = ext::dynamic_pointer_cast<SwingExercise>(exercise_);
141
0
        arguments->minExerciseRights = minExerciseRights_;
142
0
        arguments->maxExerciseRights = maxExerciseRights_;
143
0
    }
144
145
0
    bool VanillaSwingOption::isExpired() const {
146
0
        return detail::simple_event(exercise_->lastDate()).hasOccurred();
147
0
    }
148
}